add the new files :P - vx32 - Local 9vx git repository for patches.
       
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
 (DIR) commit 8aa03c4f1dc493f3a62bc88eb7d0c1ac26e24cea
 (DIR) parent 99843ed18b65e16b10f49b73956f7bfd9c495aac
 (HTM) Author: Jesus Galan Lopez (yiyus) <yiyu.jgl@gmail.com>
       Date:   Wed,  2 Jun 2010 23:04:57 +0200
       
       add the new files :P
       
       Diffstat:
         .hgignore                           |       2 ++
         src/9vx/a/aoe.h                     |      84 +++++++++++++++++++++++++++++++
         src/9vx/a/devaoe.c                  |    2575 ++++++++++++++++++++++++++++++
         src/9vx/a/devether.c                |     534 +++++++++++++++++++++++++++++++
         src/9vx/a/etherif.h                 |      39 +++++++++++++++++++++++++++++++
         src/9vx/a/netif.c                   |     761 ++++++++++++++++++++++++++++++
         src/9vx/a/sdaoe.c                   |     652 +++++++++++++++++++++++++++++++
         src/9vx/etherve.c                   |     161 +++++++++++++++++++++++++++++++
       
       8 files changed, 4808 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/.hgignore b/.hgignore
       @@ -25,6 +25,8 @@ src/vxa/bz2/*ebz2
        src/vxlinux/vxlinux
        src/9vx/9vx
        src/9vx/bootcode.S
       +src/9vx/fossil.S
       +src/9vx/venti.S
        src/9vx/data2s
        src/9vx/a/errstr.h
        src/9vx/kerndate.h
 (DIR) diff --git a/src/9vx/a/aoe.h b/src/9vx/a/aoe.h
       @@ -0,0 +1,84 @@
       +enum {
       +        ACata,
       +        ACconfig,
       +};
       +
       +enum {
       +        AQCread,
       +        AQCtest,
       +        AQCprefix,
       +        AQCset,
       +        AQCfset,
       +};
       +
       +enum {
       +        AEcmd        = 1,
       +        AEarg,
       +        AEdev,
       +        AEcfg,
       +        AEver,
       +};
       +
       +enum {
       +        Aoetype        = 0x88a2,
       +        Aoesectsz = 512,
       +        Szaoeata        = 24+12,
       +        Szaoeqc        = 24+8,
       +        Aoever        = 1,
       +
       +        AFerr        = 1<<2,
       +        AFrsp        = 1<<3,
       +
       +        AAFwrite= 1,
       +        AAFext        = 1<<6,
       +};
       +
       +ttypedef struct {
       +        uchar        dst[Eaddrlen];
       +        uchar        src[Eaddrlen];
       +        uchar        type[2];
       +        uchar        verflag;
       +        uchar        error;
       +        uchar        major[2];
       +        uchar        minor;
       +        uchar        cmd;
       +        uchar        tag[4];
       +} Aoehdr;
       +
       +ttypedef struct {
       +        uchar        dst[Eaddrlen];
       +        uchar        src[Eaddrlen];
       +        uchar        type[2];
       +        uchar        verflag;
       +        uchar        error;
       +        uchar        major[2];
       +        uchar        minor;
       +        uchar        cmd;
       +        uchar        tag[4];
       +        uchar        aflag;
       +        uchar        errfeat;
       +        uchar        scnt;
       +        uchar        cmdstat;
       +        uchar        lba[6];
       +        uchar        res[2];
       +} Aoeata;
       +
       +ttypedef struct {
       +        uchar        dst[Eaddrlen];
       +        uchar        src[Eaddrlen];
       +        uchar        type[2];
       +        uchar        verflag;
       +        uchar        error;
       +        uchar        major[2];
       +        uchar        minor;
       +        uchar        cmd;
       +        uchar        tag[4];
       +        uchar        bufcnt[2];
       +        uchar        fwver[2];
       +        uchar        scnt;
       +        uchar        verccmd;
       +        uchar        cslen[2];
       +} Aoeqc;
       +
       +extern char Echange[];
       +extern char Enotup[];
 (DIR) diff --git a/src/9vx/a/devaoe.c b/src/9vx/a/devaoe.c
       @@ -0,0 +1,2575 @@
       +/*
       + *        © 2005-8 coraid
       + *        aoe storage initiator
       + */
       +
       +#include "u.h"
       +#include "lib.h"
       +#include "mem.h"
       +#include "dat.h"
       +#include "fns.h"
       +#include "io.h"
       +#include "ureg.h"
       +#include "error.h"
       +#include "netif.h"
       +#include "etherif.h"
       +#include "ip/ip.h"
       +#include "aoe.h"
       +
       +#define        WAKEUP(x)        wakeup(&((x)->rend))
       +#define SLEEP(a,b,c)        sleep(&(a->rend), b, c)
       +
       +//#pragma        varargck argpos        eventlog        1
       +
       +#define dprint(...)        if(debug) eventlog(__VA_ARGS__); else USED(debug);
       +#define uprint(...)        snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
       +
       +enum {
       +        Maxunits        = 0xff,
       +        Maxframes        = 128,
       +        Maxmtu                = 100000,
       +        Ndevlink        = 6,
       +        Nea                = 6,
       +        Nnetlink        = 6,
       +};
       +
       +#define TYPE(q)                ((ulong)(q).path & 0xf)
       +#define UNIT(q)                (((ulong)(q).path>>4) & 0xff)
       +#define L(q)                (((ulong)(q).path>>12) & 0xf)
       +#define QID(u, t)         ((u)<<4 | (t))
       +#define Q3(l, u, t)        ((l)<<8 | QID(u, t))
       +#define UP(d)                ((d)->flag & Dup)
       +
       +#define        Ticks                msec()
       +#define        Ms2tk(t)        (((t)*HZ)/1000)
       +#define        Tk2ms(t)        (((t)*1000)/HZ)
       +
       +enum {
       +        Qzero,
       +        Qtopdir                = 1,
       +        Qtopbase,
       +        Qtopctl                = Qtopbase,
       +        Qtoplog,
       +        Qtopend,
       +
       +        Qunitdir,
       +        Qunitbase,
       +        Qctl                = Qunitbase,
       +        Qdata,
       +        Qconfig,
       +        Qident,
       +
       +        Qdevlinkdir,
       +        Qdevlinkbase,
       +        Qdevlink        = Qdevlinkbase,
       +        Qdevlinkend,
       +
       +        Qtopfiles        = Qtopend-Qtopbase,
       +        Qdevlinkfiles        = Qdevlinkend-Qdevlinkbase,
       +
       +        Eventlen         = 256,
       +        Nevents         = 64,
       +
       +        Fread                = 0,
       +        Fwrite,
       +        Tfree                = -1,
       +        Tmgmt,
       +
       +        /* round trip bounds, timeouts, in ticks */
       +        Rtmax                = Ms2tk(320),
       +        Rtmin                = Ms2tk(20),
       +        Srbtimeout        = 45*HZ,
       +
       +        Dbcnt                = 1024,
       +
       +        Crd                = 0x20,
       +        Crdext                = 0x24,
       +        Cwr                = 0x30,
       +        Cwrext                = 0x34,
       +        Cid                = 0xec,
       +};
       +
       +enum {
       +        Read,
       +        Write,
       +};
       +
       +/*
       + * unified set of flags
       + * a Netlink + Aoedev most both be jumbo capable
       + * to send jumbograms to that interface.
       + */
       +enum {
       +        /* sync with ahci.h */
       +        Dllba         = 1<<0,
       +        Dsmart        = 1<<1,
       +        Dpower        = 1<<2,
       +        Dnop        = 1<<3,
       +        Datapi        = 1<<4,
       +        Datapi16= 1<<5,
       +
       +        /* aoe specific */
       +        Dup        = 1<<6,
       +        Djumbo        = 1<<7,
       +};
       +
       +static char *flagname[] = {
       +        "llba",
       +        "smart",
       +        "power",
       +        "nop",
       +        "atapi",
       +        "atapi16",
       +
       +        "up",
       +        "jumbo",
       +};
       +
       +ttypedef struct {
       +        uchar        flag;
       +        uchar        lostjumbo;
       +        int        datamtu;
       +
       +        Chan        *cc;
       +        Chan        *dc;
       +        Chan        *mtu;                /* open early to prevent bind issues. */
       +        char        path[Maxpath];
       +        uchar        ea[Eaddrlen];
       +} Netlink;
       +
       +ttypedef struct {
       +        Netlink        *nl;
       +        int        nea;
       +        ulong        eaidx;
       +        uchar        eatab[Nea][Eaddrlen];
       +        int        datamtu;
       +        ulong        npkt;
       +        ulong        resent;
       +        uchar        flag;
       +
       +        ulong        rttavg;
       +        ulong        mintimer;
       +} Devlink;
       +
       +ttypedef struct Srb Srb;
       +struct Srb {
       +        Rendez        rend;
       +        Srb        *next;
       +        ulong        ticksent;
       +        ulong        len;
       +        vlong        sector;
       +        short        write;
       +        short        nout;
       +        char        *error;
       +        void        *dp;
       +        void        *data;
       +};
       +
       +ttypedef struct {
       +        int        tag;
       +        ulong        bcnt;
       +        ulong        dlen;
       +        vlong        lba;
       +        ulong        ticksent;
       +        int        nhdr;
       +        uchar        hdr[ETHERMINTU];
       +        void        *dp;
       +        Devlink        *dl;
       +        Netlink        *nl;
       +        int        eaidx;
       +        Srb        *srb;
       +} Frame;
       +
       +ttypedef struct Aoedev Aoedev;
       +struct Aoedev {
       +        QLock        qlock;
       +        Aoedev        *next;
       +
       +        ulong        vers;
       +
       +        int        ndl;
       +        ulong        dlidx;
       +        Devlink        *dl;
       +        Devlink        dltab[Ndevlink];
       +
       +        ushort        fwver;
       +        uchar        flag;
       +        int        nopen;
       +        int        major;
       +        int        minor;
       +        int        unit;
       +        int        lasttag;
       +        int        nframes;
       +        Frame        *frames;
       +        vlong        bsize;
       +        vlong        realbsize;
       +
       +        uint        maxbcnt;
       +        uint        maxmtu;
       +        ulong        lostjumbo;
       +        ushort        nout;
       +        ushort        maxout;
       +        ulong        lastwadj;
       +        Srb        *head;
       +        Srb        *tail;
       +        Srb        *inprocess;
       +
       +        char        serial[20+1];
       +        char        firmware[8+1];
       +        char        model[40+1];
       +        int        nconfig;
       +        uchar        config[1024];
       +        uchar        ident[512];
       +};
       +
       +//#pragma        varargck type        "æ"        Aoedev*
       +
       +static struct {
       +        Lock        lk;
       +        QLock        qlock;
       +        Rendez        rend;
       +        char        buf[Eventlen*Nevents];
       +        char        *rp;
       +        char        *wp;
       +} events;
       +
       +static struct {
       +        RWlock        rwlock;
       +        int        nd;
       +        Aoedev        *d;
       +} devs;
       +
       +static struct {
       +        Lock        lk;
       +        int        reader[Nnetlink];        /* reader is running. */
       +        Rendez        rendez[Nnetlink];        /* confirm exit. */
       +        Netlink        nl[Nnetlink];
       +} netlinks;
       +
       +extern        Dev         aoedevtab;
       +static        Ref         units;
       +static        Ref        drivevers;
       +static        int        debug;
       +static        int        autodiscover        = 1;
       +static        int        rediscover;
       +        char         Enotup[]         = "aoe device is down";
       +        char        Echange[]        = "media or partition has changed";
       +
       +static Srb*
       +srballoc(ulong sz)
       +{
       +        Srb *srb;
       +
       +        srb = malloc(sizeof *srb+sz);
       +        srb->dp = srb->data = srb+1;
       +        srb->ticksent = Ticks;
       +        return srb;
       +}
       +
       +static Srb*
       +srbkalloc(void *db, ulong dummy)
       +{
       +        Srb *srb;
       +
       +        srb = malloc(sizeof *srb);
       +        srb->dp = srb->data = db;
       +        srb->ticksent = Ticks;
       +        return srb;
       +}
       +
       +#define srbfree(srb) free(srb)
       +
       +static void
       +srberror(Srb *srb, char *s)
       +{
       +        srb->error = s;
       +        srb->nout--;
       +        WAKEUP(srb);
       +}
       +
       +static void
       +frameerror(Aoedev *d, Frame *f, char *s)
       +{
       +        Srb *srb;
       +
       +        srb = f->srb;
       +        if(f->tag == Tfree)
       +                return;
       +        f->srb = nil;
       +        f->tag = Tfree;                /* don't get fooled by way-slow responses */
       +        if(!srb)
       +                return;
       +        srberror(srb, s);
       +        d->nout--;
       +}
       +
       +static char*
       +unitname(Aoedev *d)
       +{
       +        uprint("%d.%d", d->major, d->minor);
       +        return up->genbuf;
       +}
       +
       +static long
       +eventlogread(void *a, long n)
       +{
       +        int len;
       +        char *p, *buf;
       +
       +        buf = smalloc(Eventlen);
       +        QLOCK(&events);
       +        LOCK(&events);
       +        p = events.rp;
       +        len = *p;
       +        if(len == 0){
       +                n = 0;
       +                UNLOCK(&events);
       +        } else {
       +                if(n > len)
       +                        n = len;
       +                /* can't move directly into pageable space with events lock held */
       +                memmove(buf, p+1, n);
       +                *p = 0;
       +                events.rp = p += Eventlen;
       +                if(p >= events.buf + sizeof events.buf)
       +                        events.rp = events.buf;
       +                UNLOCK(&events);
       +
       +                /* the concern here is page faults in memmove below */
       +                if(waserror()){
       +                        free(buf);
       +                        QUNLOCK(&events);
       +                        nexterror();
       +                }
       +                memmove(a, buf, n);
       +                poperror();
       +        }
       +        free(buf);
       +        QUNLOCK(&events);
       +        return n;
       +}
       +
       +static int
       +eventlog(char *fmt, ...)
       +{
       +        int dragrp, n;
       +        char *p;
       +        va_list arg;
       +
       +        LOCK(&events);
       +        p = events.wp;
       +        dragrp = *p++;
       +        va_start(arg, fmt);
       +        n = vsnprint(p, Eventlen-1, fmt, arg);
       +        *--p = n;
       +        p = events.wp += Eventlen;
       +        if(p >= events.buf + sizeof events.buf)
       +                p = events.wp = events.buf;
       +        if(dragrp)
       +                events.rp = p;
       +        UNLOCK(&events);
       +        WAKEUP(&events);
       +        return n;
       +}
       +
       +static int
       +eventcount(void)
       +{
       +        int n;
       +
       +        LOCK(&events);
       +        if(*events.rp == 0)
       +                n = 0;
       +        else if(events.wp < events.rp)
       +                n = Nevents - (events.rp - events.wp);
       +        else
       +                n = events.wp - events.rp;
       +        UNLOCK(&events);
       +        return n/Eventlen;
       +}
       +
       +static int
       +ttsince(int tag)
       +{
       +        int n;
       +
       +        n = Ticks & 0xffff;
       +        n -= tag & 0xffff;
       +        if(n < 0)
       +                n += 1<<16;
       +        return n;
       +}
       +
       +static int
       +newtag(Aoedev *d)
       +{
       +        int t;
       +
       +        do {
       +                t = ++d->lasttag << 16;
       +                t |= Ticks & 0xffff;
       +        } while (t == Tfree || t == Tmgmt);
       +        return t;
       +}
       +
       +static void
       +downdev(Aoedev *d, char *err)
       +{
       +        Frame *f, *e;
       +
       +        d->flag &= ~Dup;
       +        f = d->frames;
       +        e = f + d->nframes;
       +        for(; f < e; f->tag = Tfree, f->srb = nil, f++)
       +                frameerror(d, f, Enotup);
       +        d->inprocess = nil;
       +        eventlog("%æ: removed; %s\n", d, err);
       +}
       +
       +static Block*
       +allocfb(Frame *f)
       +{
       +        int len;
       +        Block *b;
       +
       +        len = f->nhdr + f->dlen;
       +        if(len < ETHERMINTU)
       +                len = ETHERMINTU;
       +        b = allocb(len);
       +        memmove(b->wp, f->hdr, f->nhdr);
       +        if(f->dlen)
       +                memmove(b->wp + f->nhdr, f->dp, f->dlen);
       +        b->wp += len;
       +        return b;
       +}
       +
       +static void
       +putlba(Aoeata *a, vlong lba)
       +{
       +        uchar *c;
       +
       +        c = a->lba;
       +        c[0] = lba;
       +        c[1] = lba >> 8;
       +        c[2] = lba >> 16;
       +        c[3] = lba >> 24;
       +        c[4] = lba >> 32;
       +        c[5] = lba >> 40;
       +}
       +
       +static Devlink*
       +pickdevlink(Aoedev *d)
       +{
       +        ulong i, n;
       +        Devlink *l;
       +
       +        for(i = 0; i < d->ndl; i++){
       +                n = d->dlidx++ % d->ndl;
       +                l = d->dl + n;
       +                if(l && l->flag & Dup)
       +                        return l;
       +        }
       +        return 0;
       +}
       +
       +static int
       +pickea(Devlink *l)
       +{
       +        if(l == 0)
       +                return -1;
       +        if(l->nea == 0)
       +                return -1;
       +        return l->eaidx++ % l->nea;
       +}
       +
       +static int
       +hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd)
       +{
       +        int i;
       +        Devlink *l;
       +
       +        if(f->srb)
       +        if((long)(Ticks-f->srb->ticksent) > Srbtimeout){
       +                eventlog("%æ: srb timeout\n", d);
       +                frameerror(d, f, Etimedout);
       +                return -1;
       +        }
       +        l = pickdevlink(d);
       +        i = pickea(l);
       +        if(i == -1){
       +                downdev(d, "resend fails; no netlink/ea");
       +                return -1;
       +        }
       +        memmove(h->dst, l->eatab[i], Eaddrlen);
       +        memmove(h->src, l->nl->ea, sizeof h->src);
       +        hnputs(h->type, Aoetype);
       +        h->verflag = Aoever << 4;
       +        h->error = 0;
       +        hnputs(h->major, d->major);
       +        h->minor = d->minor;
       +        h->cmd = cmd;
       +
       +        hnputl(h->tag, f->tag = newtag(d));
       +        f->dl = l;
       +        f->nl = l->nl;
       +        f->eaidx = i;
       +        f->ticksent = Ticks;
       +
       +        return f->tag;
       +}
       +
       +static int
       +resend(Aoedev *d, Frame *f)
       +{
       +        ulong n;
       +        Aoeata *a;
       +
       +        a = (Aoeata*)f->hdr;
       +        if(hset(d, f, (Aoehdr*)a, a->cmd) == -1)
       +                return -1;
       +        n = f->bcnt;
       +        if(n > d->maxbcnt){
       +                n = d->maxbcnt;                /* mtu mismatch (jumbo fail?) */
       +                if(f->dlen > n)
       +                        f->dlen = n;
       +        }
       +        a->scnt = n / Aoesectsz;
       +        f->dl->resent++;
       +        f->dl->npkt++;
       +        if(waserror())
       +                /* should remove the netlink */
       +                return -1;
       +        devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
       +        poperror();
       +        return 0;
       +}
       +
       +static void
       +discover(int major, int minor)
       +{
       +        Aoehdr *h;
       +        Block *b;
       +        Netlink *nl, *e;
       +
       +        nl = netlinks.nl;
       +        e = nl + nelem(netlinks.nl);
       +        for(; nl < e; nl++){
       +                if(nl->cc == nil)
       +                        continue;
       +                b = allocb(ETHERMINTU);
       +                if(waserror()){
       +                        freeb(b);
       +                        nexterror();
       +                }
       +                b->wp = b->rp + ETHERMINTU;
       +                memset(b->rp, 0, ETHERMINTU);
       +                h = (Aoehdr*)b->rp;
       +                memset(h->dst, 0xff, sizeof h->dst);
       +                memmove(h->src, nl->ea, sizeof h->src);
       +                hnputs(h->type, Aoetype);
       +                h->verflag = Aoever << 4;
       +                hnputs(h->major, major);
       +                h->minor = minor;
       +                h->cmd = ACconfig;
       +                poperror();
       +                devtab[nl->dc->type]->bwrite(nl->dc, b, 0);
       +        }
       +}
       +
       +/*
       + * Check all frames on device and resend any frames that have been
       + * outstanding for 200% of the device round trip time average.
       + */
       +static void
       +aoesweepproc(void *dummy)
       +{
       +        ulong i, tx, timeout, nbc;
       +        vlong starttick;
       +        enum { Nms = 100, Nbcms = 30*1000, };
       +        uchar *ea;
       +        Aoeata *a;
       +        Aoedev *d;
       +        Devlink *l;
       +        Frame *f, *e;
       +
       +        nbc = Nbcms/Nms;
       +loop:
       +        if(nbc-- == 0){
       +                if(rediscover && !waserror()){
       +                        discover(0xffff, 0xff);
       +                        poperror();
       +                }
       +                nbc = Nbcms/Nms;
       +        }
       +        starttick = Ticks;
       +        RLOCK(&devs);
       +        for(d = devs.d; d; d = d->next){
       +                if(!CANQLOCK(d))
       +                        continue;
       +                if(!UP(d)){
       +                        QUNLOCK(d);
       +                        continue;
       +                }
       +                tx = 0;
       +                f = d->frames;
       +                e = f + d->nframes;
       +                for (; f < e; f++){
       +                        if(f->tag == Tfree)
       +                                continue;
       +                        l = f->dl;
       +                        timeout = l->rttavg << 1;
       +                        i = tsince(f->tag);
       +                        if(i < timeout)
       +                                continue;
       +                        if(d->nout == d->maxout){
       +                                if(d->maxout > 1)
       +                                        d->maxout--;
       +                                d->lastwadj = Ticks;
       +                        }
       +                        a = (Aoeata*)f->hdr;
       +                        if(a->scnt > Dbcnt / Aoesectsz &&
       +                           ++f->nl->lostjumbo > (d->nframes << 1)){
       +                                ea = f->dl->eatab[f->eaidx];
       +                                eventlog("%æ: jumbo failure on %s:%E; lba%lld\n",
       +                                        d, f->nl->path, ea, f->lba);
       +                                d->maxbcnt = Dbcnt;
       +                                d->flag &= ~Djumbo;
       +                        }
       +                        resend(d, f);
       +                        if(tx++ == 0){
       +                                if((l->rttavg <<= 1) > Rtmax)
       +                                        l->rttavg = Rtmax;
       +                                eventlog("%æ: rtt %ldms\n", d, Tk2ms(l->rttavg));
       +                        }
       +                }
       +                if(d->nout == d->maxout && d->maxout < d->nframes &&
       +                   TK2MS(Ticks-d->lastwadj) > 10*1000){
       +                        d->maxout++;
       +                        d->lastwadj = Ticks;
       +                }
       +                QUNLOCK(d);
       +        }
       +        RUNLOCK(&devs);
       +        i = Nms - TK2MS(Ticks - starttick);
       +        if(i > 0)
       +                tsleep(&up->sleep, return0, 0, i);
       +        goto loop;
       +}
       +
       +static int
       +fmtaoe(Fmt *f)
       +{
       +        char buf[16];
       +        Aoedev *d;
       +
       +        d = va_arg(f->args, Aoedev*);
       +        snprint(buf, sizeof buf, "aoe%d.%d", d->major, d->minor);
       +        return fmtstrcpy(f, buf);
       +}
       +
       +static void netbind(char *path);
       +
       +static void
       +aoecfg(void)
       +{
       +        int n, i;
       +        char *p, *f[32], buf[24];
       +
       +        if(1)
       +//        if((p = getconf("aoeif")) == nil || (n = tokenize(p, f, nelem(f))) < 1)
       +                return;
       +        /* goo! */
       +        for(i = 0; i < n; i++){
       +                p = f[i];
       +                if(strncmp(p, "ether", 5) == 0)
       +                        snprint(buf, sizeof buf, "#l%c/ether%c", p[5], p[5]);
       +                else if(strncmp(p, "#l", 2) == 0)
       +                        snprint(buf, sizeof buf, "#l%c/ether%c", p[2], p[2]);
       +                else
       +                        continue;
       +                if(!waserror()){
       +                        netbind(buf);
       +                        poperror();
       +                }
       +        }
       +}
       +
       +static void
       +aoeinit(void)
       +{
       +        static int init;
       +        static QLock l;
       +
       +        if(!canqlock(&l))
       +                return;
       +        if(init == 0){
       +                fmtinstall(L'æ', fmtaoe);
       +                events.rp = events.wp = events.buf;
       +                kproc("aoesweep", aoesweepproc, nil);
       +                aoecfg();
       +                init = 1;
       +        }
       +        qunlock(&l);
       +}
       +
       +static Chan*
       +aoeattach(char *spec)
       +{
       +        Chan *c;
       +
       +        if(*spec)
       +                error(Enonexist);
       +        aoeinit();
       +        c = devattach(L'æ', spec);
       +        mkqid(&c->qid, Qzero, 0, QTDIR);
       +        return c;
       +}
       +
       +static Aoedev*
       +unitseq(ulong unit)
       +{
       +        int i;
       +        Aoedev *d;
       +
       +        i = 0;
       +        RLOCK(&devs);
       +        for(d = devs.d; d; d = d->next)
       +                if(i++ == unit)
       +                        break;
       +        RUNLOCK(&devs);
       +        return d;
       +}
       +
       +static Aoedev*
       +unit2dev(ulong unit)
       +{
       +        Aoedev *d;
       +
       +        RLOCK(&devs);
       +        for(d = devs.d; d; d = d->next)
       +                if(d->unit == unit){
       +                        RUNLOCK(&devs);
       +                        return d;
       +                }
       +        RUNLOCK(&devs);
       +        error("unit lookup failure");
       +        return nil;
       +}
       +
       +static int
       +unitgen(Chan *c, ulong type, Dir *dp)
       +{
       +        int perm, t;
       +        ulong vers;
       +        vlong size;
       +        char *p;
       +        Aoedev *d;
       +        Qid q;
       +
       +        d = unit2dev(UNIT(c->qid));
       +        perm = 0644;
       +        size = 0;
       +        vers = d->vers;
       +        t = QTFILE;
       +
       +        switch(type){
       +        default:
       +                return -1;
       +        case Qctl:
       +                p = "ctl";
       +                break;
       +        case Qdata:
       +                p = "data";
       +                perm = 0640;
       +                if(UP(d))
       +                        size = d->bsize;
       +                break;
       +        case Qconfig:
       +                p = "config";
       +                if(UP(d))
       +                        size = d->nconfig;
       +                break;
       +        case Qident:
       +                p = "ident";
       +                if(UP(d))
       +                        size = sizeof d->ident;
       +                break;
       +        case Qdevlinkdir:
       +                p = "devlink";
       +                t = QTDIR;
       +                perm = 0555;
       +                break;
       +        }
       +        mkqid(&q, QID(UNIT(c->qid), type), vers, t);
       +        devdir(c, q, p, size, eve, perm, dp);
       +        return 1;
       +}
       +
       +static int
       +ttopgen(Chan *c, ulong type, Dir *d)
       +{
       +        int perm;
       +        vlong size;
       +        char *p;
       +        Qid q;
       +
       +        perm = 0444;
       +        size = 0;
       +        switch(type){
       +        default:
       +                return -1;
       +        case Qtopctl:
       +                p = "ctl";
       +                perm = 0644;
       +                break;
       +        case Qtoplog:
       +                p = "log";
       +                size = eventcount();
       +                break;
       +        }
       +        mkqid(&q, type, 0, QTFILE);
       +        devdir(c, q, p, size, eve, perm, d);
       +        return 1;
       +}
       +
       +static int
       +aoegen(Chan *c, char *d0, Dirtab *d1, int d2, int s, Dir *dp)
       +{
       +        int i;
       +        Aoedev *d;
       +        Qid q;
       +
       +        if(c->qid.path == 0){
       +                switch(s){
       +                case DEVDOTDOT:
       +                        q.path = 0;
       +                        q.type = QTDIR;
       +                        devdir(c, q, "#æ", 0, eve, 0555, dp);
       +                        break;
       +                case 0:
       +                        q.path = Qtopdir;
       +                        q.type = QTDIR;
       +                        devdir(c, q, "aoe", 0, eve, 0555, dp);
       +                        break;
       +                default:
       +                        return -1;
       +                }
       +                return 1;
       +        }
       +
       +        switch(TYPE(c->qid)){
       +        default:
       +                return -1;
       +        case Qtopdir:
       +                if(s == DEVDOTDOT){
       +                        mkqid(&q, Qzero, 0, QTDIR);
       +                        devdir(c, q, "aoe", 0, eve, 0555, dp);
       +                        return 1;
       +                }
       +                if(s < Qtopfiles)
       +                        return topgen(c, Qtopbase + s, dp);
       +                s -= Qtopfiles;
       +                if((d = unitseq(s)) == 0)
       +                        return -1;
       +                mkqid(&q, QID(d->unit, Qunitdir), 0, QTDIR);
       +                devdir(c, q, unitname(d), 0, eve, 0555, dp);
       +                return 1;
       +        case Qtopctl:
       +        case Qtoplog:
       +                return topgen(c, TYPE(c->qid), dp);
       +        case Qunitdir:
       +                if(s == DEVDOTDOT){
       +                        mkqid(&q, QID(0, Qtopdir), 0, QTDIR);
       +                        uprint("%uld", UNIT(c->qid));
       +                        devdir(c, q, up->genbuf, 0, eve, 0555, dp);
       +                        return 1;
       +                }
       +                return unitgen(c, Qunitbase+s, dp);
       +        case Qctl:
       +        case Qdata:
       +        case Qconfig:
       +        case Qident:
       +                return unitgen(c, TYPE(c->qid), dp);
       +        case Qdevlinkdir:
       +                i = UNIT(c->qid);
       +                if(s == DEVDOTDOT){
       +                        mkqid(&q, QID(i, Qunitdir), 0, QTDIR);
       +                        devdir(c, q, "devlink", 0, eve, 0555, dp);
       +                        return 1;
       +                }
       +                if(i >= units.ref)
       +                        return -1;
       +                d = unit2dev(i);
       +                if(s >= d->ndl)
       +                        return -1;
       +                uprint("%d", s);
       +                mkqid(&q, Q3(s, i, Qdevlink), 0, QTFILE);
       +                devdir(c, q, up->genbuf, 0, eve, 0755, dp);
       +                return 1;
       +        case Qdevlink:
       +                uprint("%d", s);
       +                mkqid(&q, Q3(s, UNIT(c->qid), Qdevlink), 0, QTFILE);
       +                devdir(c, q, up->genbuf, 0, eve, 0755, dp);
       +                return 1;
       +        }
       +}
       +
       +static Walkqid*
       +aoewalk(Chan *c, Chan *nc, char **name, int nname)
       +{
       +        return devwalk(c, nc, name, nname, nil, 0, aoegen);
       +}
       +
       +static int
       +aoestat(Chan *c, uchar *db, int n)
       +{
       +        return devstat(c, db, n, nil, 0, aoegen);
       +}
       +
       +static Chan*
       +aoeopen(Chan *c, int omode)
       +{
       +        Aoedev *d;
       +
       +        if(TYPE(c->qid) != Qdata)
       +                return devopen(c, omode, 0, 0, aoegen);
       +
       +        d = unit2dev(UNIT(c->qid));
       +        QLOCK(d);
       +        if(waserror()){
       +                QUNLOCK(d);
       +                nexterror();
       +        }
       +        if(!UP(d))
       +                error(Enotup);
       +        c = devopen(c, omode, 0, 0, aoegen);
       +        d->nopen++;
       +        poperror();
       +        QUNLOCK(d);
       +        return c;
       +}
       +
       +static void
       +aoeclose(Chan *c)
       +{
       +        Aoedev *d;
       +
       +        if(TYPE(c->qid) != Qdata || (c->flag&COPEN) == 0)
       +                return;
       +
       +        d = unit2dev(UNIT(c->qid));
       +        QLOCK(d);
       +        if(--d->nopen == 0 && !waserror()){
       +                discover(d->major, d->minor);
       +                poperror();
       +        }
       +        QUNLOCK(d);
       +}
       +
       +static void
       +atarw(Aoedev *d, Frame *f)
       +{
       +        ulong bcnt;
       +        char extbit, writebit;
       +        Aoeata *ah;
       +        Srb *srb;
       +
       +        extbit = 0x4;
       +        writebit = 0x10;
       +
       +        srb = d->inprocess;
       +        bcnt = d->maxbcnt;
       +        if(bcnt > srb->len)
       +                bcnt = srb->len;
       +        f->nhdr = Szaoeata;
       +        memset(f->hdr, 0, f->nhdr);
       +        ah = (Aoeata*)f->hdr;
       +        if(hset(d, f, (Aoehdr*)ah, ACata) == -1)
       +                return;
       +        f->dp = srb->dp;
       +        f->bcnt = bcnt;
       +        f->lba = srb->sector;
       +        f->srb = srb;
       +
       +        ah->scnt = bcnt / Aoesectsz;
       +        putlba(ah, f->lba);
       +        if(d->flag & Dllba)
       +                ah->aflag |= AAFext;
       +        else {
       +                extbit = 0;
       +                ah->lba[3] &= 0x0f;
       +                ah->lba[3] |= 0xe0;        /* LBA bit+obsolete 0xa0 */
       +        }
       +        if(srb->write){
       +                ah->aflag |= AAFwrite;
       +                f->dlen = bcnt;
       +        }else{
       +                writebit = 0;
       +                f->dlen = 0;
       +        }
       +        ah->cmdstat = 0x20 | writebit | extbit;
       +
       +        /* mark tracking fields and load out */
       +        srb->nout++;
       +        srb->dp = (uchar*)srb->dp + bcnt;
       +        srb->len -= bcnt;
       +        srb->sector += bcnt / Aoesectsz;
       +        if(srb->len == 0)
       +                d->inprocess = nil;
       +        d->nout++;
       +        f->dl->npkt++;
       +        if(waserror()){
       +                f->tag = Tfree;
       +                d->inprocess = nil;
       +                nexterror();
       +        }
       +        devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
       +        poperror();
       +}
       +
       +static char*
       +aoeerror(Aoehdr *h)
       +{
       +        int n;
       +        static char *errs[] = {
       +                "aoe protocol error: unknown",
       +                "aoe protocol error: bad command code",
       +                "aoe protocol error: bad argument param",
       +                "aoe protocol error: device unavailable",
       +                "aoe protocol error: config string present",
       +                "aoe protocol error: unsupported version",
       +        };
       +
       +        if((h->verflag & AFerr) == 0)
       +                return 0;
       +        n = h->error;
       +        if(n > nelem(errs))
       +                n = 0;
       +        return errs[n];
       +}
       +
       +static void
       +rtupdate(Devlink *l, int rtt)
       +{
       +        int n;
       +
       +        n = rtt;
       +        if(rtt < 0){
       +                n = -rtt;
       +                if(n < Rtmin)
       +                        n = Rtmin;
       +                else if(n > Rtmax)
       +                        n = Rtmax;
       +                l->mintimer += (n - l->mintimer) >> 1;
       +        } else if(n < l->mintimer)
       +                n = l->mintimer;
       +        else if(n > Rtmax)
       +                n = Rtmax;
       +
       +        /* g == .25; cf. Congestion Avoidance and Control, Jacobson&Karels; 1988 */
       +        n -= l->rttavg;
       +        l->rttavg += n >> 2;
       +}
       +
       +static int
       +srbready(void *v)
       +{
       +        Srb *s;
       +
       +        s = v;
       +        return s->error || (!s->nout && !s->len);
       +}
       +
       +static Frame*
       +getframe(Aoedev *d, int tag)
       +{
       +        Frame *f, *e;
       +
       +        f = d->frames;
       +        e = f + d->nframes;
       +        for(; f < e; f++)
       +                if(f->tag == tag)
       +                        return f;
       +        return nil;
       +}
       +
       +static Frame*
       +freeframe(Aoedev *d)
       +{
       +        if(d->nout < d->maxout)
       +                return getframe(d, Tfree);
       +        return nil;
       +}
       +
       +static void
       +work(Aoedev *d)
       +{
       +        Frame *f;
       +
       +        while(f = freeframe(d)) {
       +                if(d->inprocess == nil){
       +                        if(d->head == nil)
       +                                return;
       +                        d->inprocess = d->head;
       +                        d->head = d->head->next;
       +                        if(d->head == nil)
       +                                d->tail = nil;
       +                }
       +                atarw(d, f);
       +        }
       +}
       +
       +static void
       +strategy(Aoedev *d, Srb *srb)
       +{
       +        QLOCK(d);
       +        if(waserror()){
       +                QUNLOCK(d);
       +                nexterror();
       +        }
       +        srb->next = nil;
       +        if(d->tail)
       +                d->tail->next = srb;
       +        d->tail = srb;
       +        if(d->head == nil)
       +                d->head = srb;
       +        work(d);
       +        poperror();
       +        QUNLOCK(d);
       +
       +        while(waserror())
       +                ;
       +        SLEEP(srb, srbready, srb);
       +        poperror();
       +}
       +
       +#define iskaddr(a)        (!up || (uintptr)(a) > up->pmmu.uzero+USTKTOP)
       +
       +static long
       +rw(Aoedev *d, int write, uchar *db, long len, uvlong off)
       +{
       +        long n, nlen, copy;
       +        enum { Srbsz = 1<<19, };
       +        Srb *srb;
       +
       +        if((off|len) & (Aoesectsz-1))
       +                error("offset and length must be sector multiple.\n");
       +        if(off >= d->bsize)
       +                return 0;
       +        if(off + len > d->bsize)
       +                len = d->bsize - off;
       +        copy = 0;
       +        if(iskaddr(db)){
       +panic("iskaddr %p %p\n", db);
       +                srb = srbkalloc(db, len);
       +                copy = 1;
       +        }else
       +                srb = srballoc(Srbsz <= len? Srbsz: len);
       +        if(waserror()){
       +                srbfree(srb);
       +                nexterror();
       +        }
       +        srb->write = write;
       +        for(nlen = len; nlen; nlen -= n){
       +                if(!UP(d))
       +                        error(Eio);
       +                srb->sector = off / Aoesectsz;
       +                srb->dp = srb->data;
       +                n = nlen;
       +                if(n > Srbsz)
       +                        n = Srbsz;
       +                srb->len = n;
       +                if(write && !copy)
       +                        memmove(srb->data, db, n);
       +                strategy(d, srb);
       +                if(srb->error)
       +                        error(srb->error);
       +                if(!write && !copy)
       +                        memmove(db, srb->data, n);
       +                db += n;
       +                off += n;
       +        }
       +        poperror();
       +        srbfree(srb);
       +        return len;
       +}
       +
       +static long
       +readmem(ulong off, void *dst, long n, void *src, long size)
       +{
       +        if(off >= size)
       +                return 0;
       +        if(off + n > size)
       +                n = size - off;
       +        memmove(dst, (uchar*)src + off, n);
       +        return n;
       +}
       +
       +static char*
       +pflag(char *s, char *e, uchar f)
       +{
       +        uchar i;
       +
       +        for(i = 0; i < nelem(flagname); i++)
       +                if(f & 1 << i)
       +                        s = seprint(s, e, "%s ", flagname[i]);
       +        return seprint(s, e, "\n");
       +}
       +
       +static int
       +pstat(Aoedev *d, char *db, int len, int off)
       +{
       +        int i;
       +        char *state, *s, *p, *e;
       +
       +        s = p = malloc(1024);
       +        e = p + 1024;
       +
       +        state = "down";
       +        if(UP(d))
       +                state = "up";
       +
       +        p = seprint(p, e,
       +                "state: %s\n"        "nopen: %d\n"        "nout: %d\n"
       +                "nmaxout: %d\n"        "nframes: %d\n"        "maxbcnt: %d [maxmtu %d]\n"
       +                "fw: %.4ux\n"
       +                "model: %s\n"        "serial: %s\n"        "firmware: %s\n",
       +                state,                d->nopen,        d->nout,
       +                d->maxout,         d->nframes,        d->maxbcnt, d->maxmtu,
       +                d->fwver,
       +                d->model,         d->serial,         d->firmware);
       +        p = seprint(p, e, "flag: ");
       +        p = pflag(p, e, d->flag);
       +
       +        if(p - s < len)
       +                len = p - s;
       +        i = readstr(off, db, len, s);
       +        free(s);
       +        return i;
       +}
       +
       +static long
       +unitread(Chan *c, void *db, long len, vlong off)
       +{
       +        Aoedev *d;
       +
       +        d = unit2dev(UNIT(c->qid));
       +        if(d->vers != c->qid.vers)
       +                error(Echange);
       +        switch(TYPE(c->qid)){
       +        default:
       +                error(Ebadarg);
       +        case Qctl:
       +                return pstat(d, db, len, off);
       +        case Qdata:
       +                return rw(d, Read, db, len, off);
       +        case Qconfig:
       +                if(!UP(d))
       +                        error(Enotup);
       +                return readmem(off, db, len, d->config, d->nconfig);
       +        case Qident:
       +                if(!UP(d))
       +                        error(Enotup);
       +                return readmem(off, db, len, d->ident, sizeof d->ident);
       +        }
       +}
       +
       +static int
       +devlinkread(Chan *c, void *db, int len, int off)
       +{
       +        int i;
       +        char *s, *p, *e;
       +        Aoedev *d;
       +        Devlink *l;
       +
       +        d = unit2dev(UNIT(c->qid));
       +        i = L(c->qid);
       +        if(i >= d->ndl)
       +                return 0;
       +        l = d->dl + i;
       +
       +        s = p = malloc(1024);
       +        e = s + 1024;
       +
       +        p = seprint(p, e, "addr: ");
       +        for(i = 0; i < l->nea; i++)
       +                p = seprint(p, e, "%E ", l->eatab[i]);
       +        p = seprint(p, e, "\n");
       +        p = seprint(p, e, "npkt: %uld\n", l->npkt);
       +        p = seprint(p, e, "resent: %uld\n", l->resent);
       +        p = seprint(p, e, "flag: "); p = pflag(p, e, l->flag);
       +        p = seprint(p, e, "rttavg: %uld\n", Tk2ms(l->rttavg));
       +        p = seprint(p, e, "mintimer: %uld\n", Tk2ms(l->mintimer));
       +
       +        p = seprint(p, e, "nl path: %s\n", l->nl->path);
       +        p = seprint(p, e, "nl ea: %E\n", l->nl->ea);
       +        p = seprint(p, e, "nl flag: "); p = pflag(p, e, l->flag);
       +        p = seprint(p, e, "nl lostjumbo: %d\n", l->nl->lostjumbo);
       +        p = seprint(p, e, "nl datamtu: %d\n", l->nl->datamtu);
       +
       +        if(p - s < len)
       +                len = p - s;
       +        i = readstr(off, db, len, s);
       +        free(s);
       +        return i;
       +}
       +
       +static long
       +ttopctlread(Chan *d0, void *db, int len, int off)
       +{
       +        int i;
       +        char *s, *p, *e;
       +        Netlink *n;
       +
       +        s = p = malloc(1024);
       +        e = s + 1024;
       +
       +        p = seprint(p, e, "debug: %d\n", debug);
       +        p = seprint(p, e, "autodiscover: %d\n", autodiscover);
       +        p = seprint(p, e, "rediscover: %d\n", rediscover);
       +
       +        for(i = 0; i < Nnetlink; i++){
       +                n = netlinks.nl+i;
       +                if(n->cc == 0)
       +                        continue;
       +                p = seprint(p, e, "if%d path: %s\n", i, n->path);
       +                p = seprint(p, e, "if%d ea: %E\n", i, n->ea);
       +                p = seprint(p, e, "if%d flag: ", i); p = pflag(p, e, n->flag);
       +                p = seprint(p, e, "if%d lostjumbo: %d\n", i, n->lostjumbo);
       +                p = seprint(p, e, "if%d datamtu: %d\n", i, n->datamtu);
       +        }
       +
       +        if(p - s < len)
       +                len = p - s;
       +        i = readstr(off, db, len, s);
       +        free(s);
       +        return i;
       +}
       +
       +static long
       +aoeread(Chan *c, void *db, long n, vlong off)
       +{
       +        switch(TYPE(c->qid)){
       +        default:
       +                error(Eperm);
       +        case Qzero:
       +        case Qtopdir:
       +        case Qunitdir:
       +        case Qdevlinkdir:
       +                return devdirread(c, db, n, 0, 0, aoegen);
       +        case Qtopctl:
       +                return topctlread(c, db, n, off);
       +        case Qtoplog:
       +                return eventlogread(db, n);
       +        case Qctl:
       +        case Qdata:
       +        case Qconfig:
       +        case Qident:
       +                return unitread(c, db, n, off);
       +        case Qdevlink:
       +                return devlinkread(c, db, n, off);
       +        }
       +}
       +
       +static long
       +configwrite(Aoedev *d, void *db, long len)
       +{
       +        char *s;
       +        Aoeqc *ch;
       +        Frame *f;
       +        Srb *srb;
       +
       +        if(!UP(d))
       +                error(Enotup);
       +        if(len > sizeof d->config)
       +                error(Etoobig);
       +        srb = srballoc(len);
       +        s = malloc(len);
       +        memmove(s, db, len);
       +        if(waserror()){
       +                srbfree(srb);
       +                free(s);
       +                nexterror();
       +        }
       +        for (;;) {
       +                QLOCK(d);
       +                if(waserror()){
       +                        QUNLOCK(d);
       +                        nexterror();
       +                }
       +                f = freeframe(d);
       +                if(f != nil)
       +                        break;
       +                poperror();
       +                QUNLOCK(d);
       +                if(waserror())
       +                        nexterror();
       +                tsleep(&up->sleep, return0, 0, 100);
       +                poperror();
       +        }
       +        f->nhdr = Szaoeqc;
       +        memset(f->hdr, 0, f->nhdr);
       +        ch = (Aoeqc*)f->hdr;
       +        if(hset(d, f, (Aoehdr*)ch, ACconfig) == -1)
       +                return 0;
       +        f->srb = srb;
       +        f->dp = s;
       +        ch->verccmd = AQCfset;
       +        hnputs(ch->cslen, len);
       +        d->nout++;
       +        srb->nout++;
       +        f->dl->npkt++;
       +        f->dlen = len;
       +        /*
       +         * these refer to qlock & waserror in the above for loop.
       +         * there's still the first waserror outstanding.
       +         */
       +        poperror();
       +        QUNLOCK(d);
       +
       +        devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
       +        SLEEP(srb, srbready, srb);
       +        if(srb->error)
       +                error(srb->error);
       +
       +        QLOCK(d);
       +        if(waserror()){
       +                QUNLOCK(d);
       +                nexterror();
       +        }
       +        memmove(d->config, s, len);
       +        d->nconfig = len;
       +        poperror();
       +        QUNLOCK(d);
       +
       +        poperror();                        /* pop first waserror */
       +
       +        srbfree(srb);
       +        memmove(db, s, len);
       +        free(s);
       +        return len;
       +}
       +
       +static int
       +getmtu(Chan *m)
       +{
       +        int n, mtu;
       +        char buf[36];
       +
       +        mtu = 1514;
       +        if(m == nil || waserror())
       +                return mtu;
       +        n = devtab[m->type]->read(m, buf, sizeof buf - 1, 0);
       +        poperror();
       +        if(n > 12){
       +                buf[n] = 0;
       +                mtu = strtoul(buf + 12, 0, 0);
       +        }
       +        return mtu;
       +}
       +
       +static int
       +devmaxdata(Aoedev *d)
       +{
       +        int i, m, mtu;
       +        Devlink *l;
       +        Netlink *n;
       +
       +        mtu = 100000;
       +        for(i = 0; i < d->ndl; i++){
       +                l = d->dl + i;
       +                n = l->nl;
       +                if((l->flag & Dup) == 0 || (n->flag & Dup) == 0)
       +                        continue;
       +                m = getmtu(n->mtu);
       +                if(m > l->datamtu)
       +                        m = l->datamtu;
       +                if(m < mtu)
       +                        mtu = m;
       +        }
       +        if(mtu == 100000)
       +                mtu = 1514;
       +        mtu -= Szaoeata;
       +        mtu -= mtu % Aoesectsz;
       +        return mtu;
       +}
       +
       +static int
       +ttoggle(char *s, int init)
       +{
       +        if(s == nil)
       +                return init ^ 1;
       +        return strcmp(s, "on") == 0;
       +}
       +
       +static void ataident(Aoedev*);
       +
       +static long
       +unitctlwrite(Aoedev *d, void *db, long n)
       +{
       +        uint maxbcnt, m;
       +        uvlong bsize;
       +        enum {
       +                Failio,
       +                Ident,
       +                Jumbo,
       +                Maxbno,
       +                Mtu,
       +                Setsize,
       +        };
       +        Cmdbuf *cb;
       +        Cmdtab *ct;
       +        static Cmdtab cmds[] = {
       +                {Failio,         "failio",         1 },
       +                {Ident,         "identify",         1 },
       +                {Jumbo,         "jumbo",         0 },
       +                {Maxbno,        "maxbno",        0 },
       +                {Mtu,                "mtu",                0 },
       +                {Setsize,         "setsize",         0 },
       +        };
       +
       +        cb = parsecmd(db, n);
       +        QLOCK(d);
       +        if(waserror()){
       +                QUNLOCK(d);
       +                free(cb);
       +                nexterror();
       +        }
       +        ct = lookupcmd(cb, cmds, nelem(cmds));
       +        switch(ct->index){
       +        case Failio:
       +                downdev(d, "i/o failure");
       +                break;
       +        case Ident:
       +                ataident(d);
       +                break;
       +        case Jumbo:
       +                m = 0;
       +                if(d->flag & Djumbo)
       +                        m = 1;
       +                toggle(cb->f[1], m);
       +                if(m)
       +                        d->flag |= Djumbo;
       +                else
       +                        d->flag &= ~Djumbo;
       +                break;
       +        case Maxbno:
       +        case Mtu:
       +                maxbcnt = devmaxdata(d);
       +                if(cb->nf > 2)
       +                        error(Ecmdargs);
       +                if(cb->nf == 2){
       +                        m = strtoul(cb->f[1], 0, 0);
       +                        if(ct->index == Maxbno)
       +                                m *= Aoesectsz;
       +                        else{
       +                                m -= Szaoeata;
       +                                m &= ~(Aoesectsz-1);
       +                        }
       +                        if(m == 0 || m > maxbcnt)
       +                                cmderror(cb, "invalid mtu");
       +                        maxbcnt = m;
       +                        d->maxmtu = m;
       +                } else
       +                        d->maxmtu = Maxmtu;
       +                d->maxbcnt = maxbcnt;
       +                break;
       +        case Setsize:
       +                bsize = d->realbsize;
       +                if(cb->nf > 2)
       +                        error(Ecmdargs);
       +                if(cb->nf == 2){
       +                        bsize = strtoull(cb->f[1], 0, 0);
       +                        if(bsize % Aoesectsz)
       +                                cmderror(cb, "disk size must be sector aligned");
       +                }
       +                d->bsize = bsize;
       +                break;
       +        default:
       +                cmderror(cb, "unknown aoe control message");
       +        }
       +        poperror();
       +        QUNLOCK(d);
       +        free(cb);
       +        return n;
       +}
       +
       +static long
       +unitwrite(Chan *c, void *db, long n, vlong off)
       +{
       +        long rv;
       +        char *buf;
       +        Aoedev *d;
       +
       +        d = unit2dev(UNIT(c->qid));
       +        switch(TYPE(c->qid)){
       +        default:
       +                error(Ebadarg);
       +        case Qctl:
       +                return unitctlwrite(d, db, n);
       +        case Qident:
       +                error(Eperm);
       +        case Qdata:
       +                return rw(d, Write, db, n, off);
       +        case Qconfig:
       +                if(off + n > sizeof d->config)
       +                        error(Etoobig);
       +                buf = malloc(sizeof d->config);
       +                if(waserror()){
       +                        free(buf);
       +                        nexterror();
       +                }
       +                memmove(buf, d->config, d->nconfig);
       +                memmove(buf + off, db, n);
       +                rv = configwrite(d, buf, n + off);
       +                poperror();
       +                free(buf);
       +                return rv;
       +        }
       +}
       +
       +static Netlink*
       +addnet(char *path, Chan *cc, Chan *dc, Chan *mtu, uchar *ea)
       +{
       +        Netlink *nl, *e;
       +
       +        LOCK(&netlinks);
       +        if(waserror()){
       +                UNLOCK(&netlinks);
       +                nexterror();
       +        }
       +        nl = netlinks.nl;
       +        e = nl + nelem(netlinks.nl);
       +        for(; nl < e && nl->cc; nl++)
       +                continue;
       +        if(nl == e)
       +                error("out of netlink structures");
       +        nl->cc = cc;
       +        nl->dc = dc;
       +        nl->mtu = mtu;
       +        strncpy(nl->path, path, sizeof nl->path);
       +        memmove(nl->ea, ea, sizeof nl->ea);
       +        poperror();
       +        nl->flag |= Dup;
       +        UNLOCK(&netlinks);
       +        return nl;
       +}
       +
       +static int
       +newunit(void)
       +{
       +        int x;
       +
       +        LOCK(&units);
       +        if(units.ref == Maxunits)
       +                x = -1;
       +        else
       +                x = units.ref++;
       +        UNLOCK(&units);
       +        return x;
       +}
       +
       +static int
       +dropunit(void)
       +{
       +        int x;
       +
       +        LOCK(&units);
       +        x = --units.ref;
       +        UNLOCK(&units);
       +        return x;
       +}
       +
       +/*
       + * always allocate max frames.  maxout may change.
       + */
       +static Aoedev*
       +newdev(long major, long minor, int n)
       +{
       +        Aoedev *d;
       +        Frame *f, *e;
       +
       +        d = malloc(sizeof *d);
       +        f = malloc(sizeof *f*Maxframes);
       +        if(!d || !f) {
       +                free(d);
       +                free(f);
       +                error("aoe device allocation failure");
       +        }
       +        d->nframes = n;
       +        d->frames = f;
       +        for (e = f + Maxframes; f < e; f++)
       +                f->tag = Tfree;
       +        d->maxout = n;
       +        d->major = major;
       +        d->minor = minor;
       +        d->maxbcnt = Dbcnt;
       +        d->flag = Djumbo;
       +        d->maxmtu = Maxmtu;
       +        d->unit = newunit();                /* bzzt.  inaccurate if units removed */
       +        if(d->unit == -1){
       +                free(d);
       +                free(d->frames);
       +                error("too many units");
       +        }
       +        d->dl = d->dltab;
       +        return d;
       +}
       +
       +static Aoedev*
       +mm2dev(int major, int minor)
       +{
       +        Aoedev *d;
       +
       +        RLOCK(&devs);
       +        for(d = devs.d; d; d = d->next)
       +                if(d->major == major && d->minor == minor){
       +                        RUNLOCK(&devs);
       +                        return d;
       +                }
       +        RUNLOCK(&devs);
       +        eventlog("mm2dev: %d.%d not found\n", major, minor);
       +        return nil;
       +}
       +
       +/* Find the device in our list.  If not known, add it */
       +static Aoedev*
       +getdev(long major, long minor, int n)
       +{
       +        Aoedev *d;
       +
       +        if(major == 0xffff || minor == 0xff)
       +                return 0;
       +        WLOCK(&devs);
       +        if(waserror()){
       +                WUNLOCK(&devs);
       +                nexterror();
       +        }
       +        for(d = devs.d; d; d = d->next)
       +                if(d->major == major && d->minor == minor)
       +                        break;
       +        if(d == nil) {
       +                d = newdev(major, minor, n);
       +                d->next = devs.d;
       +                devs.d = d;
       +        }
       +        poperror();
       +        WUNLOCK(&devs);
       +        return d;
       +}
       +
       +static ushort
       +gbit16(void *a)
       +{
       +        uchar *i;
       +
       +        i = a;
       +        return i[1] << 8 | i[0];
       +}
       +
       +static ulong
       +gbit32(void *a)
       +{
       +        ulong j;
       +        uchar *i;
       +
       +        i = a;
       +        j  = i[3] << 24;
       +        j |= i[2] << 16;
       +        j |= i[1] << 8;
       +        j |= i[0];
       +        return j;
       +}
       +
       +static uvlong
       +gbit64(void *a)
       +{
       +        uchar *i;
       +
       +        i = a;
       +        return (uvlong)gbit32(i+4) << 32 | gbit32(a);
       +}
       +
       +static void
       +ataident(Aoedev *d)
       +{
       +        Aoeata *a;
       +        Block *b;
       +        Frame *f;
       +
       +        f = freeframe(d);
       +        if(f == nil)
       +                return;
       +        f->nhdr = Szaoeata;
       +        memset(f->hdr, 0, f->nhdr);
       +        a = (Aoeata*)f->hdr;
       +        if(hset(d, f, (Aoehdr*)a, ACata) == -1)
       +                return;
       +        f->srb = srbkalloc(0, 0);
       +        a->cmdstat = Cid;        /* ata 6, page 110 */
       +        a->scnt = 1;
       +        a->lba[3] = 0xa0;
       +        d->nout++;
       +        f->dl->npkt++;
       +        f->bcnt = 512;
       +        f->dlen = 0;
       +        b = allocfb(f);
       +        devtab[f->nl->dc->type]->bwrite(f->nl->dc, b, 0);
       +}
       +
       +static int
       +newdlea(Devlink *l, uchar *ea)
       +{
       +        int i;
       +        uchar *t;
       +
       +        for(i = 0; i < Nea; i++){
       +                t = l->eatab[i];
       +                if(i == l->nea){
       +                        memmove(t, ea, Eaddrlen);
       +                        return l->nea++;
       +                }
       +                if(memcmp(t, ea, Eaddrlen) == 0)
       +                        return i;
       +        }
       +        return -1;
       +}
       +
       +static Devlink*
       +newdevlink(Aoedev *d, Netlink *n, Aoeqc *c)
       +{
       +        int i;
       +        Devlink *l;
       +
       +        for(i = 0; i < Ndevlink; i++){
       +                l = d->dl + i;
       +                if(i == d->ndl){
       +                        d->ndl++;
       +                        newdlea(l, c->src);
       +                        l->datamtu = c->scnt*Aoesectsz;
       +                        l->nl = n;
       +                        l->flag |= Dup;
       +                        l->mintimer = Rtmin;
       +                        l->rttavg = Rtmax;
       +                        return l;
       +                }
       +                if(l->nl == n){
       +                        newdlea(l, c->src);
       +                        l->datamtu = c->scnt*Aoesectsz;
       +                        l->flag |= Dup;
       +                        return l;
       +                }
       +        }
       +        eventlog("%æ: out of links: %s:%E to %E\n", d, n->path, n->ea, c->src);
       +        return 0;
       +}
       +
       +static void
       +errrsp(Block *b, char *s)
       +{
       +        int n;
       +        Aoedev *d;
       +        Aoehdr *h;
       +        Frame *f;
       +
       +        h = (Aoehdr*)b->rp;
       +        n = nhgetl(h->tag);
       +        if(n == Tmgmt || n == Tfree)
       +                return;
       +        d = mm2dev(nhgets(h->major), h->minor);
       +        if(d == 0)
       +                return;
       +        if(f = getframe(d, n))
       +                frameerror(d, f, s);
       +}
       +
       +static void
       +qcfgrsp(Block *b, Netlink *nl)
       +{
       +        int major, cmd, cslen, blen;
       +        unsigned n;
       +        Aoedev *d;
       +        Aoeqc *ch;
       +        Devlink *l;
       +        Frame *f;
       +
       +        ch = (Aoeqc*)b->rp;
       +        major = nhgets(ch->major);
       +        n = nhgetl(ch->tag);
       +        if(n != Tmgmt){
       +                d = mm2dev(major, ch->minor);
       +                if(d == nil)
       +                        return;
       +                QLOCK(d);
       +                f = getframe(d, n);
       +                if(f == nil){
       +                        QUNLOCK(d);
       +                        eventlog("%æ: unknown response tag %ux\n", d, n);
       +                        return;
       +                }
       +                cslen = nhgets(ch->cslen);
       +                blen = BLEN(b) - Szaoeqc;
       +                if(cslen < blen)
       +                        eventlog("%æ: cfgrsp: tag %.8ux oversized %d %d\n",
       +                                d, n, cslen, blen);
       +                if(cslen > blen){
       +                        eventlog("%æ: cfgrsp: tag %.8ux runt %d %d\n",
       +                                d, n, cslen, blen);
       +                        cslen = blen;
       +                }
       +                memmove(f->dp, ch + 1, cslen);
       +                f->srb->nout--;
       +                WAKEUP(f->srb);
       +                d->nout--;
       +                f->srb = nil;
       +                f->tag = Tfree;
       +                QUNLOCK(d);
       +                return;
       +        }
       +
       +        cmd = ch->verccmd & 0xf;
       +        if(cmd != 0){
       +                eventlog("aoe%d.%d: cfgrsp: bad command %d\n", major, ch->minor, cmd);
       +                return;
       +        }
       +        n = nhgets(ch->bufcnt);
       +        if(n > Maxframes)
       +                n = Maxframes;
       +
       +        if(waserror()){
       +                eventlog("getdev: %d.%d ignored: %s\n", major, ch->minor, up->errstr);
       +                return;
       +        }
       +        d = getdev(major, ch->minor, n);
       +        poperror();
       +        if(d == 0)
       +                return;
       +
       +        QLOCK(d);
       +        *up->errstr = 0;
       +        if(waserror()){
       +                QUNLOCK(d);
       +                eventlog("%æ: %s\n", d, up->errstr);
       +                nexterror();
       +        }
       +
       +        l = newdevlink(d, nl, ch);                /* add this interface. */
       +
       +        d->fwver = nhgets(ch->fwver);
       +        n = nhgets(ch->cslen);
       +        if(n > sizeof d->config)
       +                n = sizeof d->config;
       +        d->nconfig = n;
       +        memmove(d->config, ch + 1, n);
       +
       +        /* manually set mtu may be reset lower if conditions warrant */
       +        if(l){
       +                n = devmaxdata(d);
       +                if(!(d->flag & Djumbo))
       +                        n = Dbcnt;
       +                if(n > d->maxmtu)
       +                        n = d->maxmtu;
       +                if(n != d->maxbcnt){
       +                        eventlog("%æ: setting %d byte mtu on %s:%E\n",
       +                                d, n, nl->path, nl->ea);
       +                        d->maxbcnt = n;
       +                }
       +        }
       +        if(d->nopen == 0)
       +                ataident(d);
       +        poperror();
       +        QUNLOCK(d);
       +}
       +
       +static void
       +idmove(char *p, ushort *a, unsigned n)
       +{
       +        int i;
       +        char *op, *e;
       +
       +        op = p;
       +        for(i = 0; i < n / 2; i++){
       +                *p++ = a[i] >> 8;
       +                *p++ = a[i];
       +        }
       +        *p = 0;
       +        while(p > op && *--p == ' ')
       +                *p = 0;
       +        e = p;
       +        p = op;
       +        while(*p == ' ')
       +                p++;
       +        memmove(op, p, n - (e - p));
       +}
       +
       +static vlong
       +aoeidentify(Aoedev *d, ushort *id)
       +{
       +        int i;
       +        vlong s;
       +
       +        d->flag &= ~(Dllba|Dpower|Dsmart|Dnop|Dup);
       +
       +        i = gbit16(id+83) | gbit16(id+86);
       +        if(i & (1<<10)){
       +                d->flag |= Dllba;
       +                s = gbit64(id+100);
       +        }else
       +                s = gbit32(id+60);
       +
       +        i = gbit16(id+83);
       +        if((i>>14) == 1) {
       +                if(i & (1<<3))
       +                        d->flag  |= Dpower;
       +                i = gbit16(id+82);
       +                if(i & 1)
       +                        d->flag  |= Dsmart;
       +                if(i & (1<<14))
       +                        d->flag  |= Dnop;
       +        }
       +//        eventlog("%æ up\n", d);
       +        d->flag |= Dup;
       +        memmove(d->ident, id, sizeof d->ident);
       +        return s;
       +}
       +
       +static void
       +newvers(Aoedev *d)
       +{
       +        LOCK(&drivevers);
       +        d->vers = drivevers.ref++;
       +        UNLOCK(&drivevers);
       +}
       +
       +static int
       +identify(Aoedev *d, ushort *id)
       +{
       +        vlong osectors, s;
       +        uchar oserial[21];
       +
       +        s = aoeidentify(d, id);
       +        if(s == -1)
       +                return -1;
       +        osectors = d->realbsize;
       +        memmove(oserial, d->serial, sizeof d->serial);
       +
       +        idmove(d->serial, id+10, 20);
       +        idmove(d->firmware, id+23, 8);
       +        idmove(d->model, id+27, 40);
       +
       +        s *= Aoesectsz;
       +        if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){
       +                d->bsize = s;
       +                d->realbsize = s;
       +//                d->mediachange = 1;
       +                newvers(d);
       +        }
       +        return 0;
       +}
       +
       +static void
       +atarsp(Block *b)
       +{
       +        unsigned n;
       +        short major;
       +        Aoeata *ahin, *ahout;
       +        Aoedev *d;
       +        Frame *f;
       +        Srb *srb;
       +
       +        ahin = (Aoeata*)b->rp;
       +        major = nhgets(ahin->major);
       +        d = mm2dev(major, ahin->minor);
       +        if(d == nil)
       +                return;
       +        QLOCK(d);
       +        if(waserror()){
       +                QUNLOCK(d);
       +                nexterror();
       +        }
       +        n = nhgetl(ahin->tag);
       +        f = getframe(d, n);
       +        if(f == nil){
       +                dprint("%æ: unexpected response; tag %ux\n", d, n);
       +                goto bail;
       +        }
       +        rtupdate(f->dl, tsince(f->tag));
       +        ahout = (Aoeata*)f->hdr;
       +        srb = f->srb;
       +
       +        if(ahin->cmdstat & 0xa9){
       +                eventlog("%æ: ata error cmd %.2ux stat %.2ux\n",
       +                        d, ahout->cmdstat, ahin->cmdstat);
       +                if(srb)
       +                        srb->error = Eio;
       +        } else {
       +                n = ahout->scnt * Aoesectsz;
       +                switch(ahout->cmdstat){
       +                case Crd:
       +                case Crdext:
       +                        if(BLEN(b) - Szaoeata < n){
       +                                eventlog("%æ: runt read blen %ld expect %d\n",
       +                                        d, BLEN(b), n);
       +                                goto bail;
       +                        }
       +                        memmove(f->dp, b->rp + Szaoeata, n);
       +                case Cwr:
       +                case Cwrext:
       +                        if(n > Dbcnt)
       +                                f->nl->lostjumbo = 0;
       +                        if(f->bcnt -= n){
       +                                f->lba += n / Aoesectsz;
       +                                f->dp = (uchar*)f->dp + n;
       +                                resend(d, f);
       +                                goto bail;
       +                        }
       +                        break;
       +                case Cid:
       +                        if(BLEN(b) - Szaoeata < 512){
       +                                eventlog("%æ: runt identify blen %ld expect %d\n",
       +                                        d, BLEN(b), n);
       +                                goto bail;
       +                        }
       +                        identify(d, (ushort*)(b->rp + Szaoeata));
       +                        break;
       +                default:
       +                        eventlog("%æ: unknown ata command %.2ux \n",
       +                                d, ahout->cmdstat);
       +                }
       +        }
       +
       +        if(srb && --srb->nout == 0 && srb->len == 0)
       +                WAKEUP(srb);
       +        f->srb = nil;
       +        f->tag = Tfree;
       +        d->nout--;
       +
       +        work(d);
       +bail:
       +        poperror();
       +        QUNLOCK(d);
       +}
       +
       +static void
       +netrdaoeproc(void *v)
       +{
       +        int idx;
       +        char name[Maxpath+1], *s;
       +        Aoehdr *h;
       +        Block *b;
       +        Netlink *nl;
       +
       +        nl = (Netlink*)v;
       +        idx = nl - netlinks.nl;
       +        netlinks.reader[idx] = 1;
       +        kstrcpy(name, nl->path, Maxpath);
       +
       +        if(waserror()){
       +                eventlog("netrdaoe@%s: exiting: %s\n", name, up->errstr);
       +                netlinks.reader[idx] = 0;
       +                wakeup(netlinks.rendez + idx);
       +                pexit(up->errstr, 1);
       +        }
       +        if(autodiscover)
       +                discover(0xffff, 0xff);
       +        for (;;) {
       +                if(!(nl->flag & Dup))
       +                        error("netlink is down");
       +                if(nl->dc == nil)
       +                        panic("netrdaoe: nl->dc == nil");
       +                b = devtab[nl->dc->type]->bread(nl->dc, 1<<16, 0);
       +                if(b == nil)
       +                        error("network read");
       +                h = (Aoehdr*)b->rp;
       +                if(h->verflag & AFrsp)
       +                        if(s = aoeerror(h)){
       +                                eventlog("%s: %s\n", nl->path, s);
       +                                errrsp(b, s);
       +                        }else if(h->cmd == ACata)
       +                                atarsp(b);
       +                        else if(h->cmd == ACconfig)
       +                                qcfgrsp(b, nl);
       +                        else if((h->cmd & 0xf0) == 0){
       +                                eventlog("%s: unknown cmd %d\n",
       +                                        nl->path, h->cmd);
       +                                errrsp(b, "unknown command");
       +                        }
       +                freeb(b);
       +        }
       +}
       +
       +static void
       +getaddr(char *path, uchar *ea)
       +{
       +        int n;
       +        char buf[2*Eaddrlen+1];
       +        Chan *c;
       +
       +        uprint("%s/addr", path);
       +        c = namec(up->genbuf, Aopen, OREAD, 0);
       +        if(waserror()) {
       +                cclose(c);
       +                nexterror();
       +        }
       +        if(c == nil)
       +                panic("æ: getaddr: c == nil");
       +        n = devtab[c->type]->read(c, buf, sizeof buf-1, 0);
       +        poperror();
       +        cclose(c);
       +        buf[n] = 0;
       +        if(parseether(ea, buf) < 0)
       +                error("parseether failure");
       +}
       +
       +static void
       +netbind(char *path)
       +{
       +        char addr[Maxpath];
       +        uchar ea[2*Eaddrlen+1];
       +        Chan *dc, *cc, *mtu;
       +        Netlink *nl;
       +
       +        snprint(addr, sizeof addr, "%s!0x%x", path, Aoetype);
       +        dc = chandial(addr, nil, nil, &cc);
       +        snprint(addr, sizeof addr, "%s/mtu", path);
       +        if(waserror())
       +                mtu = nil;
       +        else {
       +                mtu = namec(addr, Aopen, OREAD, 0);
       +                poperror();
       +        }
       +
       +        if(waserror()){
       +                cclose(dc);
       +                cclose(cc);
       +                if(mtu)
       +                        cclose(mtu);
       +                nexterror();
       +        }
       +        if(dc == nil  || cc == nil)
       +                error(Enonexist);
       +        getaddr(path, ea);
       +        nl = addnet(path, cc, dc, mtu, ea);
       +        snprint(addr, sizeof addr, "netrdaoe@%s", path);
       +        kproc(addr, netrdaoeproc, nl);
       +        poperror();
       +}
       +
       +static int
       +unbound(void *v)
       +{
       +        return *(int*)v != 0;
       +}
       +
       +static void
       +netunbind(char *path)
       +{
       +        int i, idx;
       +        Aoedev *d, *p, *next;
       +        Chan *dc, *cc;
       +        Devlink *l;
       +        Frame *f;
       +        Netlink *n, *e;
       +
       +        n = netlinks.nl;
       +        e = n + nelem(netlinks.nl);
       +
       +        LOCK(&netlinks);
       +        for(; n < e; n++)
       +                if(n->dc && strcmp(n->path, path) == 0)
       +                        break;
       +        UNLOCK(&netlinks);
       +        if(n == e)
       +                error("device not bound");
       +
       +        /*
       +         * hunt down devices using this interface; disable
       +         * this also terminates the reader.
       +         */
       +        idx = n - netlinks.nl;
       +        WLOCK(&devs);
       +        for(d = devs.d; d; d = d->next){
       +                QLOCK(d);
       +                for(i = 0; i < d->ndl; i++){
       +                        l = d->dl + i;
       +                        if(l->nl == n)
       +                                l->flag &= ~Dup;
       +                }
       +                QUNLOCK(d);
       +        }
       +        n->flag &= ~Dup;
       +        WUNLOCK(&devs);
       +
       +        /* confirm reader is down. */
       +        while(waserror())
       +                ;
       +        sleep(netlinks.rendez + idx, unbound, netlinks.reader + idx);
       +        poperror();
       +
       +        /* reschedule packets. */
       +        WLOCK(&devs);
       +        for(d = devs.d; d; d = d->next){
       +                QLOCK(d);
       +                for(i = 0; i < d->nframes; i++){
       +                        f = d->frames + i;
       +                        if(f->tag != Tfree && f->nl == n)
       +                                resend(d, f);
       +                }
       +                QUNLOCK(d);
       +        }
       +        WUNLOCK(&devs);
       +
       +        /* squeeze devlink pool.  (we assert nobody is using them now) */
       +        WLOCK(&devs);
       +        for(d = devs.d; d; d = d->next){
       +                QLOCK(d);
       +                for(i = 0; i < d->ndl; i++){
       +                        l = d->dl + i;
       +                        if(l->nl == n)
       +                                memmove(l, l + 1, sizeof *l * (--d->ndl - i));
       +                }
       +                QUNLOCK(d);
       +        }
       +        WUNLOCK(&devs);
       +
       +        /* close device link. */
       +        LOCK(&netlinks);
       +        dc = n->dc;
       +        cc = n->cc;
       +        if(n->mtu)
       +                cclose(n->mtu);
       +        memset(n, 0, sizeof *n);
       +        UNLOCK(&netlinks);
       +
       +        cclose(dc);
       +        cclose(cc);
       +
       +        /* squeeze orphan devices */
       +        WLOCK(&devs);
       +        for(p = d = devs.d; d; d = next){
       +                next = d->next;
       +                if(d->ndl > 0){
       +                        p = d;
       +                        continue;
       +                }
       +                QLOCK(d);
       +                downdev(d, "orphan");
       +                QUNLOCK(d);
       +                if(p != devs.d)
       +                        p->next = next;
       +                else{
       +                        devs.d = next;
       +                        p = devs.d;
       +                }
       +                free(d->frames);
       +                free(d);
       +                dropunit();
       +        }
       +        WUNLOCK(&devs);
       +}
       +
       +static void
       +strtoss(char *f, ushort *shelf, ushort *slot)
       +{
       +        ulong sh;
       +        char *s;
       +
       +        *shelf = 0xffff;
       +        *slot = 0xff;
       +        if(!f)
       +                return;
       +        *shelf = sh = strtol(f, &s, 0);
       +        if(s == f || sh > 0xffff)
       +                error("bad shelf");
       +        f = s;
       +        if(*f++ == '.'){
       +                *slot = strtol(f, &s, 0);
       +                if(s == f || *slot > 0xff)
       +                        error("bad shelf");
       +        }else
       +                *slot = 0xff;
       +}
       +
       +static void
       +discoverstr(char *f)
       +{
       +        ushort shelf, slot;
       +
       +        strtoss(f, &shelf, &slot);
       +        discover(shelf, slot);
       +}
       +
       +static void
       +removedev(Aoedev *d)
       +{
       +        int i;
       +        Aoedev *p;
       +
       +        WLOCK(&devs);
       +        p = 0;
       +        if(d != devs.d)
       +        for(p = devs.d; p; p = p->next)
       +                if(p->next == d)
       +                        break;
       +        QLOCK(d);
       +        d->flag &= ~Dup;
       +        newvers(d);
       +        d->ndl = 0;
       +        QUNLOCK(d);
       +        for(i = 0; i < d->nframes; i++)
       +                frameerror(d, d->frames+i, Enotup);
       +
       +        if(p)
       +                p->next = d->next;
       +        else
       +                devs.d = d->next;
       +        free(d->frames);
       +        free(d);
       +        dropunit();
       +        WUNLOCK(&devs);
       +}
       +
       +
       +static void
       +aoeremove(Chan *c)
       +{
       +        switch(TYPE(c->qid)){
       +        default:
       +        case Qzero:
       +        case Qtopdir:
       +        case Qtoplog:
       +        case Qtopctl:
       +        case Qctl:
       +        case Qdata:
       +        case Qconfig:
       +        case Qident:
       +                error(Eperm);
       +        case Qunitdir:
       +                removedev(unit2dev(UNIT(c->qid)));
       +                break;
       +        }
       +}
       +
       +static void
       +removestr(char *f)
       +{
       +        ushort shelf, slot;
       +        Aoedev *d;
       +
       +        strtoss(f, &shelf, &slot);
       +        WLOCK(&devs);
       +        for(d = devs.d; d; d = d->next)
       +                if(shelf == d->major && slot == d->minor){
       +                        WUNLOCK(&devs);        /* BOTCH */
       +                        removedev(d);
       +                        return;
       +                }
       +        WUNLOCK(&devs);
       +        error("device not bound");
       +}
       +
       +static long
       +ttopctlwrite(void *db, long n)
       +{
       +        enum {
       +                Autodiscover,
       +                Bind,
       +                Debug,
       +                Discover,
       +                Closewait,
       +                Rediscover,
       +                Remove,
       +                Unbind,
       +        };
       +        char *f;
       +        Cmdbuf *cb;
       +        Cmdtab *ct;
       +        static Cmdtab cmds[] = {
       +                { Autodiscover,        "autodiscover",        0        },
       +                { Bind,         "bind",         2        },
       +                { Debug,         "debug",         0        },
       +                { Discover,         "discover",         0        },
       +                { Rediscover,        "rediscover",        0        },
       +                { Remove,        "remove",        2        },
       +                { Unbind,        "unbind",        2        },
       +        };
       +
       +        cb = parsecmd(db, n);
       +        if(waserror()){
       +                free(cb);
       +                nexterror();
       +        }
       +        ct = lookupcmd(cb, cmds, nelem(cmds));
       +        f = cb->f[1];
       +        switch(ct->index){
       +        case Autodiscover:
       +                autodiscover = toggle(f, autodiscover);
       +                break;
       +        case Bind:
       +                netbind(f);
       +                break;
       +        case Debug:
       +                debug = toggle(f, debug);
       +                break;
       +        case Discover:
       +                discoverstr(f);
       +                break;
       +        case Rediscover:
       +                rediscover = toggle(f, rediscover);
       +                break;
       +        case Remove:
       +                removestr(f);        /* depricated */
       +                break;
       +        case Unbind:
       +                netunbind(f);
       +                break;
       +        default:
       +                cmderror(cb, "unknown aoe control message");
       +        }
       +        poperror();
       +        free(cb);
       +        return n;
       +}
       +
       +static long
       +aoewrite(Chan *c, void *db, long n, vlong off)
       +{
       +        switch(TYPE(c->qid)){
       +        default:
       +        case Qzero:
       +        case Qtopdir:
       +        case Qunitdir:
       +        case Qtoplog:
       +                error(Eperm);
       +        case Qtopctl:
       +                return topctlwrite(db, n);
       +        case Qctl:
       +        case Qdata:
       +        case Qconfig:
       +        case Qident:
       +                return unitwrite(c, db, n, off);
       +        }
       +}
       +
       +Dev aoedevtab = {
       +        L'æ',
       +        "aoe",
       +
       +        devreset,
       +        devinit,
       +        devshutdown,
       +        aoeattach,
       +        aoewalk,
       +        aoestat,
       +        aoeopen,
       +        devcreate,
       +        aoeclose,
       +        aoeread,
       +        devbread,
       +        aoewrite,
       +        devbwrite,
       +        aoeremove,
       +        devwstat,
       +        devpower,
       +        devconfig,
       +};
 (DIR) diff --git a/src/9vx/a/devether.c b/src/9vx/a/devether.c
       @@ -0,0 +1,534 @@
       +#include "u.h"
       +#include "lib.h"
       +#include "mem.h"
       +#include "dat.h"
       +#include "fns.h"
       +#include "io.h"
       +#include "ureg.h"
       +#include "error.h"
       +#include "netif.h"
       +
       +#include "etherif.h"
       +
       +#define MEMSIZE (256<<20)        // same as ../a/devether.c:13 (TODO: var)
       +
       +static Ether *etherxx[MaxEther];
       +
       +Chan*
       +etherattach(char* spec)
       +{
       +        ulong ctlrno;
       +        char *p;
       +        Chan *chan;
       +
       +        ctlrno = 0;
       +        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->dev = ctlrno;
       +        if(etherxx[ctlrno]->attach)
       +                etherxx[ctlrno]->attach(etherxx[ctlrno]);
       +        poperror();
       +        return chan;
       +}
       +
       +static Walkqid*
       +etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
       +{
       +        return netifwalk(&etherxx[chan->dev]->ni, chan, nchan, name, nname);
       +}
       +
       +static int
       +etherstat(Chan* chan, uchar* dp, int n)
       +{
       +        return netifstat(&etherxx[chan->dev]->ni, chan, dp, n);
       +}
       +
       +static Chan*
       +etheropen(Chan* chan, int omode)
       +{
       +        return netifopen(&etherxx[chan->dev]->ni, chan, omode);
       +}
       +
       +static void
       +ethercreate(Chan* ch, char* c, int i, ulong ul)
       +{
       +}
       +
       +static void
       +etherclose(Chan* chan)
       +{
       +        netifclose(&etherxx[chan->dev]->ni, chan);
       +}
       +
       +static long
       +etherread(Chan* chan, void* buf, long n, vlong off)
       +{
       +        Ether *ether;
       +        ulong offset = off;
       +
       +        ether = etherxx[chan->dev];
       +        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->ni, chan, buf, n, offset);
       +}
       +
       +static Block*
       +etherbread(Chan* chan, long n, ulong offset)
       +{
       +        return netifbread(&etherxx[chan->dev]->ni, chan, n, offset);
       +}
       +
       +static int
       +etherwstat(Chan* chan, uchar* dp, int n)
       +{
       +        return netifwstat(&etherxx[chan->dev]->ni, chan, dp, n);
       +}
       +
       +static void
       +etherrtrace(Netfile* f, Etherpkt* pkt, int len)
       +{
       +        int i, n;
       +        Block *bp;
       +
       +        if(qwindow(f->in) <= 0)
       +                return;
       +        if(len > 58)
       +                n = 58;
       +        else
       +                n = len;
       +        bp = iallocb(64);
       +        if(bp == nil)
       +                return;
       +        memmove(bp->wp, pkt->d, n);
       +        i = TK2MS(MACHP(0)->tscticks);
       +        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->in, bp);
       +}
       +
       +Block*
       +etheriq(Ether* ether, Block* bp, int fromwire)
       +{
       +        Etherpkt *pkt;
       +        ushort type;
       +        int len, multi, tome, fromme;
       +        Netfile **ep, *f, **fp, *fx;
       +        Block *xbp;
       +
       +        ether->ni.inpackets++;
       +
       +        pkt = (Etherpkt*)bp->rp;
       +        len = BLEN(bp);
       +        type = (pkt->type[0]<<8)|pkt->type[1];
       +        fx = 0;
       +        ep = &ether->ni.f[Ntypes];
       +
       +        multi = pkt->d[0] & 1;
       +        /* check for valid multicast addresses */
       +        if(multi && memcmp(pkt->d, ether->ni.bcast, sizeof(pkt->d)) != 0 && ether->ni.prom == 0){
       +                if(!activemulti(&ether->ni, 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->ni.f; fp < ep; fp++){
       +                if((f = *fp) != nil)
       +                if(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)
       +                                        fx = f;
       +                                else if((xbp = iallocb(len)) != nil){
       +                                        memmove(xbp->wp, pkt, len);
       +                                        xbp->wp += len;
       +                                        if(qpass(f->in, xbp) < 0)
       +                                                ether->ni.soverflows++;
       +                                }
       +                                else
       +                                        ether->ni.soverflows++;
       +                        }
       +                        else
       +                                etherrtrace(f, pkt, len);
       +                }
       +        }
       +
       +        if(fx){
       +                if(qpass(fx->in, bp) < 0)
       +                        ether->ni.soverflows++;
       +                return 0;
       +        }
       +        if(fromwire){
       +                freeb(bp);
       +                return 0;
       +        }
       +
       +        return bp;
       +}
       +
       +static int
       +etheroq(Ether* ether, Block* bp)
       +{
       +        int len, loopback, s;
       +        Etherpkt *pkt;
       +
       +        ether->ni.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 = (Etherpkt*)bp->rp;
       +        len = BLEN(bp);
       +        loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
       +        if(loopback || memcmp(pkt->d, ether->ni.bcast, sizeof(pkt->d)) == 0 || ether->ni.prom){
       +                s = splhi();
       +                etheriq(ether, bp, 0);
       +                splx(s);
       +        }
       +
       +        if(!loopback){
       +                qbwrite(ether->oq, bp);
       +                if(ether->transmit != nil)
       +                        ether->transmit(ether);
       +        } else
       +                freeb(bp);
       +
       +        return len;
       +}
       +
       +static long
       +etherwrite(Chan* chan, void* buf, long n, vlong v)
       +{
       +        Ether *ether;
       +        Block *bp;
       +        int nn, onoff;
       +        Cmdbuf *cb;
       +
       +        ether = etherxx[chan->dev];
       +        if(NETTYPE(chan->qid.path) != Ndataqid) {
       +                nn = netifwrite(&ether->ni, 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->oq, onoff);
       +                        free(cb);
       +                        return n;
       +                }
       +                free(cb);
       +                if(ether->ctl!=nil)
       +                        return ether->ctl(ether,buf,n);
       +
       +                error(Ebadctl);
       +        }
       +
       +        if(n > ether->maxmtu)
       +                error(Etoobig);
       +        if(n < ether->minmtu)
       +                error(Etoosmall);
       +
       +        bp = allocb(n);
       +        if(waserror()){
       +                freeb(bp);
       +                nexterror();
       +        }
       +        memmove(bp->rp, buf, n);
       +        memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
       +        poperror();
       +        bp->wp += n;
       +
       +        return etheroq(ether, bp);
       +}
       +
       +static long
       +etherbwrite(Chan* chan, Block* bp, ulong u)
       +{
       +        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 = etherxx[chan->dev];
       +
       +        if(n > ether->maxmtu){
       +                freeb(bp);
       +                error(Etoobig);
       +        }
       +        if(n < ether->minmtu){
       +                freeb(bp);
       +                error(Etoosmall);
       +        }
       +
       +        return etheroq(ether, bp);
       +}
       +
       +static struct {
       +        char*        type;
       +        int        (*reset)(Ether*);
       +} cards[MaxEther+1];
       +
       +void
       +addethercard(char* t, int (*r)(Ether*))
       +{
       +        static int ncard;
       +
       +        if(ncard == MaxEther)
       +                panic("too many ether cards");
       +        cards[ncard].type = t;
       +        cards[ncard].reset = r;
       +        ncard++;
       +}
       +
       +int
       +parseether(uchar *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 Ether*
       +etherprobe(int cardno, int ctlrno)
       +{
       +        int i, lg;
       +        ulong mb, bsz;
       +        Ether *ether;
       +        char buf[128], name[32];
       +
       +        ether = malloc(sizeof(Ether));
       +        memset(ether, 0, sizeof(Ether));
       +        ether->ctlrno = ctlrno;
       +        ether->tbdf = BUSUNKNOWN;
       +        ether->ni.mbps = 100;
       +        ether->minmtu = ETHERMINTU;
       +        ether->maxmtu = ETHERMAXTU;
       +
       +        if(cardno < 0){
       +                for(cardno = 0; cards[cardno].type; cardno++){
       +                        for(i = 0; i < ether->isac.nopt; i++){
       +                                if(strncmp(ether->isac.opt[i], "ea=", 3))
       +                                        continue;
       +                                if(parseether(ether->ea, &ether->isac.opt[i][3]))
       +                                        memset(ether->ea, 0, Eaddrlen);
       +                        }
       +                        break;
       +                }
       +        }
       +
       +        if(cardno >= MaxEther || cards[cardno].type == nil){
       +                free(ether);
       +                return nil;
       +        }
       +        if(cards[cardno].reset(ether) < 0){
       +                free(ether);
       +                return nil;
       +        }
       +
       +        /*
       +         * 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->isac.irq == 2)
       +                ether->isac.irq = 9;
       +        snprint(name, sizeof(name), "ether%d", ctlrno);
       +
       +        i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d",
       +                ctlrno, cards[cardno].type, ether->ni.mbps, ether->isac.port, ether->isac.irq);
       +        if(ether->isac.mem)
       +                i += sprint(buf+i, " addr 0x%luX", ether->isac.mem);
       +        if(ether->isac.size)
       +                i += sprint(buf+i, " size 0x%luX", ether->isac.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");
       +        print(buf);
       +
       +        /* compute log10(ether->ni.mbps) into lg */
       +        for(lg = 0, mb = ether->ni.mbps; mb >= 10; lg++)
       +                mb /= 10;
       +        if (lg > 0)
       +                lg--;
       +        if (lg > 14)                        /* 2^(14+17) = 2⁳ⁱ */
       +                lg = 14;
       +        /* allocate larger output queues for higher-speed interfaces */
       +        bsz = 1UL << (lg + 17);                /* 2ⁱ⁷ = 128K, bsz = 2ⁿ × 128K */
       +        while (bsz > MEMSIZE && bsz >= 128*1024)
       +                bsz /= 2;
       +
       +        netifinit(&ether->ni, name, Ntypes, bsz);
       +        while (ether->oq == nil && bsz >= 128*1024) {
       +                bsz /= 2;
       +                ether->oq = qopen(bsz, Qmsg, 0, 0);
       +                ether->ni.limit = bsz;
       +        }
       +        if(ether->oq == nil)
       +                panic("etherreset %s", name);
       +        ether->ni.alen = Eaddrlen;
       +        memmove(ether->ni.addr, ether->ea, Eaddrlen);
       +        memset(ether->ni.bcast, 0xFF, Eaddrlen);
       +
       +        return ether;
       +}
       +
       +static void
       +etherreset(void)
       +{
       +        Ether *ether;
       +        int cardno, ctlrno;
       +
       +        for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
       +                if((ether = etherprobe(-1, ctlrno)) == nil)
       +                        continue;
       +                etherxx[ctlrno] = ether;
       +        }
       +
       +        cardno = ctlrno = 0;
       +        while(cards[cardno].type != nil && ctlrno < MaxEther){
       +                if(etherxx[ctlrno] != nil){
       +                        ctlrno++;
       +                        continue;
       +                }
       +                if((ether = etherprobe(cardno, ctlrno)) == nil){
       +                        cardno++;
       +                        continue;
       +                }
       +                etherxx[ctlrno] = ether;
       +                ctlrno++;
       +        }
       +}
       +
       +static void
       +ethershutdown(void)
       +{
       +        Ether *ether;
       +        int i;
       +
       +        for(i = 0; i < MaxEther; i++){
       +                ether = etherxx[i];
       +                if(ether == nil)
       +                        continue;
       +                if(ether->shutdown == nil) {
       +                        print("#l%d: no shutdown fuction\n", i);
       +                        continue;
       +                }
       +                (*ether->shutdown)(ether);
       +        }
       +}
       +
       +
       +#define POLY 0xedb88320
       +
       +/* really slow 32 bit crc for ethers */
       +ulong
       +ethercrc(uchar *p, int len)
       +{
       +        int i, j;
       +        ulong crc, b;
       +
       +        crc = 0xffffffff;
       +        for(i = 0; i < len; i++){
       +                b = *p++;
       +                for(j = 0; j < 8; j++){
       +                        crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
       +                        b >>= 1;
       +                }
       +        }
       +        return crc;
       +}
       +
       +Dev etherdevtab = {
       +        'l',
       +        "ether",
       +
       +        etherreset,
       +        devinit,
       +        ethershutdown,
       +        etherattach,
       +        etherwalk,
       +        etherstat,
       +        etheropen,
       +        ethercreate,
       +        etherclose,
       +        etherread,
       +        etherbread,
       +        etherwrite,
       +        etherbwrite,
       +        devremove,
       +        etherwstat,
       +};
 (DIR) diff --git a/src/9vx/a/etherif.h b/src/9vx/a/etherif.h
       @@ -0,0 +1,39 @@
       +enum {
       +        MaxEther        = 48,
       +        Ntypes                = 8,
       +};
       +
       +ttypedef struct Ether Ether;
       +struct Ether {
       +        ISAConf isac;
       +
       +        int        ctlrno;
       +        int        tbdf;                        /* type+busno+devno+funcno */
       +        int        minmtu;
       +        int         maxmtu;
       +        uchar        ea[Eaddrlen];
       +
       +        void        (*attach)(Ether*);        /* filled in by reset routine */
       +        void        (*detach)(Ether*);
       +        void        (*transmit)(Ether*);
       +        void        (*interrupt)(Ureg*, void*);
       +        long        (*ifstat)(Ether*, void*, long, ulong);
       +        long         (*ctl)(Ether*, void*, long); /* custom ctl messages */
       +        void        (*power)(Ether*, int);        /* power on/off */
       +        void        (*shutdown)(Ether*);        /* shutdown hardware before reboot */
       +        void        *ctlr;
       +
       +        Queue*        oq;
       +
       +        Netif        ni;
       +};
       +
       +extern Block* etheriq(Ether*, Block*, int);
       +extern void addethercard(char*, int(*)(Ether*));
       +extern ulong ethercrc(uchar*, int);
       +extern int parseether(uchar*, char*);
       +
       +#define NEXT(x, l)        (((uint)(x)+1)%(l))
       +#define PREV(x, l)        (((x) == 0) ? (l)-1: (x)-1)
       +#define        HOWMANY(x, y)        (((x)+((y)-1))/(y))
       +#define ROUNDUP(x, y)        (HOWMANY((x), (y))*(y))
 (DIR) diff --git a/src/9vx/a/netif.c b/src/9vx/a/netif.c
       @@ -0,0 +1,761 @@
       +#include        "u.h"
       +#include        "lib.h"
       +#include        "mem.h"
       +#include        "dat.h"
       +#include        "fns.h"
       +#include        "error.h"
       +#include        "netif.h"
       +
       +static int netown(Netfile*, char*, int);
       +static int openfile(Netif*, int);
       +static char* matchtoken(char*, char*);
       +static char* netmulti(Netif*, Netfile*, uchar*, int);
       +static int parseaddr(uchar*, char*, int);
       +
       +int        netifdebug;
       +#define        dprint(...)        if(netifdebug)print(__VA_ARGS__); else USED(netifdebug)
       +
       +/*
       + *  set up a new network interface
       + */
       +void
       +netifinit(Netif *nif, char *name, int nfile, ulong limit)
       +{
       +        strncpy(nif->name, name, KNAMELEN-1);
       +        nif->name[KNAMELEN-1] = 0;
       +        nif->nfile = nfile;
       +        nif->f = xalloc(nfile*sizeof(Netfile*));
       +        if (nif->f == nil)
       +                panic("netifinit: no memory");
       +        nif->limit = limit;
       +}
       +
       +#define DD(c,q,nam,n,owner,perm,dp) dprint("%lux.%llux %s\n", q.type, q.path, nam); devdir(c,q,nam,n,owner,perm,dp)
       +
       +/*
       + *  generate a 3 level directory
       + */
       +static int
       +netifgen(Chan *c, char *dummy, Dirtab *vp, int dummy1, int i, Dir *dp)
       +{
       +        Qid q;
       +        Netif *nif = (Netif*)vp;
       +        Netfile *f;
       +        int t, perm;
       +        char *o;
       +
       +        memset(&q, 0, sizeof q);
       +        q.type = QTFILE;
       +        q.vers = 0;
       +
       +        dprint("gen %d %llud %.2d        ", c->dri, c->qid.path, i);
       +        /* top level directory contains the name of the network */
       +        if(c->qid.path == 0){
       +                switch(i){
       +                case DEVDOTDOT:
       +                        q.path = 0;
       +                        q.type = QTDIR;
       +                        DD(c, q, ".", 0, eve, 0555, dp);
       +                        break;
       +                case 0:
       +                        q.path = N2ndqid;
       +                        q.type = QTDIR;
       +                        strcpy(up->genbuf, nif->name);
       +                        DD(c, q, up->genbuf, 0, eve, 0555, dp);
       +                        break;
       +                default:
       +                        dprint("-> -1 (top)\n");
       +                        return -1;
       +                }
       +                return 1;
       +        }
       +
       +        /* second level contains clone plus all the conversations */
       +        t = NETTYPE(c->qid.path);
       +        if(t == N2ndqid || t == Ncloneqid || t == Naddrqid || t == Nstatqid || t == Nifstatqid){
       +                switch(i){
       +                case DEVDOTDOT:
       +                        q.type = QTDIR;
       +                        q.path = 0;
       +                        DD(c, q, ".", 0, eve, DMDIR|0555, dp);
       +                        break;
       +                case 0:
       +                        q.path = Ncloneqid;
       +                        DD(c, q, "clone", 0, eve, 0666, dp);
       +                        break;
       +                case 1:
       +                        q.path = Naddrqid;
       +                        DD(c, q, "addr", 0, eve, 0666, dp);
       +                        break;
       +                case 2:
       +                        q.path = Nstatqid;
       +                        DD(c, q, "stats", 0, eve, 0444, dp);
       +                        break;
       +                case 3:
       +                        q.path = Nifstatqid;
       +                        DD(c, q, "ifstats", 0, eve, 0444, dp);
       +                        break;
       +                default:
       +                        i -= 4;
       +                        if(i >= nif->nfile){
       +                                dprint("-> -1 (2d): %d %d\n", i, nif->nfile);
       +                                return -1;
       +                        }
       +                        if(nif->f[i] == 0){
       +                                dprint("nif->f[%d] -> 0\n", i);
       +                                return 0;
       +                        }
       +                        q.type = QTDIR;
       +                        q.path = NETQID(i, N3rdqid);
       +                        sprint(up->genbuf, "%d", i);
       +                        DD(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
       +                        break;
       +                }
       +                return 1;
       +        }
       +
       +        /* third level */
       +        f = nif->f[NETID(c->qid.path)];
       +        if(f == 0){
       +                dprint("->f 0\n");
       +                return -1;
       +        }
       +        if(*f->owner){
       +                o = f->owner;
       +                perm = f->mode;
       +        } else {
       +                o = eve;
       +                perm = 0666;
       +        }
       +        switch(i){
       +        case DEVDOTDOT:
       +                q.type = QTDIR;
       +                q.path = N2ndqid;
       +                strcpy(up->genbuf, nif->name);
       +                DD(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
       +                break;
       +        case 0:
       +                q.path = NETQID(NETID(c->qid.path), Ndataqid);
       +                DD(c, q, "data", 0, o, perm, dp);
       +                break;
       +        case 1:
       +                q.path = NETQID(NETID(c->qid.path), Nctlqid);
       +                DD(c, q, "ctl", 0, o, perm, dp);
       +                break;
       +        case 2:
       +                q.path = NETQID(NETID(c->qid.path), Nstatqid);
       +                DD(c, q, "stats", 0, eve, 0444, dp);
       +                break;
       +        case 3:
       +                q.path = NETQID(NETID(c->qid.path), Ntypeqid);
       +                DD(c, q, "type", 0, eve, 0444, dp);
       +                break;
       +        case 4:
       +                q.path = NETQID(NETID(c->qid.path), Nifstatqid);
       +                DD(c, q, "ifstats", 0, eve, 0444, dp);
       +                break;
       +        default:
       +                dprint("-> -1 (third)\n");
       +                return -1;
       +        }
       +        return 1;
       +}
       +
       +static void
       +prwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname)
       +{
       +        char buf[512], *e, *p;
       +
       +        if(netifdebug == 0)
       +                return;
       +        p = buf;
       +        e = p + sizeof buf;
       +        for(int i = 0; i < nname; i++)
       +                p = seprint(p, e, "%s ", name[i]);
       +        if(p > buf)
       +                p--;
       +        *p = 0;
       +        print("netifwalk %lld [%s]\n", c->qid.path, buf);
       +}
       +
       +Walkqid*
       +netifwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname)
       +{
       +        prwalk(nif, c, nc, name, nname);
       +        return devwalk(c, nc, name, nname, (Dirtab *)nif, 0, netifgen);
       +}
       +
       +Chan*
       +netifopen(Netif *nif, Chan *c, int omode)
       +{
       +        int id;
       +        Netfile *f;
       +
       +        dprint("netifopen %p %d\n", nif, c? c->qid.path: -1);
       +        id = 0;
       +        if(c->qid.type & QTDIR){
       +                if(omode != OREAD)
       +                        error(Eperm);
       +        } else {
       +                switch(NETTYPE(c->qid.path)){
       +                case Ndataqid:
       +                case Nctlqid:
       +                        id = NETID(c->qid.path);
       +                        openfile(nif, id);
       +                        break;
       +                case Ncloneqid:
       +                        id = openfile(nif, -1);
       +                        c->qid.path = NETQID(id, Nctlqid);
       +                        break;
       +                default:
       +                        if(omode != OREAD)
       +                                error(Ebadarg);
       +                }
       +                switch(NETTYPE(c->qid.path)){
       +                case Ndataqid:
       +                case Nctlqid:
       +                        f = nif->f[id];
       +                        if(netown(f, up->user, omode&7) < 0)
       +                                error(Eperm);
       +                        break;
       +                }
       +        }
       +        c->mode = openmode(omode);
       +        c->flag |= COPEN;
       +        c->offset = 0;
       +        c->iounit = qiomaxatomic;
       +        return c;
       +}
       +
       +long
       +netifread(Netif *nif, Chan *c, void *a, long n, ulong offset)
       +{
       +        int i, j;
       +        Netfile *f;
       +        char *p;
       +
       +        dprint("netifread %lud %lud\n", c->qid.path, NETTYPE(c->qid.path));
       +        if(c->qid.type&QTDIR)
       +                return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen);
       +
       +        switch(NETTYPE(c->qid.path)){
       +        case Ndataqid:
       +                f = nif->f[NETID(c->qid.path)];
       +                return qread(f->in, a, n);
       +        case Nctlqid:
       +                return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE);
       +        case Nstatqid:
       +                dprint("netstatqid\n");
       +                p = smalloc(READSTR);
       +                j = snprint(p, READSTR, "in: %llud\n", nif->inpackets);
       +                j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
       +                j += snprint(p+j, READSTR-j, "out: %llud\n", nif->outpackets);
       +                j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
       +                j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
       +                j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
       +                j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames);
       +                j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs);
       +                j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs);
       +                j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom);
       +                j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps);
       +                j += snprint(p+j, READSTR-j, "addr: ");
       +                for(i = 0; i < nif->alen; i++)
       +                        j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
       +                snprint(p+j, READSTR-j, "\n");
       +                n = readstr(offset, a, n, p);
       +                free(p);
       +                return n;
       +        case Naddrqid:
       +                p = malloc(READSTR);
       +                j = 0;
       +                for(i = 0; i < nif->alen; i++)
       +                        j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
       +                n = readstr(offset, a, n, p);
       +                free(p);
       +                return n;
       +        case Ntypeqid:
       +                f = nif->f[NETID(c->qid.path)];
       +                return readnum(offset, a, n, f->type, NUMSIZE);
       +        case Nifstatqid:
       +                return 0;
       +        }
       +        error(Ebadarg);
       +        return -1;        /* not reached */
       +}
       +
       +Block*
       +netifbread(Netif *nif, Chan *c, long n, ulong offset)
       +{
       +        if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
       +                return devbread(c, n, offset);
       +
       +        return qbread(nif->f[NETID(c->qid.path)]->in, n);
       +}
       +
       +/*
       + *  make sure this type isn't already in use on this device
       + */
       +static int
       +ttypeinuse(Netif *nif, int type)
       +{
       +        Netfile *f, **fp, **efp;
       +
       +        if(type <= 0)
       +                return 0;
       +
       +        efp = &nif->f[nif->nfile];
       +        for(fp = nif->f; fp < efp; fp++){
       +                f = *fp;
       +                if(f == 0)
       +                        continue;
       +                if(f->type == type)
       +                        return 1;
       +        }
       +        return 0;
       +}
       +
       +/*
       + *  the devxxx.c that calls us handles writing data, it knows best
       + */
       +long
       +netifwrite(Netif *nif, Chan *c, void *a, long n)
       +{
       +        Netfile *f;
       +        int type;
       +        char *p, buf[64];
       +        uchar binaddr[Nmaxaddr];
       +
       +        if(NETTYPE(c->qid.path) != Nctlqid)
       +                error(Eperm);
       +
       +        if(n >= sizeof(buf))
       +                n = sizeof(buf)-1;
       +        memmove(buf, a, n);
       +        buf[n] = 0;
       +
       +        if(waserror()){
       +                QUNLOCK(nif);
       +                nexterror();
       +        }
       +
       +        QLOCK(nif);
       +        f = nif->f[NETID(c->qid.path)];
       +        if((p = matchtoken(buf, "connect")) != 0){
       +                type = atoi(p);
       +                if(typeinuse(nif, type))
       +                        error(Einuse);
       +                f->type = type;
       +                if(f->type < 0)
       +                        nif->all++;
       +        } else if(matchtoken(buf, "promiscuous")){
       +                if(f->prom == 0){
       +                        if(nif->prom == 0 && nif->promiscuous != nil)
       +                                nif->promiscuous(nif->arg, 1);
       +                        f->prom = 1;
       +                        nif->prom++;
       +                }
       +        } else if((p = matchtoken(buf, "scanbs")) != 0){
       +                /* scan for base stations */
       +                if(f->scan == 0){
       +                        type = atoi(p);
       +                        if(type < 5)
       +                                type = 5;
       +                        if(nif->scanbs != nil)
       +                                nif->scanbs(nif->arg, type);
       +                        f->scan = type;
       +                        nif->scan++;
       +                }
       +        } else if(matchtoken(buf, "bridge")){
       +                f->bridge = 1;
       +        } else if(matchtoken(buf, "headersonly")){
       +                f->headersonly = 1;
       +        } else if((p = matchtoken(buf, "addmulti")) != 0){
       +                if(parseaddr(binaddr, p, nif->alen) < 0)
       +                        error("bad address");
       +                p = netmulti(nif, f, binaddr, 1);
       +                if(p)
       +                        error(p);
       +        } else if((p = matchtoken(buf, "remmulti")) != 0){
       +                if(parseaddr(binaddr, p, nif->alen) < 0)
       +                        error("bad address");
       +                p = netmulti(nif, f, binaddr, 0);
       +                if(p)
       +                        error(p);
       +        } else
       +                n = -1;
       +        QUNLOCK(nif);
       +        poperror();
       +        return n;
       +}
       +
       +int
       +netifwstat(Netif *nif, Chan *c, uchar *db, int n)
       +{
       +        Dir *dir;
       +        Netfile *f;
       +        int m;
       +
       +        f = nif->f[NETID(c->qid.path)];
       +        if(f == 0)
       +                error(Enonexist);
       +
       +        if(netown(f, up->user, OWRITE) < 0)
       +                error(Eperm);
       +
       +        dir = smalloc(sizeof(Dir)+n);
       +        m = convM2D(db, n, &dir[0], (char*)&dir[1]);
       +        if(m == 0){
       +                free(dir);
       +                error(Eshortstat);
       +        }
       +        if(!emptystr(dir[0].uid))
       +                strncpy(f->owner, dir[0].uid, KNAMELEN);
       +        if(dir[0].mode != ~0UL)
       +                f->mode = dir[0].mode;
       +        free(dir);
       +        return m;
       +}
       +
       +int
       +netifstat(Netif *nif, Chan *c, uchar *db, int n)
       +{
       +        dprint("netifstat %s nfile %d %lld type=%d\n", nif->name, nif->nfile, c->qid.path, c->type);
       +        return devstat(c, db, n, (Dirtab *)nif, 0, netifgen);
       +}
       +
       +void
       +netifclose(Netif *nif, Chan *c)
       +{
       +        Netfile *f;
       +        int t;
       +        Netaddr *ap;
       +
       +        if((c->flag & COPEN) == 0)
       +                return;
       +
       +        t = NETTYPE(c->qid.path);
       +        if(t != Ndataqid && t != Nctlqid)
       +                return;
       +
       +        f = nif->f[NETID(c->qid.path)];
       +        QLOCK(f);
       +        if(--(f->inuse) == 0){
       +                if(f->prom){
       +                        QLOCK(nif);
       +                        if(--(nif->prom) == 0 && nif->promiscuous != nil)
       +                                nif->promiscuous(nif->arg, 0);
       +                        QUNLOCK(nif);
       +                        f->prom = 0;
       +                }
       +                if(f->scan){
       +                        QLOCK(nif);
       +                        if(--(nif->scan) == 0 && nif->scanbs != nil)
       +                                nif->scanbs(nif->arg, 0);
       +                        QUNLOCK(nif);
       +                        f->prom = 0;
       +                        f->scan = 0;
       +                }
       +                if(f->nmaddr){
       +                        QLOCK(nif);
       +                        t = 0;
       +                        for(ap = nif->maddr; ap; ap = ap->next){
       +                                if(f->maddr[t/8] & (1<<(t%8)))
       +                                        netmulti(nif, f, ap->addr, 0);
       +                        }
       +                        QUNLOCK(nif);
       +                        f->nmaddr = 0;
       +                }
       +                if(f->type < 0){
       +                        QLOCK(nif);
       +                        --(nif->all);
       +                        QUNLOCK(nif);
       +                }
       +                f->owner[0] = 0;
       +print("drop type %.4ux\n", f->type);
       +                f->type = 0;
       +                f->bridge = 0;
       +                f->headersonly = 0;
       +                qclose(f->in);
       +        }
       +        QUNLOCK(f);
       +}
       +
       +Lock netlock;
       +
       +static int
       +netown(Netfile *p, char *o, int omode)
       +{
       +        static int access[] = { 0400, 0200, 0600, 0100 };
       +        int mode;
       +        int t;
       +
       +        lock(&netlock);
       +        if(*p->owner){
       +                if(strncmp(o, p->owner, KNAMELEN) == 0)        /* User */
       +                        mode = p->mode;
       +                else if(strncmp(o, eve, KNAMELEN) == 0)        /* Bootes is group */
       +                        mode = p->mode<<3;
       +                else
       +                        mode = p->mode<<6;                /* Other */
       +
       +                t = access[omode&3];
       +                if((t & mode) == t){
       +                        unlock(&netlock);
       +                        return 0;
       +                } else {
       +                        unlock(&netlock);
       +                        return -1;
       +                }
       +        }
       +        strncpy(p->owner, o, KNAMELEN);
       +        p->mode = 0660;
       +        unlock(&netlock);
       +        return 0;
       +}
       +
       +/*
       + *  Increment the reference count of a network device.
       + *  If id < 0, return an unused ether device.
       + */
       +static int
       +openfile(Netif *nif, int id)
       +{
       +        Netfile *f, **fp, **efp;
       +
       +        if(id >= 0){
       +                f = nif->f[id];
       +                if(f == 0)
       +                        error(Enodev);
       +                QLOCK(f);
       +                qreopen(f->in);
       +                f->inuse++;
       +                QUNLOCK(f);
       +                return id;
       +        }
       +
       +        QLOCK(nif);
       +        if(waserror()){
       +                QUNLOCK(nif);
       +                nexterror();
       +        }
       +        efp = &nif->f[nif->nfile];
       +        for(fp = nif->f; fp < efp; fp++){
       +                f = *fp;
       +                if(f == 0){
       +                        f = malloc(sizeof(Netfile));
       +                        if(f == 0)
       +                                exhausted("memory");
       +                        f->in = qopen(nif->limit, Qmsg, 0, 0);
       +                        if(f->in == nil){
       +                                free(f);
       +                                exhausted("memory");
       +                        }
       +                        *fp = f;
       +                        QLOCK(f);
       +                } else {
       +                        QLOCK(f);
       +                        if(f->inuse){
       +                                QUNLOCK(f);
       +                                continue;
       +                        }
       +                }
       +                f->inuse = 1;
       +                qreopen(f->in);
       +                netown(f, up->user, 0);
       +                QUNLOCK(f);
       +                QUNLOCK(nif);
       +                poperror();
       +                return fp - nif->f;
       +        }
       +        error(Enodev);
       +        return -1;        /* not reached */
       +}
       +
       +/*
       + *  look for a token starting a string,
       + *  return a pointer to first non-space char after it
       + */
       +static char*
       +matchtoken(char *p, char *token)
       +{
       +        int n;
       +
       +        n = strlen(token);
       +        if(strncmp(p, token, n))
       +                return 0;
       +        p += n;
       +        if(*p == 0)
       +                return p;
       +        if(*p != ' ' && *p != '\t' && *p != '\n')
       +                return 0;
       +        while(*p == ' ' || *p == '\t' || *p == '\n')
       +                p++;
       +        return p;
       +}
       +
       +void
       +hnputv(void *p, uvlong v)
       +{
       +        uchar *a;
       +
       +        a = p;
       +        hnputl(a, v>>32);
       +        hnputl(a+4, v);
       +}
       +
       +void
       +hnputl(void *p, uint v)
       +{
       +        uchar *a;
       +
       +        a = p;
       +        a[0] = v>>24;
       +        a[1] = v>>16;
       +        a[2] = v>>8;
       +        a[3] = v;
       +}
       +
       +void
       +hnputs(void *p, ushort v)
       +{
       +        uchar *a;
       +
       +        a = p;
       +        a[0] = v>>8;
       +        a[1] = v;
       +}
       +
       +uvlong
       +nhgetv(void *p)
       +{
       +        uchar *a;
       +
       +        a = p;
       +        return ((vlong)nhgetl(a) << 32) | nhgetl(a+4);
       +}
       +
       +uint
       +nhgetl(void *p)
       +{
       +        uchar *a;
       +
       +        a = p;
       +        return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
       +}
       +
       +ushort
       +nhgets(void *p)
       +{
       +        uchar *a;
       +
       +        a = p;
       +        return (a[0]<<8)|(a[1]<<0);
       +}
       +
       +static ulong
       +hash(uchar *a, int len)
       +{
       +        ulong sum = 0;
       +
       +        while(len-- > 0)
       +                sum = (sum << 1) + *a++;
       +        return sum%Nmhash;
       +}
       +
       +int
       +activemulti(Netif *nif, uchar *addr, int alen)
       +{
       +        Netaddr *hp;
       +
       +        for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
       +                if(memcmp(addr, hp->addr, alen) == 0){
       +                        if(hp->ref)
       +                                return 1;
       +                        else
       +                                break;
       +                }
       +        return 0;
       +}
       +
       +static int
       +parseaddr(uchar *to, char *from, int alen)
       +{
       +        char nip[4];
       +        char *p;
       +        int i;
       +
       +        p = from;
       +        for(i = 0; i < alen; 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;
       +}
       +
       +/*
       + *  keep track of multicast addresses
       + */
       +static char*
       +netmulti(Netif *nif, Netfile *f, uchar *addr, int add)
       +{
       +        Netaddr **l, *ap;
       +        int i;
       +        ulong h;
       +
       +        if(nif->multicast == nil)
       +                return "interface does not support multicast";
       +
       +        l = &nif->maddr;
       +        i = 0;
       +        for(ap = *l; ap; ap = *l){
       +                if(memcmp(addr, ap->addr, nif->alen) == 0)
       +                        break;
       +                i++;
       +                l = &ap->next;
       +        }
       +
       +        if(add){
       +                if(ap == 0){
       +                        *l = ap = smalloc(sizeof(*ap));
       +                        memmove(ap->addr, addr, nif->alen);
       +                        ap->next = 0;
       +                        ap->ref = 1;
       +                        h = hash(addr, nif->alen);
       +                        ap->hnext = nif->mhash[h];
       +                        nif->mhash[h] = ap;
       +                } else {
       +                        ap->ref++;
       +                }
       +                if(ap->ref == 1){
       +                        nif->nmaddr++;
       +                        nif->multicast(nif->arg, addr, 1);
       +                }
       +                if(i < 8*sizeof(f->maddr)){
       +                        if((f->maddr[i/8] & (1<<(i%8))) == 0)
       +                                f->nmaddr++;
       +                        f->maddr[i/8] |= 1<<(i%8);
       +                }
       +        } else {
       +                if(ap == 0 || ap->ref == 0)
       +                        return 0;
       +                ap->ref--;
       +                if(ap->ref == 0){
       +                        nif->nmaddr--;
       +                        nif->multicast(nif->arg, addr, 0);
       +                }
       +                if(i < 8*sizeof(f->maddr)){
       +                        if((f->maddr[i/8] & (1<<(i%8))) != 0)
       +                                f->nmaddr--;
       +                        f->maddr[i/8] &= ~(1<<(i%8));
       +                }
       +        }
       +        return 0;
       +}
 (DIR) diff --git a/src/9vx/a/sdaoe.c b/src/9vx/a/sdaoe.c
       @@ -0,0 +1,652 @@
       +/*
       + * aoe sd driver, copyright © 2007 coraid
       + */
       +
       +#include "u.h"
       +#include "lib.h"
       +#include "mem.h"
       +#include "dat.h"
       +#include "fns.h"
       +#include "io.h"
       +#include "error.h"
       +#include "sd.h"
       +#include "netif.h"
       +#include "aoe.h"
       +
       +extern        char        Echange[];
       +extern        char        Enotup[];
       +
       +#define uprint(...)        snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
       +
       +enum {
       +        Nctlr        = 32,
       +        Maxpath        = 128,
       +};
       +
       +enum {
       +        /* sync with ahci.h */
       +        Dllba         = 1<<0,
       +        Dsmart        = 1<<1,
       +        Dpower        = 1<<2,
       +        Dnop        = 1<<3,
       +        Datapi        = 1<<4,
       +        Datapi16= 1<<5,
       +};
       +
       +static char *flagname[] = {
       +        "llba",
       +        "smart",
       +        "power",
       +        "nop",
       +        "atapi",
       +        "atapi16",
       +};
       +
       +ttypedef struct Ctlr Ctlr;
       +struct Ctlr{
       +        QLock        qlock;
       +
       +        Ctlr        *next;
       +        SDunit        *unit;
       +
       +        char        path[Maxpath];
       +        Chan        *c;
       +
       +        ulong        vers;
       +        uchar        mediachange;
       +        uchar        flag;
       +        uchar        smart;
       +        uchar        smartrs;
       +        uchar        feat;
       +
       +        uvlong        sectors;
       +        char        serial[20+1];
       +        char        firmware[8+1];
       +        char        model[40+1];
       +        char        ident[0x100];
       +};
       +
       +static        Lock        ctlrlock;
       +static        Ctlr        *head;
       +static        Ctlr        *tail;
       +
       +SDifc sdaoeifc;
       +
       +static void
       +idmove(char *p, ushort *a, int n)
       +{
       +        int i;
       +        char *op, *e;
       +
       +        op = p;
       +        for(i = 0; i < n/2; i++){
       +                *p++ = a[i] >> 8;
       +                *p++ = a[i];
       +        }
       +        *p = 0;
       +        while(p > op && *--p == ' ')
       +                *p = 0;
       +        e = p;
       +        p = op;
       +        while(*p == ' ')
       +                p++;
       +        memmove(op, p, n - (e - p));
       +}
       +
       +static ushort
       +gbit16(void *a)
       +{
       +        uchar *i;
       +
       +        i = a;
       +        return i[1] << 8 | i[0];
       +}
       +
       +static ulong
       +gbit32(void *a)
       +{
       +        ulong j;
       +        uchar *i;
       +
       +        i = a;
       +        j  = i[3] << 24;
       +        j |= i[2] << 16;
       +        j |= i[1] << 8;
       +        j |= i[0];
       +        return j;
       +}
       +
       +static uvlong
       +gbit64(void *a)
       +{
       +        uchar *i;
       +
       +        i = a;
       +        return (uvlong)gbit32(i+4)<<32 | gbit32(i);
       +}
       +
       +static int
       +identify(Ctlr *c, ushort *id)
       +{
       +        int i;
       +        uchar oserial[21];
       +        uvlong osectors, s;
       +
       +        osectors = c->sectors;
       +        memmove(oserial, c->serial, sizeof c->serial);
       +
       +        c->feat &= ~(Dllba|Dpower|Dsmart|Dnop);
       +        i = gbit16(id+83) | gbit16(id+86);
       +        if(i & (1<<10)){
       +                c->feat |= Dllba;
       +                s = gbit64(id+100);
       +        }else
       +                s = gbit32(id+60);
       +
       +        i = gbit16(id+83);
       +        if((i>>14) == 1) {
       +                if(i & (1<<3))
       +                        c->feat |= Dpower;
       +                i = gbit16(id+82);
       +                if(i & 1)
       +                        c->feat |= Dsmart;
       +                if(i & (1<<14))
       +                        c->feat |= Dnop;
       +        }
       +
       +        idmove(c->serial, id+10, 20);
       +        idmove(c->firmware, id+23, 8);
       +        idmove(c->model, id+27, 40);
       +
       +        if((osectors == 0 || osectors != s) &&
       +            memcmp(oserial, c->serial, sizeof oserial) != 0){
       +                c->sectors = s;
       +                c->mediachange = 1;
       +                c->vers++;
       +        }
       +        return 0;
       +}
       +
       +/* must call with d qlocked */
       +static int
       +aoeidentify(Ctlr *d, SDunit *u)
       +{
       +        Chan *c;
       +
       +        c = nil;
       +        if(waserror()){
       +                if(c)
       +                        cclose(c);
       +                iprint("aoeidentify: %s\n", up->errstr);
       +                nexterror();
       +        }
       +
       +        uprint("%s/ident", d->path);
       +        c = namec(up->genbuf, Aopen, OREAD, 0);
       +        devtab[c->type]->read(c, d->ident, sizeof d->ident, 0);
       +
       +        poperror();
       +        cclose(c);
       +
       +        d->feat = 0;
       +        d->smart = 0;
       +        identify(d, (ushort*)d->ident);
       +
       +        memset(u->inquiry, 0, sizeof u->inquiry);
       +        u->inquiry[2] = 2;
       +        u->inquiry[3] = 2;
       +        u->inquiry[4] = sizeof u->inquiry - 4;
       +        memmove(u->inquiry+8, d->model, 40);
       +
       +        return 0;
       +}
       +
       +static Ctlr*
       +ctlrlookup(char *path)
       +{
       +        Ctlr *c;
       +
       +        lock(&ctlrlock);
       +        for(c = head; c; c = c->next)
       +                if(strcmp(c->path, path) == 0)
       +                        break;
       +        unlock(&ctlrlock);
       +        return c;
       +}
       +
       +static Ctlr*
       +newctlr(char *path)
       +{
       +        Ctlr *c;
       +
       +        /* race? */
       +        if(ctlrlookup(path))
       +                error(Eexist);
       +
       +        if((c = malloc(sizeof *c)) == nil)
       +                return 0;
       +        kstrcpy(c->path, path, sizeof c->path);
       +        lock(&ctlrlock);
       +        if(head != nil)
       +                tail->next = c;
       +        else
       +                head = c;
       +        tail = c;
       +        unlock(&ctlrlock);
       +        return c;
       +}
       +
       +static void
       +delctlr(Ctlr *c)
       +{
       +        Ctlr *x, *prev;
       +
       +        lock(&ctlrlock);
       +
       +        for(prev = 0, x = head; x; prev = x, x = c->next)
       +                if(strcmp(c->path, x->path) == 0)
       +                        break;
       +        if(x == 0){
       +                unlock(&ctlrlock);
       +                error(Enonexist);
       +        }
       +
       +        if(prev)
       +                prev->next = x->next;
       +        else
       +                head = x->next;
       +        if(x->next == nil)
       +                tail = prev;
       +        unlock(&ctlrlock);
       +
       +        if(x->c)
       +                cclose(x->c);
       +        free(x);
       +}
       +
       +static SDev*
       +aoeprobe(char *path, SDev *s)
       +{
       +        int n, i;
       +        char *p;
       +        Chan *c;
       +        Ctlr *ctlr;
       +
       +        if((p = strrchr(path, '/')) == 0)
       +                error(Ebadarg);
       +        *p = 0;
       +        uprint("%s/ctl", path);
       +        *p = '/';
       +
       +        c = namec(up->genbuf, Aopen, OWRITE, 0);
       +        if(waserror()) {
       +                cclose(c);
       +                nexterror();
       +        }
       +        n = uprint("discover %s", p+1);
       +        devtab[c->type]->write(c, up->genbuf, n, 0);
       +        poperror();
       +        cclose(c);
       +
       +        for(i = 0;; i += 200){
       +                if(i > 8000 || waserror())
       +                        error(Etimedout);
       +                tsleep(&up->sleep, return0, 0, 200);
       +                poperror();
       +
       +                uprint("%s/ident", path);
       +                if(waserror())
       +                        continue;
       +                c = namec(up->genbuf, Aopen, OREAD, 0);
       +                poperror();
       +                cclose(c);
       +
       +                ctlr = newctlr(path);
       +                break;
       +        }
       +
       +        if(s == nil && (s = malloc(sizeof *s)) == nil)
       +                return nil;
       +        s->ctlr = ctlr;
       +        s->ifc = &sdaoeifc;
       +        s->nunit = 1;
       +        return s;
       +}
       +
       +static char         *probef[32];
       +static int         nprobe;
       +
       +static int
       +pnpprobeid(char *s)
       +{
       +        int id;
       +
       +        if(strlen(s) < 2)
       +                return 0;
       +        id = 'e';
       +        if(s[1] == '!')
       +                id = s[0];
       +        return id;
       +}
       +
       +static SDev*
       +aoepnp(void)
       +{
       +        int i, id;
       +        char *p;
       +        SDev *h, *t, *s;
       +
       +//        if((p = getconf("aoedev")) == 0)
       +        if(1)
       +                return 0;
       +        nprobe = tokenize(p, probef, nelem(probef));
       +        h = t = 0;
       +        for(i = 0; i < nprobe; i++){
       +                id = pnpprobeid(probef[i]);
       +                if(id == 0)
       +                        continue;
       +                s = malloc(sizeof *s);
       +                if(s == nil)
       +                        break;
       +                s->ctlr = 0;
       +                s->idno = id;
       +                s->ifc = &sdaoeifc;
       +                s->nunit = 1;
       +
       +                if(h)
       +                        t->next = s;
       +                else
       +                        h = s;
       +                t = s;
       +        }
       +        return h;
       +}
       +
       +static Ctlr*
       +pnpprobe(SDev *sd)
       +{
       +        int j;
       +        char *p;
       +        static int i;
       +
       +        if(i > nprobe)
       +                return 0;
       +        p = probef[i++];
       +        if(strlen(p) < 2)
       +                return 0;
       +        if(p[1] == '!')
       +                p += 2;
       +
       +        for(j = 0;; j += 200){
       +                if(j > 8000){
       +                        print("#æ: pnpprobe: %s: %s\n", probef[i-1], up->errstr);
       +                        return 0;
       +                }
       +                if(waserror()){
       +                        tsleep(&up->sleep, return0, 0, 200);
       +                        continue;
       +                }
       +                sd = aoeprobe(p, sd);
       +                poperror();
       +                break;
       +        }
       +        print("#æ: pnpprobe establishes %sin %dms\n", probef[i-1], j);
       +        return sd->ctlr;
       +}
       +
       +
       +static int
       +aoeverify(SDunit *u)
       +{
       +        SDev *s;
       +        Ctlr *c;
       +
       +        s = u->dev;
       +        c = s->ctlr;
       +        if(c == nil && (s->ctlr = c = pnpprobe(s)) == nil)
       +                return 0;
       +        c->mediachange = 1;
       +        return 1;
       +}
       +
       +static int
       +aoeconnect(SDunit *u, Ctlr *c)
       +{
       +        QLOCK(c);
       +        if(waserror()){
       +                QUNLOCK(c);
       +                return -1;
       +        }
       +
       +        aoeidentify(u->dev->ctlr, u);
       +        if(c->c)
       +                cclose(c->c);
       +        c->c = 0;
       +        uprint("%s/data", c->path);
       +        c->c = namec(up->genbuf, Aopen, ORDWR, 0);
       +        QUNLOCK(c);
       +        poperror();
       +
       +        return 0;
       +}
       +
       +static int
       +aoeonline(SDunit *u)
       +{
       +        Ctlr *c;
       +        int r;
       +
       +        c = u->dev->ctlr;
       +        r = 0;
       +
       +        if((c->feat&Datapi) && c->mediachange){
       +                if(aoeconnect(u, c) == 0 && (r = scsionline(u)) > 0)
       +                        c->mediachange = 0;
       +                return r;
       +        }
       +
       +        if(c->mediachange){
       +                if(aoeconnect(u, c) == -1)
       +                        return 0;
       +                r = 2;
       +                c->mediachange = 0;
       +                u->sectors = c->sectors;
       +                u->secsize = Aoesectsz;
       +        } else
       +                r = 1;
       +
       +        return r;
       +}
       +
       +static int
       +aoerio(SDreq *r)
       +{
       +        int i, count;
       +        uvlong lba;
       +        char *name;
       +        uchar *cmd;
       +        long (*rio)(Chan*, void*, long, vlong);
       +        Ctlr *c;
       +        SDunit *unit;
       +
       +        unit = r->unit;
       +        c = unit->dev->ctlr;
       +//        if(c->feat & Datapi)
       +//                return aoeriopkt(r, d);
       +
       +        cmd = r->cmd;
       +        name = unit->perm.name;
       +
       +        if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
       +//                QLOCK(c);
       +//                i = flushcache();
       +//                QUNLOCK(c);
       +//                if(i == 0)
       +//                        return sdsetsense(r, SDok, 0, 0, 0);
       +                return sdsetsense(r, SDcheck, 3, 0xc, 2);
       +        }
       +
       +        if((i = sdfakescsi(r, c->ident, sizeof c->ident)) != SDnostatus){
       +                r->status = i;
       +                return i;
       +        }
       +
       +        switch(*cmd){
       +        case 0x88:
       +        case 0x28:
       +                rio = devtab[c->c->type]->read;
       +                break;
       +        case 0x8a:
       +        case 0x2a:
       +                rio = devtab[c->c->type]->write;
       +                break;
       +        default:
       +                print("%s: bad cmd %#.2ux\n", name, cmd[0]);
       +                r->status = SDcheck;
       +                return SDcheck;
       +        }
       +
       +        if(r->data == nil)
       +                return SDok;
       +
       +        if(r->clen == 16){
       +                if(cmd[2] || cmd[3])
       +                        return sdsetsense(r, SDcheck, 3, 0xc, 2);
       +                lba = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32;
       +                lba |=   cmd[6]<<24 |  cmd[7]<<16 |  cmd[8]<<8 | cmd[9];
       +                count = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13];
       +        }else{
       +                lba  = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
       +                count = cmd[7]<<8 | cmd[8];
       +        }
       +
       +        count *= Aoesectsz;
       +
       +        if(r->dlen < count)
       +                count = r->dlen & ~0x1ff;
       +
       +        if(waserror()){
       +                if(strcmp(up->errstr, Echange) == 0 ||
       +                    strcmp(up->errstr, Enotup) == 0)
       +                        unit->sectors = 0;
       +                nexterror();
       +        }
       +        r->rlen = rio(c->c, r->data, count, Aoesectsz * lba);
       +        poperror();
       +        r->status = SDok;
       +        return SDok;
       +}
       +
       +static char *smarttab[] = {
       +        "unset",
       +        "error",
       +        "threshold exceeded",
       +        "normal"
       +};
       +
       +static char *
       +pflag(char *s, char *e, uchar f)
       +{
       +        uchar i, m;
       +
       +        for(i = 0; i < 8; i++){
       +                m = 1 << i;
       +                if(f & m)
       +                        s = seprint(s, e, "%s ", flagname[i]);
       +        }
       +        return seprint(s, e, "\n");
       +}
       +
       +static int
       +aoerctl(SDunit *u, char *p, int l)
       +{
       +        Ctlr *c;
       +        char *e, *op;
       +
       +        if((c = u->dev->ctlr) == nil)
       +                return 0;
       +        e = p+l;
       +        op = p;
       +
       +        p = seprint(p, e, "model\t%s\n", c->model);
       +        p = seprint(p, e, "serial\t%s\n", c->serial);
       +        p = seprint(p, e, "firm        %s\n", c->firmware);
       +        if(c->smartrs == 0xff)
       +                p = seprint(p, e, "smart\tenable error\n");
       +        else if(c->smartrs == 0)
       +                p = seprint(p, e, "smart\tdisabled\n");
       +        else
       +                p = seprint(p, e, "smart\t%s\n", smarttab[c->smart]);
       +        p = seprint(p, e, "flag        ");
       +        p = pflag(p, e, c->feat);
       +        p = seprint(p, e, "geometry %llud %d\n", c->sectors, Aoesectsz);
       +        return p-op;
       +}
       +
       +static int
       +aoewctl(SDunit *d1, Cmdbuf *cmd)
       +{
       +        cmderror(cmd, Ebadarg);
       +        return 0;
       +}
       +
       +static SDev*
       +aoeprobew(DevConf *c)
       +{
       +        char *p;
       +
       +        p = strchr(c->type, '/');
       +        if(p == nil || strlen(p) > Maxpath - 11)
       +                error(Ebadarg);
       +        if(p[1] == '#')
       +                p++;                        /* hack */
       +        if(ctlrlookup(p))
       +                error(Einuse);
       +        return aoeprobe(p, 0);
       +}
       +
       +static void
       +aoeclear(SDev *s)
       +{
       +        delctlr((Ctlr *)s->ctlr);
       +}
       +
       +static char*
       +aoertopctl(SDev *s, char *p, char *e)
       +{
       +        Ctlr *c;
       +
       +        c = s->ctlr;
       +        return seprint(p, e, "%s aoe %s\n", s->name, c->path);
       +}
       +
       +static int
       +aoewtopctl(SDev *d1, Cmdbuf *cmd)
       +{
       +        switch(cmd->nf){
       +        default:
       +                cmderror(cmd, Ebadarg);
       +        }
       +        return 0;
       +}
       +
       +SDifc sdaoeifc = {
       +        "aoe",
       +
       +        aoepnp,
       +        nil,                /* legacy */
       +        nil,                /* enable */
       +        nil,                /* disable */
       +
       +        aoeverify,
       +        aoeonline,
       +        aoerio,
       +        aoerctl,
       +        aoewctl,
       +
       +        scsibio,
       +        aoeprobew,        /* probe */
       +        aoeclear,        /* clear */
       +        aoertopctl,
       +        aoewtopctl,
       +};
 (DIR) diff --git a/src/9vx/etherve.c b/src/9vx/etherve.c
       @@ -0,0 +1,161 @@
       +/*
       + * etherve - portable Virtual Ethernet driver for 9vx.
       + * 
       + * Copyright (c) 2008 Devon H. O'Dell
       + * copyright © 2008 erik quanstrom
       + *
       + * Released under 2-clause BSD license.
       + */
       +
       +#include "u.h"
       +
       +#include "a/lib.h"
       +#include "a/mem.h"
       +#include "a/dat.h"
       +#include "a/fns.h"
       +#include "a/io.h"
       +#include "a/error.h"
       +#include "a/netif.h"
       +
       +#include "a/etherif.h"
       +
       +#include <pcap.h>
       +
       +extern        char        *netdev;
       +static        uvlong        txerrs;
       +
       +ttypedef struct Ctlr Ctlr;
       +struct Ctlr {
       +        pcap_t        *pd;
       +};
       +
       +static pcap_t *
       +setup(void)
       +{
       +        char        *filter = "ether dst 00:48:01:23:45:67";        /* XXX */
       +        char        errbuf[PCAP_ERRBUF_SIZE];
       +        pcap_t        *pd;
       +        struct bpf_program prog;
       +        bpf_u_int32 net;
       +        bpf_u_int32 mask;
       +
       +        if (!netdev)
       +                netdev = "en1"; /* XXX */
       +
       +        if ((pd = pcap_open_live(netdev, 1514, 1, 1, errbuf)) == nil)
       +                return nil;
       +
       +        pcap_lookupnet(netdev, &net, &mask, errbuf);
       +        pcap_compile(pd, &prog, filter, 0, net);
       +
       +        if (pcap_setfilter(pd, &prog) == -1)
       +                return nil;
       +
       +        pcap_freecode(&prog);
       +
       +        return pd;
       +}
       +
       +static Block *
       +vepkt(Ctlr *c)
       +{
       +        struct pcap_pkthdr hdr;
       +        Block *b;
       +
       +        if (hdr.caplen) {
       +                b = allocb(1514);
       +                while ((b->rp = pcap_next(c->pd, &hdr)) == nil) ;
       +
       +                b->wp += hdr.caplen;
       +
       +                // iprint("Got packet len %d\n", hdr.caplen);
       +
       +                return b;
       +        }
       +
       +        return nil;
       +}
       +
       +static void
       +verecvkproc(void *v)
       +{
       +        Ether *e;
       +        Block *b;
       +
       +        e = v;
       +        while (b = vepkt(e->ctlr)) 
       +                if (b != nil)
       +                        etheriq(e, b, 1);
       +}
       +
       +static void
       +vetransmit(Ether* e)
       +{
       +        const u_char *u;
       +        Block *b;
       +        Ctlr *c;
       +
       +        c = e->ctlr;
       +        while ((b = qget(e->oq)) != nil) {
       +                int wlen;
       +
       +                u = (const u_char*)b->rp;
       +
       +                wlen = pcap_inject(c->pd, u, BLEN(b));
       +                // iprint("injected packet len %d\n", wlen);
       +                if (wlen == -1)
       +                        txerrs++;
       +
       +                freeb(b);
       +        }
       +}
       +
       +static long
       +veifstat(Ether *e, void *a, long n, ulong offset)
       +{
       +        char buf[128];
       +
       +        snprint(buf, sizeof buf, "txerrors: %lud\n", txerrs);
       +        return readstr(offset, a, n, buf);
       +}
       +
       +static void
       +veattach(Ether* e)
       +{
       +        kproc("verecv", verecvkproc, e);
       +}
       +
       +static uchar ea[6] = {0x00, 0x48, 0x01, 0x23, 0x45, 0x67};
       +
       +static int
       +vepnp(Ether* e)
       +{
       +        Ctlr c;
       +        static int nctlr = 0;
       +
       +        if (nctlr++ > 0)
       +                return -1;
       +
       +        memset(&c, 0, sizeof(c));
       +        c.pd = setup();
       +        if (c.pd == nil) {
       +                iprint("ve: pcap failed to initialize\n");
       +                return -1;
       +        }
       +        e->ctlr = malloc(sizeof(c));
       +        memcpy(e->ctlr, &c, sizeof(c));
       +        e->tbdf = BUSUNKNOWN;
       +        memcpy(e->ea, ea, sizeof(ea));
       +        e->attach = veattach;
       +        e->transmit = vetransmit;
       +        e->ifstat = veifstat;
       +        e->ni.arg = e;
       +        e->ni.link = 1;
       +        return 0;
       +}
       +
       +void
       +ethervelink(void)
       +{
       +        addethercard("ve", vepnp);
       +}