9ns: Fix devtab function pointer signatures
[akaros.git] / kern / drivers / dev / ether.c
index b309ee8..f0a7c14 100644 (file)
@@ -1,17 +1,30 @@
-// INFERNO
-#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 <ip.h>
+/* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+ * Portions Copyright © 1997-1999 Vita Nuova Limited
+ * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
+ *                                (www.vitanuova.com)
+ * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+ *
+ * Modified for the Akaros operating system:
+ * Copyright (c) 2013-2014 The Regents of the University of California
+ * Copyright (c) 2013-2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
 
 #include <vfs.h>
 #include <kfs.h>
 #include <cpio.h>
 #include <pmap.h>
 #include <smp.h>
-#include <ip.h>
+#include <net/ip.h>
+
+struct dev etherdevtab;
+
+static char *devname(void)
+{
+       return etherdevtab.name;
+}
 
 enum {
-       Type8021Q=      0x8100, /* value of type field for 802.1[pQ] tags */
+       Type8021Q = 0x8100,                     /* value of type field for 802.1[pQ] tags */
 };
 
 static struct ether *etherxx[MaxEther];        /* real controllers */
-static struct ether*   vlanalloc(struct ether*, int);
-static void    vlanoq(struct ether*, struct block*);
+static struct ether *vlanalloc(struct ether *, int);
+static void vlanoq(struct ether *, struct block *);
 
-struct chan*
-etherattach(char* spec)
+struct chan *etherattach(char *spec)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
        uint32_t ctlrno;
        char *p;
        struct chan *chan;
@@ -47,254 +66,267 @@ etherattach(char* spec)
 
        ctlrno = 0;
        vlanid = 0;
