|  | /* 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 <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 <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 */ | 
|  | }; | 
|  |  | 
|  | static struct ether *etherxx[MaxEther];	/* real controllers */ | 
|  | static struct ether *vlanalloc(struct ether *, int); | 
|  | static void vlanoq(struct ether *, struct block *); | 
|  |  | 
|  | struct chan *etherattach(char *spec) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | uint32_t ctlrno; | 
|  | char *p; | 
|  | struct chan *chan; | 
|  | struct ether *ether, *vlan; | 
|  | int vlanid; | 
|  |  | 
|  | ctlrno = 0; | 
|  | vlanid = 0; | 
|  | if (spec && *spec) { | 
|  | ctlrno = strtoul(spec, &p, 0); | 
|  | /* somebody interpret this for me. */ | 
|  | 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, ERROR_FIXME); | 
|  | rlock(ðer->rwlock); | 
|  | if (waserror()) { | 
|  | runlock(ðer->rwlock); | 
|  | nexterror(); | 
|  | } | 
|  | if (vlanid) { | 
|  | if (ether->max_mtu < ETHERMAXTU + ETHERHDRSIZE + 4) | 
|  | error(EFAIL, "interface cannot support 802.1 tags"); | 
|  | vlan = vlanalloc(ether, vlanid); | 
|  | chan = devattach(devname(), spec); | 
|  | chan->dev = ctlrno + (vlanid << 8); | 
|  | chan->aux = vlan; | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | return chan; | 
|  | } | 
|  | chan = devattach(devname(), spec); | 
|  | chan->dev = ctlrno; | 
|  | chan->aux = ether; | 
|  | if (ether->attach) | 
|  | ether->attach(ether); | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | return chan; | 
|  | } | 
|  |  | 
|  | static void ethershutdown(void) | 
|  | { | 
|  | struct ether *ether; | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < MaxEther; i++) { | 
|  | ether = etherxx[i]; | 
|  | if (ether != NULL && ether->detach != NULL) | 
|  | ether->detach(ether); | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct walkqid *etherwalk(struct chan *chan, struct chan *nchan, | 
|  | char **name, unsigned int nname) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct walkqid *wq; | 
|  | struct ether *ether; | 
|  |  | 
|  | ether = chan->aux; | 
|  | rlock(ðer->rwlock); | 
|  | if (waserror()) { | 
|  | runlock(ðer->rwlock); | 
|  | nexterror(); | 
|  | } | 
|  | wq = netifwalk(ether, chan, nchan, name, nname); | 
|  | if (wq && wq->clone != NULL && wq->clone != chan) | 
|  | wq->clone->aux = ether; | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | return wq; | 
|  | } | 
|  |  | 
|  | static size_t etherstat(struct chan *chan, uint8_t *dp, size_t n) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | size_t s; | 
|  | struct ether *ether; | 
|  |  | 
|  | ether = chan->aux; | 
|  | rlock(ðer->rwlock); | 
|  | if (waserror()) { | 
|  | runlock(ðer->rwlock); | 
|  | nexterror(); | 
|  | } | 
|  | s = netifstat(ether, chan, dp, n); | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | return s; | 
|  | } | 
|  |  | 
|  | static struct chan *etheropen(struct chan *chan, int omode) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct chan *c; | 
|  | struct ether *ether; | 
|  |  | 
|  | ether = chan->aux; | 
|  | rlock(ðer->rwlock); | 
|  | if (waserror()) { | 
|  | runlock(ðer->rwlock); | 
|  | nexterror(); | 
|  | } | 
|  | c = netifopen(ether, chan, omode); | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | return c; | 
|  | } | 
|  |  | 
|  | static void etherclose(struct chan *chan) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct ether *ether; | 
|  |  | 
|  | ether = chan->aux; | 
|  | rlock(ðer->rwlock); | 
|  | if (waserror()) { | 
|  | runlock(ðer->rwlock); | 
|  | nexterror(); | 
|  | } | 
|  | netifclose(ether, chan); | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | } | 
|  |  | 
|  | static size_t etherread(struct chan *chan, void *buf, size_t n, off64_t off) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct ether *ether; | 
|  | uint32_t offset = off; | 
|  | long r; | 
|  |  | 
|  | ether = chan->aux; | 
|  | rlock(ðer->rwlock); | 
|  | if (waserror()) { | 
|  | runlock(ðer->rwlock); | 
|  | nexterror(); | 
|  | } | 
|  | 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) { | 
|  | r = ether->ifstat(ether, buf, n, offset); | 
|  | goto out; | 
|  | } | 
|  | if (NETTYPE(chan->qid.path) == Nstatqid) | 
|  | ether->ifstat(ether, buf, 0, offset); | 
|  | } | 
|  | r = netifread(ether, chan, buf, n, offset); | 
|  | out: | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | static struct block *etherbread(struct chan *chan, size_t n, off64_t offset) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct block *b; | 
|  | struct ether *ether; | 
|  |  | 
|  | ether = chan->aux; | 
|  | rlock(ðer->rwlock); | 
|  | if (waserror()) { | 
|  | runlock(ðer->rwlock); | 
|  | nexterror(); | 
|  | } | 
|  | b = netifbread(ether, chan, n, offset); | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | return b; | 
|  | } | 
|  |  | 
|  | static size_t etherwstat(struct chan *chan, uint8_t *dp, size_t n) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct ether *ether; | 
|  | int r; | 
|  |  | 
|  | ether = chan->aux; | 
|  | rlock(ðer->rwlock); | 
|  | if (waserror()) { | 
|  | runlock(ðer->rwlock); | 
|  | nexterror(); | 
|  | } | 
|  | r = netifwstat(ether, chan, dp, n); | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | static void etherrtrace(struct netfile *f, struct etherpkt *pkt, int len) | 
|  | { | 
|  | uint64_t i, n; | 
|  | struct block *bp; | 
|  |  | 
|  | if (qwindow(f->in) <= 0) | 
|  | return; | 
|  | if (len > 58) | 
|  | n = 58; | 
|  | else | 
|  | n = len; | 
|  | 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[59] = len; | 
|  | 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); | 
|  | } | 
|  |  | 
|  | #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 multi, tome, fromme, vlanid, i; | 
|  | struct netfile **ep, *f, **fp, *fx; | 
|  | struct block *xbp; | 
|  | struct ether *vlan; | 
|  |  | 
|  | 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) { | 
|  | /* 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); | 
|  | } | 
|  | } | 
|  | /* allow normal type handling to accept or discard it */ | 
|  | } | 
|  | } | 
|  |  | 
|  | fx = 0; | 
|  | ep = ðer->f[Ntypes]; | 
|  |  | 
|  | multi = pkt->d[0] & 1; | 
|  | /* check for valid multcast addresses */ | 
|  | 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; | 
|  | } | 
|  | return bp; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* is it for me? */ | 
|  | tome = eaddrcmp(pkt->d, ether->ea) == 0; | 
|  | fromme = eaddrcmp(pkt->s, ether->ea) == 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->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; | 
|  | continue; | 
|  | } | 
|  | xbp = copyblock(bp, MEM_ATOMIC); | 
|  | if (xbp == 0) { | 
|  | ether->soverflows++; | 
|  | continue; | 
|  | } | 
|  | if (qpass(f->in, xbp) < 0) | 
|  | ether->soverflows++; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (fx) { | 
|  | if (qpass(fx->in, bp) < 0) | 
|  | ether->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->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 | 
|  | * 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 = eaddrcmp(pkt->d, ether->ea) == 0; | 
|  | if (loopback || eaddrcmp(pkt->d, ether->bcast) == 0 || ether->prom) { | 
|  | disable_irqsave(&irq_state); | 
|  | etheriq(ether, bp, 0); | 
|  | enable_irqsave(&irq_state); | 
|  | if (loopback) { | 
|  | freeb(bp); | 
|  | return len; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (ether->vlanid) { | 
|  | /* add tag */ | 
|  | bp = padblock(bp, 2 + 2); | 
|  | memmove(bp->rp, bp->rp + 4, 2 * Eaddrlen); | 
|  | hnputs(bp->rp + 2 * Eaddrlen, Type8021Q); | 
|  | /* prio:3 0:1 vid:12 */ | 
|  | hnputs(bp->rp + 2 * Eaddrlen + 2, ether->vlanid & 0xFFF); | 
|  | 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 size_t etherwrite(struct chan *chan, void *buf, size_t n, off64_t unused) | 
|  | { | 
|  | ERRSTACK(2); | 
|  | struct ether *ether; | 
|  | struct block *bp; | 
|  | int onoff; | 
|  | struct cmdbuf *cb; | 
|  | long l; | 
|  |  | 
|  | ether = chan->aux; | 
|  | rlock(ðer->rwlock); | 
|  | if (waserror()) { | 
|  | runlock(ðer->rwlock); | 
|  | nexterror(); | 
|  | } | 
|  | if (NETTYPE(chan->qid.path) != Ndataqid) { | 
|  | l = netifwrite(ether, chan, buf, n); | 
|  | if (l >= 0) | 
|  | goto out; | 
|  | cb = parsecmd(buf, n); | 
|  | 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) | 
|  | qdropoverflow(ether->oq, onoff); | 
|  | kfree(cb); | 
|  | goto out; | 
|  | } | 
|  | kfree(cb); | 
|  | if (ether->ctl != NULL) { | 
|  | l = ether->ctl(ether, buf, n); | 
|  | goto out; | 
|  | } | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | bp->wp += n; | 
|  | poperror(); | 
|  |  | 
|  | l = etheroq(ether, bp); | 
|  | out: | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | return l; | 
|  | } | 
|  |  | 
|  | static size_t etherbwrite(struct chan *chan, struct block *bp, off64_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); | 
|  | poperror(); | 
|  | freeb(bp); | 
|  | return n; | 
|  | } | 
|  | ether = chan->aux; | 
|  | rlock(ðer->rwlock); | 
|  | if (waserror()) { | 
|  | runlock(ðer->rwlock); | 
|  | nexterror(); | 
|  | } | 
|  | if (n > ether->mtu + ETHERHDRSIZE && (bp->flag & Btso) == 0) { | 
|  | freeb(bp); | 
|  | error(E2BIG, ERROR_FIXME); | 
|  | } | 
|  | n = etheroq(ether, bp); | 
|  | poperror(); | 
|  | runlock(ðer->rwlock); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static void nop(struct ether *unused) | 
|  | { | 
|  | } | 
|  |  | 
|  | static long vlanctl(struct ether *ether, void *buf, long n) | 
|  | { | 
|  | uint8_t ea[Eaddrlen]; | 
|  | struct ether *master; | 
|  | struct cmdbuf *cb; | 
|  | int i; | 
|  |  | 
|  | cb = parsecmd(buf, n); | 
|  | 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->addr, ether->ea, Eaddrlen); | 
|  | return 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) { | 
|  | ether->vlanid = 0; | 
|  | master->nvlan--; | 
|  | break; | 
|  | } | 
|  | qunlock(&master->vlq); | 
|  | kfree(cb); | 
|  | return 0; | 
|  | } | 
|  | kfree(cb); | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | return -1;	/* not reached */ | 
|  | } | 
|  |  | 
|  | static struct ether *vlanalloc(struct ether *ether, int id) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct ether *vlan; | 
|  | int i, fid; | 
|  | char name[KNAMELEN]; | 
|  |  | 
|  | qlock(ðer->vlq); | 
|  | if (waserror()) { | 
|  | qunlock(ðer->vlq); | 
|  | nexterror(); | 
|  | } | 
|  | fid = -1; | 
|  | for (i = 0; i < ARRAY_SIZE(ether->vlans); i++) { | 
|  | vlan = ether->vlans[i]; | 
|  | if (vlan != NULL && vlan->vlanid == id) { | 
|  | poperror(); | 
|  | qunlock(ðer->vlq); | 
|  | return vlan; | 
|  | } | 
|  | if (fid < 0 && (vlan == NULL || vlan->vlanid == 0)) | 
|  | fid = i; | 
|  | } | 
|  | if (fid < 0) | 
|  | error(ENOENT, ERROR_FIXME); | 
|  | snprintf(name, sizeof(name), "ether%d.%d", ether->ctlrno, id); | 
|  | vlan = ether->vlans[fid]; | 
|  | if (vlan == NULL) { | 
|  | vlan = kzmalloc(sizeof(struct ether), 1); | 
|  | if (vlan == NULL) | 
|  | error(ENOMEM, ERROR_FIXME); | 
|  | rwinit(&vlan->rwlock); | 
|  | qlock_init(&vlan->vlq); | 
|  | netifinit(vlan, name, Ntypes, ether->limit); | 
|  | /* id is still zero, can't be matched */ | 
|  | ether->vlans[fid] = vlan; | 
|  | ether->nvlan++; | 
|  | } 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->arg = vlan; | 
|  | vlan->mbps = ether->mbps; | 
|  | vlan->fullduplex = ether->fullduplex; | 
|  | vlan->encry = ether->encry; | 
|  | 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; | 
|  | memmove(vlan->addr, ether->addr, sizeof(vlan->addr)); | 
|  | memmove(vlan->bcast, ether->bcast, sizeof(ether->bcast)); | 
|  | vlan->oq = NULL; | 
|  | vlan->ctlr = ether; | 
|  | vlan->vlanid = id; | 
|  | poperror(); | 
|  | qunlock(ðer->vlq); | 
|  | return vlan; | 
|  | } | 
|  |  | 
|  | 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++; | 
|  | } | 
|  |  | 
|  | 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) | 
|  | return -1; | 
|  | nip[0] = *p++; | 
|  | if (*p == 0) | 
|  | return -1; | 
|  | nip[1] = *p++; | 
|  | nip[2] = 0; | 
|  | to[i] = strtoul(nip, 0, 16); | 
|  | if (*p == ':') | 
|  | p++; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void etherreset(void) | 
|  | { | 
|  | struct ether *ether; | 
|  | int i, n, ctlrno, qsize; | 
|  | char name[KNAMELEN], buf[128]; | 
|  |  | 
|  | for (ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++) { | 
|  | if (ether == 0) | 
|  | ether = kzmalloc(sizeof(struct ether), 0); | 
|  | memset(ether, 0, sizeof(struct ether)); | 
|  | rwinit(ðer->rwlock); | 
|  | qlock_init(ðer->vlq); | 
|  | rendez_init(ðer->link_rz); | 
|  | ether->ctlrno = ctlrno; | 
|  | 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, | 
|  | ðer->opt[i][3]) == -1) | 
|  | memset(ether->ea, 0, Eaddrlen); | 
|  | } 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) | 
|  | ether->mbps = 100; | 
|  | } | 
|  | #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); | 
|  |  | 
|  | 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; | 
|  | } | 
|  | 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); | 
|  | memset(ether->bcast, 0xFF, Eaddrlen); | 
|  |  | 
|  | etherxx[ctlrno] = ether; | 
|  | ether = 0; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (ether) | 
|  | kfree(ether); | 
|  | } | 
|  |  | 
|  | static void etherpower(int on) | 
|  | { | 
|  | int i; | 
|  | struct ether *ether; | 
|  |  | 
|  | /* 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) { | 
|  | /* 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(ðer->rwlock)) { | 
|  | // brho added this | 
|  | runlock(ðer->rwlock); | 
|  | continue; | 
|  | } | 
|  | if (ether->power != NULL) | 
|  | ether->power(ether, on); | 
|  | wunlock(ðer->rwlock); | 
|  | } else { | 
|  | /* readers isn't in the ether struct... | 
|  | if(ether->readers) | 
|  | continue; | 
|  | */ | 
|  | wlock(ðer->rwlock); | 
|  | if (ether->power != NULL) | 
|  | ether->power(ether, on); | 
|  | /* Keep locked until power goes back on */ | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #define ETHERPOLY 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) ? ETHERPOLY : 0); | 
|  | b >>= 1; | 
|  | } | 
|  | } | 
|  | return crc; | 
|  | } | 
|  |  | 
|  | 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, | 
|  | }; |