commit 8aa03c4f1dc493f3a62bc88eb7d0c1ac26e24cea
parent 99843ed18b65e16b10f49b73956f7bfd9c495aac
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(-)
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
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,
+};
+
+typedef 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;
+
+typedef 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;
+
+typedef 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[];
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",
+};
+
+typedef 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;
+
+typedef struct {
+ Netlink *nl;
+ int nea;
+ ulong eaidx;
+ uchar eatab[Nea][Eaddrlen];
+ int datamtu;
+ ulong npkt;
+ ulong resent;
+ uchar flag;
+
+ ulong rttavg;
+ ulong mintimer;
+} Devlink;
+
+typedef 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;
+};
+
+typedef 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;
+
+typedef 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
+tsince(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
+topgen(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
+topctlread(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
+toggle(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
+topctlwrite(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,
+};
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(ðerxx[chan->dev]->ni, chan, nchan, name, nname);
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+ return netifstat(ðerxx[chan->dev]->ni, chan, dp, n);
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+ return netifopen(ðerxx[chan->dev]->ni, chan, omode);
+}
+
+static void
+ethercreate(Chan* ch, char* c, int i, ulong ul)
+{
+}
+
+static void
+etherclose(Chan* chan)
+{
+ netifclose(ðerxx[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(ðer->ni, chan, buf, n, offset);
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+ return netifbread(ðerxx[chan->dev]->ni, chan, n, offset);
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+ return netifwstat(ðerxx[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 = ðer->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(ðer->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(ðer->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, ðer->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(ðer->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,
+};
diff --git a/src/9vx/a/etherif.h b/src/9vx/a/etherif.h
@@ -0,0 +1,39 @@
+enum {
+ MaxEther = 48,
+ Ntypes = 8,
+};
+
+typedef 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))
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
+typeinuse(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;
+}
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",
+};
+
+typedef 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,
+};
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;
+
+typedef 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);
+}