| /* 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, |
| }; |