-       if(spec && *spec){
+       if (spec && *spec) {
                ctlrno = strtoul(spec, &p, 0);
                /* somebody interpret this for me. */
-               if((ctlrno == 0) && (p == spec) || (ctlrno >= MaxEther) || (*p) && (*p != '.'))
-                       error(Ebadarg);
-               if(*p == '.'){  /* vlan */
-                       vlanid = strtoul(p+1, &p, 0);
-                       if(vlanid <= 0 || vlanid > 0xFFF || *p)
-                               error(Ebadarg);
+               if (((ctlrno == 0) && (p == spec)) ||
+                       (ctlrno >= MaxEther) || ((*p) && (*p != '.')))
+                       error(EINVAL, ERROR_FIXME);
+               if (*p == '.') {        /* vlan */
+                       vlanid = strtoul(p + 1, &p, 0);
+                       if (vlanid <= 0 || vlanid > 0xFFF || *p)
+                               error(EINVAL, ERROR_FIXME);
                }
        }
-       if((ether = etherxx[ctlrno]) == 0)
-               error(Enodev);
+       if ((ether = etherxx[ctlrno]) == 0)
+               error(ENODEV, ERROR_FIXME);
        rlock(&ether->rwlock);
-       if(waserror()){
+       if (waserror()) {
                runlock(&ether->rwlock);
                nexterror();
        }
-       if(vlanid){
-               if(ether->maxmtu < ETHERMAXTU+4)
-                       error("interface cannot support 802.1 tags");
+       if (vlanid) {
+               if (ether->max_mtu < ETHERMAXTU + ETHERHDRSIZE + 4)
+                       error(EFAIL, "interface cannot support 802.1 tags");
                vlan = vlanalloc(ether, vlanid);
-               chan = devattach('l', spec);
-               chan->dev = ctlrno  + (vlanid<<8);
+               chan = devattach(devname(), spec);
+               chan->dev = ctlrno + (vlanid << 8);
                chan->aux = vlan;
                poperror();
                runlock(&ether->rwlock);
                return chan;
        }
-       chan = devattach('l', spec);
+       chan = devattach(devname(), spec);
        chan->dev = ctlrno;
        chan->aux = ether;
-       if(ether->attach)
+       if (ether->attach)
                ether->attach(ether);
        poperror();
        runlock(&ether->rwlock);
        return chan;
 }
 
-static void
-ethershutdown(void)
+static void ethershutdown(void)
 {
        struct ether *ether;
        int i;
 
-       for(i=0; i<MaxEther; i++){
+       for (i = 0; i < MaxEther; i++) {
                ether = etherxx[i];
-               if(ether != NULL && ether->detach != NULL)
+               if (ether != NULL && ether->detach != NULL)
                        ether->detach(ether);
        }
 }
 
-static struct walkqid*
-etherwalk(struct chan* chan, struct chan *nchan, char **name, int nname)
+static struct walkqid *etherwalk(struct chan *chan, struct chan *nchan,
+                                                                char **name, unsigned int nname)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
        struct walkqid *wq;
        struct ether *ether;
 
        ether = chan->aux;
        rlock(&ether->rwlock);
-       if(waserror()) {
+       if (waserror()) {
                runlock(&ether->rwlock);
                nexterror();
        }
-       wq = netifwalk(&ether->netif, chan, nchan, name, nname);
-       if(wq && wq->clone != NULL && wq->clone != chan)
+       wq = netifwalk(ether, chan, nchan, name, nname);
+       if (wq && wq->clone != NULL && wq->clone != chan)
                wq->clone->aux = ether;
        poperror();
        runlock(&ether->rwlock);
        return wq;
 }
 
-static int
-etherstat(struct chan* chan, uint8_t* dp, int n)
+static size_t etherstat(struct chan *chan, uint8_t *dp, size_t n)
 {
-       ERRSTACK(2);
-       int s;
+       ERRSTACK(1);
+       size_t s;
        struct ether *ether;
 
        ether = chan->aux;
        rlock(&ether->rwlock);
-       if(waserror()) {
+       if (waserror()) {
                runlock(&ether->rwlock);
                nexterror();
        }
-       s = netifstat(&ether->netif, chan, dp, n);
+       s = netifstat(ether, chan, dp, n);
        poperror();
        runlock(&ether->rwlock);
        return s;
 }
 
-static struct chan*
-etheropen(struct chan* chan, int omode)
+static struct chan *etheropen(struct chan *chan, int omode)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
        struct chan *c;
        struct ether *ether;
 
        ether = chan->aux;
        rlock(&ether->rwlock);
-       if(waserror()) {
+       if (waserror()) {
                runlock(&ether->rwlock);
                nexterror();
        }
-       c = netifopen(&ether->netif, chan, omode);
+       c = netifopen(ether, chan, omode);
        poperror();
        runlock(&ether->rwlock);
        return c;
 }
 
-static void
-etherclose(struct chan* chan)
+static void etherclose(struct chan *chan)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
        struct ether *ether;
 
        ether = chan->aux;
        rlock(&ether->rwlock);
-       if(waserror()) {
+       if (waserror()) {
                runlock(&ether->rwlock);
                nexterror();
        }
-       netifclose(&ether->netif, chan);
+       netifclose(ether, chan);
        poperror();
        runlock(&ether->rwlock);
 }
 
-static long
-etherread(struct chan* chan, void* buf, long n, int64_t off)
+static size_t etherread(struct chan *chan, void *buf, size_t n, off64_t off)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
        struct ether *ether;
        uint32_t offset = off;
        long r;
 
        ether = chan->aux;
        rlock(&ether->rwlock);
-       if(waserror()) {
+       if (waserror()) {
                runlock(&ether->rwlock);
                nexterror();
        }
-       if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+       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){
+               if (NETTYPE(chan->qid.path) == Nifstatqid) {
                        r = ether->ifstat(ether, buf, n, offset);
                        goto out;
                }
-               if(NETTYPE(chan->qid.path) == Nstatqid)
+               if (NETTYPE(chan->qid.path) == Nstatqid)
                        ether->ifstat(ether, buf, 0, offset);
        }
-       r = netifread(&ether->netif, chan, buf, n, offset);
+       r = netifread(ether, chan, buf, n, offset);
 out:
        poperror();
        runlock(&ether->rwlock);
        return r;
 }
 
-static struct block*
-etherbread(struct chan* chan, long n, uint32_t offset)
+static struct block *etherbread(struct chan *chan, size_t n, off64_t offset)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
        struct block *b;
        struct ether *ether;
 
        ether = chan->aux;
        rlock(&ether->rwlock);
-       if(waserror()) {
+       if (waserror()) {
                runlock(&ether->rwlock);
                nexterror();
        }
-       b = netifbread(&ether->netif, chan, n, offset);
+       b = netifbread(ether, chan, n, offset);
        poperror();
        runlock(&ether->rwlock);
        return b;
 }
 
-static int
-etherwstat(struct chan* chan, uint8_t* dp, int n)
+static size_t etherwstat(struct chan *chan, uint8_t *dp, size_t n)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
        struct ether *ether;
        int r;
 
        ether = chan->aux;
        rlock(&ether->rwlock);
-       if(waserror()) {
+       if (waserror()) {
                runlock(&ether->rwlock);
                nexterror();
        }
-       r = netifwstat(&ether->netif, chan, dp, n);
+       r = netifwstat(ether, chan, dp, n);
        poperror();
        runlock(&ether->rwlock);
        return r;
 }
 
-static void
-etherrtrace(struct netfile* f, struct etherpkt* pkt, int len)
+static void etherrtrace(struct netfile *f, struct etherpkt *pkt, int len)
 {
-       int i, n;
+       uint64_t i, n;
        struct block *bp;
 
-       if(qwindow(f->in) <= 0)
+       if (qwindow(f->in) <= 0)
                return;
-       if(len > 58)
+       if (len > 58)
                n = 58;
        else
                n = len;
-       bp = iallocb(64);
-       if(bp == NULL)
+       bp = block_alloc(68, MEM_ATOMIC);
+       if (bp == NULL)
                return;
        memmove(bp->wp, pkt->d, n);
+       /* we're storing 8 bytes here (64 bit); old 9ns was 32 bit for msec */
        i = milliseconds();
-       bp->wp[58] = len>>8;
+       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;
+       bp->wp[60] = i >> 56;
+       bp->wp[61] = i >> 48;
+       bp->wp[62] = i >> 40;
+       bp->wp[63] = i >> 32;
+       bp->wp[64] = i >> 24;
+       bp->wp[65] = i >> 16;
+       bp->wp[66] = i >> 8;
+       bp->wp[67] = i;
+       bp->wp += 68;
        qpass(f->in, bp);
 }
 
-struct block*
-etheriq(struct ether* ether, struct block* bp, int fromwire)
+#ifdef CONFIG_RISCV
+#warning "Potentially unaligned ethernet addrs!"
+#endif
+
+static inline int eaddrcmp(uint8_t *x, uint8_t *y)
+{
+       uint16_t *a = (uint16_t *)x;
+       uint16_t *b = (uint16_t *)y;
+
+       return (a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]);
+}
+
+struct block *etheriq(struct ether *ether, struct block *bp, int fromwire)
 {
        struct etherpkt *pkt;
        uint16_t type;
-       int len, multi, tome, fromme, vlanid, i;
+       int multi, tome, fromme, vlanid, i;
        struct netfile **ep, *f, **fp, *fx;
        struct block *xbp;
        struct ether *vlan;
 
-       ether->netif.inpackets++;
-
-       pkt = (struct etherpkt*)bp->rp;
-       len = BLEN(bp);
-       type = (pkt->type[0]<<8)|pkt->type[1];
-       if(type == Type8021Q && ether->nvlan){
-               vlanid = nhgets(bp->rp+2*Eaddrlen+2) & 0xFFF;
-               if(vlanid){
-                       for(i = 0; i < ARRAY_SIZE(ether->vlans); i++){
+       ether->inpackets++;
+
+       pkt = (struct etherpkt *)bp->rp;
+       /* TODO: we might need to assert more for higher layers, or otherwise deal
+        * with extra data. */
+       assert(BHLEN(bp) >= offsetof(struct etherpkt, data));
+       type = (pkt->type[0] << 8) | pkt->type[1];
+       if (type == Type8021Q && ether->nvlan) {
+               vlanid = nhgets(bp->rp + 2 * Eaddrlen + 2) & 0xFFF;
+               if (vlanid) {
+                       for (i = 0; i < ARRAY_SIZE(ether->vlans); i++) {
                                vlan = ether->vlans[i];
-                               if(vlan != NULL && vlan->vlanid == vlanid){
-                                       memmove(bp->rp+4, bp->rp, 2*Eaddrlen);
+                               if (vlan != NULL && vlan->vlanid == vlanid) {
+                                       /* might have a problem with extra data here */
+                                       assert(BHLEN(bp) >= 4 + 2 * Eaddrlen);
+                                       memmove(bp->rp + 4, bp->rp, 2 * Eaddrlen);
                                        bp->rp += 4;
                                        return etheriq(vlan, bp, fromwire);
                                }
@@ -304,13 +336,14 @@ etheriq(struct ether* ether, struct block* bp, int fromwire)
        }
 
        fx = 0;
-       ep = &ether->netif.f[Ntypes];
+       ep = &ether->f[Ntypes];
 
        multi = pkt->d[0] & 1;
        /* check for valid multcast 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){
+       if (multi && eaddrcmp(pkt->d, ether->bcast) != 0
+               && ether->prom == 0) {
+               if (!activemulti(ether, pkt->d, sizeof(pkt->d))) {
+                       if (fromwire) {
                                freeb(bp);
                                bp = 0;
                        }
@@ -319,8 +352,8 @@ etheriq(struct ether* ether, struct block* bp, int fromwire)
        }
 
        /* is it for me? */
-       tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
-       fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+       tome = eaddrcmp(pkt->d, ether->ea) == 0;
+       fromme = eaddrcmp(pkt->s, ether->ea) == 0;
 
        /*
         * Multiplex the packet to all the connections which want it.
@@ -328,35 +361,36 @@ etheriq(struct ether* ether, struct block* bp, int fromwire)
         * 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) && (f->type == type || f->type < 0))
-               if(tome || multi || f->prom){
-                       /* Don't want to hear bridged packets */
-                       if(f->bridge && !fromwire && !fromme)
-                               continue;
-                       if(!f->headersonly){
-                               if(fromwire && fx == 0)
+       for (fp = ether->f; fp < ep; fp++) {
+               if ((f = *fp) && (f->type == type || f->type < 0))
+                       if (tome || multi || f->prom) {
+                               /* Don't want to hear bridged packets */
+                               if (f->bridge && !fromwire && !fromme)
+                                       continue;
+                               if (f->headersonly) {
+                                       etherrtrace(f, pkt, BHLEN(bp));
+                                       continue;
+                               }
+                               if (fromwire && fx == 0) {
                                        fx = f;
-                               else if((xbp = iallocb(len))){
-                                       memmove(xbp->wp, pkt, len);
-                                       xbp->wp += len;
-                                       if(qpass(f->in, xbp) < 0)
-                                               ether->netif.soverflows++;
+                                       continue;
                                }
-                               else
-                                       ether->netif.soverflows++;
+                               xbp = copyblock(bp, MEM_ATOMIC);
+                               if (xbp == 0) {
+                                       ether->soverflows++;
+                                       continue;
+                               }
+                               if (qpass(f->in, xbp) < 0)
+                                       ether->soverflows++;
                        }
-                       else
-                               etherrtrace(f, pkt, len);
-               }
        }
 
-       if(fx){
-               if(qpass(fx->in, bp) < 0)
-                       ether->netif.soverflows++;
+       if (fx) {
+               if (qpass(fx->in, bp) < 0)
+                       ether->soverflows++;
                return 0;
        }
-       if(fromwire){
+       if (fromwire) {
                freeb(bp);
                return 0;
        }
@@ -364,14 +398,17 @@ etheriq(struct ether* ether, struct block* bp, int fromwire)
        return bp;
 }
 
-static int
-etheroq(struct ether* ether, struct block* bp)
+static int etheroq(struct ether *ether, struct block *bp)
 {
-       int len, loopback, s;
+       int len, loopback;
        struct etherpkt *pkt;
+       int8_t irq_state = 0;
 
-       ether->netif.outpackets++;
+       ether->outpackets++;
 
+       if (!(ether->feat & NETF_SG))
+               bp = linearizeblock(bp);
+       ptclcsum_finalize(bp, ether->feat);
        /*
         * 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
@@ -381,36 +418,39 @@ etheroq(struct ether* ether, struct block* bp)
         * To enable bridging to work, only packets that were originated
         * by this interface are fed back.
         */
-       pkt = (struct etherpkt*)bp->rp;
+       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){
-#warning "splhi"
-               //s = splhi();
+       loopback = eaddrcmp(pkt->d, ether->ea) == 0;
+       if (loopback || eaddrcmp(pkt->d, ether->bcast) == 0 || ether->prom) {
+               disable_irqsave(&irq_state);
                etheriq(ether, bp, 0);
-               //splx(s);
+               enable_irqsave(&irq_state);
+               if (loopback) {
+                       freeb(bp);
+                       return len;
+               }
        }
 
-       if(!loopback){
-               if(ether->vlanid){
-                       /* add tag */
-                       bp = padblock(bp, 2+2);
-                       memmove(bp->rp, bp->rp+4, 2*Eaddrlen);
-                       hnputs(bp->rp+2*Eaddrlen, Type8021Q);
-                       hnputs(bp->rp+2*Eaddrlen+2, ether->vlanid & 0xFFF);     /* prio:3 0:1 vid:12 */
-                       ether = ether->ctlr;
-               }
-               qbwrite(ether->oq, bp);
-               if(ether->transmit != NULL)
-                       ether->transmit(ether);
-       }else
-               freeb(bp);
+       if (ether->vlanid) {
+               /* add tag */
+               bp = padblock(bp, 2 + 2);
+               memmove(bp->rp, bp->rp + 4, 2 * Eaddrlen);
+               hnputs(bp->rp + 2 * Eaddrlen, Type8021Q);
+               hnputs(bp->rp + 2 * Eaddrlen + 2, ether->vlanid & 0xFFF);       /* prio:3 0:1 vid:12 */
+               ether = ether->ctlr;
+       }
+
+       if ((ether->feat & NETF_PADMIN) == 0 && BLEN(bp) < ether->min_mtu)
+               bp = adjustblock(bp, ether->min_mtu);
+
+       qbwrite(ether->oq, bp);
+       if (ether->transmit != NULL)
+               ether->transmit(ether);
 
        return len;
 }
 
-static long
-etherwrite(struct chan* chan, void* buf, long n, int64_t unused)
+static size_t etherwrite(struct chan *chan, void *buf, size_t n, off64_t unused)
 {
        ERRSTACK(2);
        struct ether *ether;
@@ -421,44 +461,46 @@ etherwrite(struct chan* chan, void* buf, long n, int64_t unused)
 
        ether = chan->aux;
        rlock(&ether->rwlock);
-       if(waserror()) {
+       if (waserror()) {
                runlock(&ether->rwlock);
                nexterror();
        }
-       if(NETTYPE(chan->qid.path) != Ndataqid) {
-               l = netifwrite(&ether->netif, chan, buf, n);
-               if(l >= 0)
+       if (NETTYPE(chan->qid.path) != Ndataqid) {
+               l = netifwrite(ether, chan, buf, n);
+               if (l >= 0)
                        goto out;
                cb = parsecmd(buf, n);
-               if(strcmp(cb->f[0], "nonblocking") == 0){
-                       if(cb->nf <= 1)
+               if (cb->nf < 1) {
+                       kfree(cb);
+                       error(EFAIL, "short control request");
+               }
+               if (strcmp(cb->f[0], "nonblocking") == 0) {
+                       if (cb->nf <= 1)
                                onoff = 1;
                        else
                                onoff = atoi(cb->f[1]);
-                       if(ether->oq != NULL)
-                               qnoblock(ether->oq, onoff);
+                       if (ether->oq != NULL)
+                               qdropoverflow(ether->oq, onoff);
                        kfree(cb);
                        goto out;
                }
                kfree(cb);
-               if(ether->ctl!=NULL){
-                       l = ether->ctl(ether,buf,n);
+               if (ether->ctl != NULL) {
+                       l = ether->ctl(ether, buf, n);
                        goto out;
                }
-               error(Ebadctl);
+               error(EINVAL, ERROR_FIXME);
        }
 
-       if(n > ether->maxmtu)
-               error(Etoobig);
-       if(n < ether->minmtu)
-               error(Etoosmall);
-       bp = allocb(n);
-       if(waserror()){
+       if (n > ether->mtu + ETHERHDRSIZE)
+               error(E2BIG, ERROR_FIXME);
+       bp = block_alloc(n, MEM_WAIT);
+       if (waserror()) {
                freeb(bp);
                nexterror();
        }
        memmove(bp->rp, buf, n);
-       memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+       memmove(bp->rp + Eaddrlen, ether->ea, Eaddrlen);
        bp->wp += n;
        poperror();
 
@@ -469,16 +511,15 @@ out:
        return l;
 }
 
-static long
-etherbwrite(struct chan* chan, struct block* bp, uint32_t unused)
+static size_t etherbwrite(struct chan *chan, struct block *bp, off64_t unused)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
        struct ether *ether;
        long n;
 
        n = BLEN(bp);
-       if(NETTYPE(chan->qid.path) != Ndataqid){
-               if(waserror()) {
+       if (NETTYPE(chan->qid.path) != Ndataqid) {
+               if (waserror()) {
                        freeb(bp);
                        nexterror();
                }
@@ -489,17 +530,13 @@ etherbwrite(struct chan* chan, struct block* bp, uint32_t unused)
        }
        ether = chan->aux;
        rlock(&ether->rwlock);
-       if(waserror()) {
+       if (waserror()) {
                runlock(&ether->rwlock);
                nexterror();
        }
-       if(n > ether->maxmtu){
+       if (n > ether->mtu + ETHERHDRSIZE && (bp->flag & Btso) == 0) {
                freeb(bp);
-               error(Etoobig);
-       }
-       if(n < ether->minmtu){
-               freeb(bp);
-               error(Etoosmall);
+               error(E2BIG, ERROR_FIXME);
        }
        n = etheroq(ether, bp);
        poperror();
@@ -507,13 +544,11 @@ etherbwrite(struct chan* chan, struct block* bp, uint32_t unused)
        return n;
 }
 
-static void
-nop(struct ether*unused)
+static void nop(struct ether *unused)
 {
 }
 
-static long
-vlanctl(struct ether *ether, void *buf, long n)
+static long vlanctl(struct ether *ether, void *buf, long n)
 {
        uint8_t ea[Eaddrlen];
        struct ether *master;
@@ -521,19 +556,18 @@ vlanctl(struct ether *ether, void *buf, long n)
        int i;
 
        cb = parsecmd(buf, n);
-       if(cb->nf >= 2
-       && strcmp(cb->f[0], "ea")==0
-       && parseether(ea, cb->f[1]) == 0){
+       if (cb->nf >= 2
+               && strcmp(cb->f[0], "ea") == 0 && parseether(ea, cb->f[1]) == 0) {
                kfree(cb);
                memmove(ether->ea, ea, Eaddrlen);
-               memmove(ether->netif.addr, ether->ea, Eaddrlen);
+               memmove(ether->addr, ether->ea, Eaddrlen);
                return 0;
        }
-       if(cb->nf == 1 && strcmp(cb->f[0], "disable") == 0){
+       if (cb->nf == 1 && strcmp(cb->f[0], "disable") == 0) {
                master = ether->ctlr;
                qlock(&master->vlq);
-               for(i = 0; i < ARRAY_SIZE(master->vlans); i++)
-                       if(master->vlans[i] == ether){
+               for (i = 0; i < ARRAY_SIZE(master->vlans); i++)
+                       if (master->vlans[i] == ether) {
                                ether->vlanid = 0;
                                master->nvlan--;
                                break;
@@ -543,61 +577,61 @@ vlanctl(struct ether *ether, void *buf, long n)
                return 0;
        }
        kfree(cb);
-       error(Ebadctl);
+       error(EINVAL, ERROR_FIXME);
        return -1;      /* not reached */
 }
 
-static struct ether*
-vlanalloc(struct ether *ether, int id)
+static struct ether *vlanalloc(struct ether *ether, int id)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
        struct ether *vlan;
        int i, fid;
        char name[KNAMELEN];
 
        qlock(&ether->vlq);
-       if(waserror()){
+       if (waserror()) {
                qunlock(&ether->vlq);
                nexterror();
        }
        fid = -1;
-       for(i = 0; i < ARRAY_SIZE(ether->vlans); i++){
+       for (i = 0; i < ARRAY_SIZE(ether->vlans); i++) {
                vlan = ether->vlans[i];
-               if(vlan != NULL && vlan->vlanid == id){
+               if (vlan != NULL && vlan->vlanid == id) {
                        poperror();
                        qunlock(&ether->vlq);
                        return vlan;
                }
-               if(fid < 0 && (vlan == NULL || vlan->vlanid == 0))
+               if (fid < 0 && (vlan == NULL || vlan->vlanid == 0))
                        fid = i;
        }
-       if(fid < 0)
-               error(Enoifc);
+       if (fid < 0)
+               error(ENOENT, ERROR_FIXME);
        snprintf(name, sizeof(name), "ether%d.%d", ether->ctlrno, id);
        vlan = ether->vlans[fid];
-       if(vlan == NULL){
+       if (vlan == NULL) {
                vlan = kzmalloc(sizeof(struct ether), 1);
-               if(vlan == NULL)
-                       error(Enovmem);
-               netifinit(&vlan->netif, name, Ntypes, ether->netif.limit);
+               if (vlan == NULL)
+                       error(ENOMEM, ERROR_FIXME);
+               rwinit(&vlan->rwlock);
+               qlock_init(&vlan->vlq);
+               netifinit(vlan, name, Ntypes, ether->limit);
                ether->vlans[fid] = vlan;       /* id is still zero, can't be matched */
                ether->nvlan++;
-       }else
-               memmove(vlan->netif.name, name, KNAMELEN-1);
-#warning "fix me setting up vlans"
-#if 0
+       } else
+               memmove(vlan->name, name, KNAMELEN - 1);
        vlan->attach = nop;
        vlan->transmit = NULL;
        vlan->ctl = vlanctl;
        vlan->irq = -1;
-//     vlan->promiscuous = ether->promiscuous;
-//     vlan->multicast = ether->multicast;
+       vlan->promiscuous = ether->promiscuous;
+       vlan->multicast = ether->multicast;
        vlan->arg = vlan;
        vlan->mbps = ether->mbps;
        vlan->fullduplex = ether->fullduplex;
        vlan->encry = ether->encry;
-       vlan->minmtu = ether->minmtu;
-       vlan->maxmtu = ether->maxmtu;
+       vlan->mtu = ether->mtu;
+       vlan->min_mtu = ether->min_mtu;
+       vlan->max_mtu = ether->max_mtu;
        vlan->ctlrno = ether->ctlrno;
        vlan->vlanid = id;
        vlan->alen = Eaddrlen;
@@ -606,117 +640,138 @@ vlanalloc(struct ether *ether, int id)
        vlan->oq = NULL;
        vlan->ctlr = ether;
        vlan->vlanid = id;
-#endif
        poperror();
        qunlock(&ether->vlq);
        return vlan;
 }
 
 static struct {
-       char*   type;
-       int     (*reset)(struct ether*);
-} cards[MaxEther+1];
+       char *type;
+       int (*reset) (struct ether *);
+} cards[MaxEther + 1];
 
-void
-addethercard(char* t, int (*r)(struct ether*))
+void addethercard(char *t, int (*r) (struct ether *))
 {
        static int ncard;
 
-       if(ncard == MaxEther)
+       if (ncard == MaxEther)
                panic("too many ether cards");
        cards[ncard].type = t;
        cards[ncard].reset = r;
        ncard++;
 }
 
-int
-parseether(uint8_t *to, char *from)
+int parseether(uint8_t * to, char *from)
 {
        char nip[4];
        char *p;
        int i;
 
        p = from;
-       for(i = 0; i < Eaddrlen; i++){
-               if(*p == 0)
+       for (i = 0; i < Eaddrlen; i++) {
+               if (*p == 0)
                        return -1;
                nip[0] = *p++;
-               if(*p == 0)
+               if (*p == 0)
                        return -1;
                nip[1] = *p++;
                nip[2] = 0;
                to[i] = strtoul(nip, 0, 16);
-               if(*p == ':')
+               if (*p == ':')
                        p++;
        }
        return 0;
 }
 
-static void
-etherreset(void)
+static void etherreset(void)
 {
-#warning "fix me etherreset"
-#if 0
        struct ether *ether;
-       int i, n, ctlrno;
+       int i, n, ctlrno, qsize;
        char name[KNAMELEN], buf[128];
 
-       for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
-               if(ether == 0)
+       for (ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++) {
+               if (ether == 0)
                        ether = kzmalloc(sizeof(struct ether), 0);
                memset(ether, 0, sizeof(struct ether));
+               rwinit(&ether->rwlock);
+               qlock_init(&ether->vlq);
+               rendez_init(&ether->link_rz);
                ether->ctlrno = ctlrno;
-               ether->netif.mbps = 10;
-               ether->minmtu = ETHERMINTU;
-               ether->maxmtu = ETHERMAXTU;
-               ether->netif.itype = -1;
-
-               if(archether(ctlrno, ether) <= 0)
-                       continue;
-
-               for(n = 0; cards[n].type; n++){
-                       if(cistrcmp(cards[n].type, ether->type))
+               ether->mbps = 10;
+               ether->mtu = ETHERMAXTU;
+               ether->min_mtu = ETHERMINTU;
+               ether->max_mtu = ETHERMAXTU;
+               /* looked like irq type, we don't have these yet */
+               //ether->netif.itype = -1;
+
+               /* TODO: looks like they expected some init to be done here.  at the
+                * very least, ether->type is 0 right now, and needs to be set.  looking
+                * around online, it seems to find out ether config settings, so that we
+                * can set some flags in the opt parseing below. */
+               //if(archether(ctlrno, ether) <= 0)
+               //  continue;
+
+               for (n = 0; cards[n].type; n++) {
+#if 0
+                       if (cistrcmp(cards[n].type, ether->type))
                                continue;
-                       for(i = 0; i < ether->nopt; i++){
-                               if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
-                                       if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+                       for (i = 0; i < ether->nopt; i++) {
+                               if (cistrncmp(ether->opt[i], "ea=", 3) == 0) {
+                                       if (parseether(ether->ea, &ether->opt[i][3]) == -1)
                                                memset(ether->ea, 0, Eaddrlen);
-                               }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
-                                       cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+                               } else if (cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+                                                  cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
                                        ether->fullduplex = 1;
-                               else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+                               else if (cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
                                        ether->mbps = 100;
                        }
-                       if(cards[n].reset(ether))
-                               break;
+#endif
+                       if (cards[n].reset(ether))
+                               continue;
+                       /* might be fucked a bit - reset() doesn't know the type.  might not
+                        * even matter, except for debugging. */
+                       ether->type = cards[n].type;
                        snprintf(name, sizeof(name), "ether%d", ctlrno);
 
-                       if(ether->interrupt != NULL)
-                               intrenable(ether->itype, ether->irq, ether->interrupt, ether, name);
-
-                       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));
-                       if(ether->size)
-                               i += sprint(buf+i, " size 0x%luX", ether->size);
-                       i += sprint(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]);
-                       sprint(buf+i, "\n");
-                       iprint(buf);
-
-                       if(ether->mbps == 100){
-                               netifinit(ether, name, Ntypes, 256*1024);
-                               if(ether->oq == 0)
-                                       ether->oq = qopen(256*1024, Qmsg, 0, 0);
-                       }
-                       else{
-                               netifinit(ether, name, Ntypes, 64*1024);
-                               if(ether->oq == 0)
-                                       ether->oq = qopen(64*1024, Qmsg, 0, 0);
+                       i = snprintf(buf, sizeof(buf),
+                                                "#l%d: %s: %dMbps port 0x%x irq %u", ctlrno,
+                                                ether->type, ether->mbps,
+                                    ether->port,
+                                                ether->irq);
+                       /* Looks like this is for printing MMIO addrs */
+#if 0
+                       if (ether->mem)
+                               i += snprintf(buf + i, sizeof(buf) - i, " addr 0x%lx",
+                                                         PADDR(ether->mem));
+                       if (ether->size)
+                               i += snprintf(buf + i, sizeof(buf) - i, " size 0x%lx",
+                                                         ether->size);
+#endif
+                       i += snprintf(buf + i, sizeof(buf) - i,
+                                                 ": %02.2x:%02.2x:%02.2x:%02.2x:%02.2x:%02.2x",
+                                                 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");
+                       printk(buf);
+
+                       switch (ether->mbps) {
+
+                       case 1 ... 99:
+                               qsize = 64 * 1024;
+                               break;
+                       case 100 ... 999:
+                               qsize = 256 * 1024;
+                               break;
+                       case 1000 ... 9999:
+                               qsize = 1024 * 1024;
+                               break;
+                       default:
+                               qsize = 8 * 1024 * 1024;
                        }
-                       if(ether->oq == 0)
+                       netifinit(ether, name, Ntypes, qsize);
+                       if (ether->oq == 0)
+                               ether->oq = qopen(qsize, Qmsg, 0, 0);
+                       if (ether->oq == 0)
                                panic("etherreset %s", name);
                        ether->alen = Eaddrlen;
                        memmove(ether->addr, ether->ea, Eaddrlen);
@@ -727,32 +782,40 @@ etherreset(void)
                        break;
                }
        }
-       if(ether)
+       if (ether)
                kfree(ether);
-#endif
 }
 
-static void
-etherpower(int on)
+static void etherpower(int on)
 {
        int i;
        struct ether *ether;
 
-       for(i = 0; i < MaxEther; i++){
-               if((ether = etherxx[i]) == NULL || ether->power == NULL)
+       /* TODO: fix etherpower.  locking and ether->readers are broken. */
+       warn("%s needs attention.  had a rough porting from inferno", __FUNCTION__);
+       for (i = 0; i < MaxEther; i++) {
+               if ((ether = etherxx[i]) == NULL || ether->power == NULL)
                        continue;
-               if(on){
-                       if(canrlock(&ether->rwlock))
+               if (on) {
+                       /* brho: not sure what they are doing.  there seem to be certain
+                        * assumptions about calling etherpower.  i think they are using
+                        * canrlock to see if the lock is currently writelocked.  and if it
+                        * was not lockable, they would assume they had the write lock and
+                        * could unlock.  this is super fucked up. */
+                       if (canrlock(&ether->rwlock)) {
+                               runlock(&ether->rwlock);        // brho added this
                                continue;
-                       if(ether->power != NULL)
+                       }
+                       if (ether->power != NULL)
                                ether->power(ether, on);
                        wunlock(&ether->rwlock);
-               }else{
-                       if(ether->rwlock.nr_readers)
-                               continue;
-
+               } else {
+                       /* readers isn't in the ether struct...
+                          if(ether->readers)
+                          continue;
+                        */
                        wlock(&ether->rwlock);
-                       if(ether->power != NULL)
+                       if (ether->power != NULL)
                                ether->power(ether, on);
                        /* Keep locked until power goes back on */
                }
@@ -762,41 +825,40 @@ etherpower(int on)
 #define ETHERPOLY 0xedb88320
 
 /* really slow 32 bit crc for ethers */
-uint32_t
-ethercrc(uint8_t *p, int len)
+uint32_t ethercrc(uint8_t * p, int len)
 {
        int i, j;
        uint32_t crc, b;
 
        crc = 0xffffffff;
-       for(i = 0; i < len; i++){
+       for (i = 0; i < len; i++) {
                b = *p++;
-               for(j = 0; j < 8; j++){
-                       crc = (crc>>1) ^ (((crc^b) & 1) ? ETHERPOLY : 0);
+               for (j = 0; j < 8; j++) {
+                       crc = (crc >> 1) ^ (((crc ^ b) & 1) ? ETHERPOLY : 0);
                        b >>= 1;
                }
        }
        return crc;
 }
 
-struct dev etherdevtab = {
-       'l',
-       "ether",
-
-       etherreset,
-       devinit,
-       ethershutdown,
-       etherattach,
-       etherwalk,
-       etherstat,
-       etheropen,
-       devcreate,
-       etherclose,
-       etherread,
-       etherbread,
-       etherwrite,
-       etherbwrite,
-       devremove,
-       etherwstat,
-       etherpower,
+struct dev etherdevtab __devtab = {
+       .name = "ether",
+
+       .reset = etherreset,
+       .init = devinit,
+       .shutdown = ethershutdown,
+       .attach = etherattach,
+       .walk = etherwalk,
+       .stat = etherstat,
+       .open = etheropen,
+       .create = devcreate,
+       .close = etherclose,
+       .read = etherread,
+       .bread = etherbread,
+       .write = etherwrite,
+       .bwrite = etherbwrite,
+       .remove = devremove,
+       .wstat = etherwstat,
+       .power = etherpower,
+       .chaninfo = devchaninfo,
 };