<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/quanstro/etherm10g.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/quanstro/etherm10g.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
/*
*      myricom 10 gbit ethernet
*      © 2010 erik quanstrom, coraid, inc.
*/

#include "u.h"
#include "lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

#include "etherif.h"

#define         qlock(i)
#define         qunlock(i)
#define         wakeup(i)       while(0)

#define         K       * 1024
#define         MB      * 1024 K

#define dprint(...)     if(mdebug) print(__VA_ARGS__); else {}
#define pcicapdbg(...)  if(0) print(__VA_ARGS__); else {}
#define malign(n)       xspanalloc(n, 4 K, 0)
#define if64(...)               (sizeof(uintptr) == 8? (__VA_ARGS__): 0)
#define pbit32h(x)      if64(pbit32((uvlong)x &gt;&gt; 32))

enum {
       Epromsz = 256,
       Maxslots        = 256,                  /* 1024? */
       Rbalign = BY2PG,
       Noconf  = 0xffffffff,
       Fwoffset        = 1 MB,
       Hdroff  = 0x00003c,
       Cmdoff  = 0xf80000,             /* offset of command port */
       Fwsubmt = 0xfc0000,             /* offset of firmware submission command port */
       Rdmaoff = 0xfc01c0,             /* offset of rdma command port */
};

enum {
       CZero,
       Creset,
       Cversion,

       CSintrqdma,             /* issue these before Cetherup */
       CSbigsz,                        /* in bytes bigsize = 2^n */
       CSsmallsz,

       CGsendoff,
       CGsmallrxoff,
       CGbigrxoff,
       CGirqackoff,
       CGirqdeassoff,
       CGsendrgsz,
       CGrxrgsz,

       CSintrqsz,              /* 2^n */
       Cetherup,               /* above paramters + mtu/mac addr must be set first */
       Cetherdn,

       CSmtu,                  /* below may be issued live */
       CGcoaloff,              /* in µs */
       CSstatsrate,            /* in µs */
       CSstatsdma,

       Cpromisc,
       Cnopromisc,
       CSmac,

       Cenablefc,
       Cdisablefc,

       Cdmatest,

       Cenableallmc,
       Cdisableallmc,

       CSjoinmc,
       CSleavemc,
       Cleaveallmc,

       CSstatsdma2,
       Cdmatestu,
       Custatus,               /* unaligned status */
};

typedef union {
       uint    i[2];
       uchar   c[8];
} Cmd;

typedef struct {
       ushort  cksum;
       ushort  len;
} Slot;

enum {
       SFsmall = 1,
       SFfirst = 2,
       SFalign = 4,
       SFnotso = 16,
};

typedef struct {
       uint    high;
       uint    low;
       ushort  hdroff;
       ushort  len;
       union{
               struct {
                       uchar   pad;
                       uchar   nrdma;
                       uchar   chkoff;
                       uchar   flags;
               };
               uint    fword;  /* ha! */
       };
} Send;

typedef struct {
//      QLock;
       Send    *lanai;         /* tx ring (cksum + len in lanai memory) */
       Send    *host;          /* tx ring (data in our memory). */
       Block   **bring;
       int     size;           /* how big are the buffers in the z8's memory */
       uint    segsz;
       uint    n;              /* txslots */
       uint    m;              /* mask */
       uint    i;              /* number of segments (not frames) queued */
       uint    cnt;            /* number of segments sent by the card */
       uint    starve;
       uint    starvei;                /* starve pt */
       uint    submit;

       uint    npkt;
       vlong   nbytes;
} Tx;

enum {
       Pstarve = 1&lt;&lt;0,
};

typedef struct {
       Lock;
//      Block   *head;
       uint    size;           /* buffer size of each block */
       uint    n;              /* n free buffers. */
       uint    cnt;
//      uint    flags;
} Bpool;

typedef struct {
       Bpool   *pool;          /* free buffers */
       uint    *lanai;         /* rx ring; we have no perminant host shadow. */
       Block   **host;         /* called "info" in myricom driver */
       uint    m;
       uint    n;              /* rxslots */
       uint    i;
       uint    cnt;            /* number of buffers allocated (lifetime). */
} Rx;

/* dma mapped.  unix network byte order. */
typedef struct {
       uchar   unused[4];
       uchar   dpause[4];
       uchar   dufilt[4];
       uchar   dcrc32[4];
       uchar   dphy[4];
       uchar   dmcast[4];
       uchar   txcnt[4];
       uchar   linkstat[4];
       uchar   dlinkef[4];
       uchar   derror[4];
       uchar   drunt[4];
       uchar   doverrun[4];
       uchar   dnosm[4];
       uchar   dnobg[4];
       uchar   nrdma[4];
       uchar   txstopped;
       uchar   down;
       uchar   updated;
       uchar   valid;
} Stats;

enum {
       Detached,
       Attached,
       Runed,
};

typedef struct {
       uint    *entry;
       uintptr busaddr;
       uint    m;
       uint    n;
       uint    i;
} Done;

typedef struct Ctlr Ctlr;
typedef struct Ctlr {
//      QLock;
       int     state;
       int     kprocs;
       uintptr port;
       Pcidev* pcidev;
       Ctlr*   next;
       int     active;

       uchar   ra[Eaddrlen];

       int     ramsz;
       uchar   *ram;

       uint    *irqack;
       uint    *irqdeass;
       uint    *coal;

       char    eprom[Epromsz];
       uint    serial;                 /* unit serial number */

//      QLock   cmdl;
       void    *cmdl;
       Cmd     *cmd;                   /* address of command return */
       uintptr cprt;                   /* bus address of command */

       uintptr boot;                   /* boot address */

       Done    done;
       Tx      tx;
       Rx      sm;
       Rx      bg;
       Stats   *stats;
       uintptr statsprt;
       uint    speed[2];

//      Rendez  txrendez;
       int     txrendez;

       int     msi;
       uint    linkstat;
       uint    nrdma;
} Ctlr;

enum {
       PciCapPMG       = 0x01,         /* power management */
       PciCapAGP       = 0x02,
       PciCapVPD       = 0x03,         /* vital product data */
       PciCapSID       = 0x04,         /* slot id */
       PciCapMSI       = 0x05,
       PciCapCHS       = 0x06,         /* compact pci hot swap */
       PciCapPCIX      = 0x07,
       PciCapHTC       = 0x08,         /* hypertransport irq conf */
       PciCapVND       = 0x09,         /* vendor specific information */
       PciCapPCIe      = 0x10,
       PciCapMSIX      = 0x11,
       PciCapSATA      = 0x12,
       PciCapHSW       = 0x0C,         /* hot swap */
};

enum {
       PcieAERC        = 1,
       PcieVC,
       PcieSNC,
       PciePBC,
};

enum {
       AercCCR = 0x18,         /* control register */
};

enum {
       PcieCTL         = 8,
       PcieLCR         = 12,
       PcieMRD = 0x7000,       /* maximum read size */
};

static  int     mdebug;
static  Bpool   smpool  = {.size        = 2048, };
static  Bpool   bgpool  = {.size = 2048,};
static  Ctlr    *ctlrs;

static int
pcicap(Pcidev *p, int cap)
{
       int i, c, off;

       pcicapdbg("pcicap: %x:%d\n", p-&gt;vid, p-&gt;did);
       off = 0x34;     /* 0x14 for cardbus. */
       for(i = 48; i--;){
               pcicapdbg("\t" "loop %x\n", off);
               off = pcicfgr8(p, off);
               pcicapdbg("\t" "pcicfgr8 %x\n", off);
               if(off &lt; 0x40)
                       break;
               off &amp;= ~3;
               c = pcicfgr8(p, off);
               pcicapdbg("\t" "pcicfgr8 %x\n", c);
               if(c == 0xff)
                       break;
               if(c == cap)
                       return off;
               off++;
       }
       return 0;
}

static int
parseeprom(Ctlr *c)
{
       int i, j, k, l, bits;
       char *s;

       dprint("m10g eprom:\n");
       s = c-&gt;eprom;
       bits = 3;
       for(i = 0; s[i] &amp;&amp; i &lt; Epromsz; i++){
               l = strlen(s + i);
               dprint("\t%s\n", s + i);
               if(strncmp(s + i, "MAC=", 4) == 0 &amp;&amp; l == 21){
                       bits ^= 1;
                       j = i + 4;
                       for(k = 0; k &lt; 6; k++)
                               c-&gt;ra[k] = strtoul(s + j + 3*k, 0, 16);
               }else if(strncmp(s + i, "SN=", 3) == 0){
                       bits ^= 2;
                       c-&gt;serial = atoi(s + i + 3);
               }
               i += l;
       }
       if(bits)
               return -1;
       return 0;
}

static ushort
pbit16(ushort i)
{
       ushort j;
       uchar *p;

       p = (uchar*)&amp;j;
       p[1] = i;
       p[0] = i&gt;&gt;8;
       return j;
}

static ushort
gbit16(uchar i[2])
{
       ushort j;

       j = i[1];
       j |= i[0]&lt;&lt;8;
       return j;
}

static uint
pbit32(uint i)
{
       uint j;
       uchar *p;

       p = (uchar*)&amp;j;
       p[3] = i;
       p[2] = i&gt;&gt;8;
       p[1] = i&gt;&gt;16;
       p[0] = i&gt;&gt;24;
       return j;
}

static uint
gbit32(uchar i[4])
{
       uint j;

       j = i[3];
       j |= i[2]&lt;&lt;8;
       j |= i[1]&lt;&lt;16;
       j |= i[0]&lt;&lt;24;
       return j;
}

static void
prepcmd(uint *cmd, int i)
{
       while(i-- &gt; 0)
               cmd[i] = pbit32(cmd[i]);
}

/*
* the command looks like this (int 32bit integers)
* cmd type
* data0 (or, addr low; endian backwards)
* data1 (addr high)
* data2
* response (high)
* response (low)
* 40 byte = 5 int pad.
*/

static uint
cmd(Ctlr *c, int type, int sz, uvlong data)
{
       uint buf[16], i;
       Cmd *cmd;

       qlock(&amp;c-&gt;cmdl);
       cmd = c-&gt;cmd;
       cmd-&gt;i[1] = Noconf;
       memset(buf, 0, sizeof buf);
       buf[0] = type;
       buf[1] = data;
       buf[2] = data&gt;&gt;32;
       buf[3] = sz;
       buf[4] = (uvlong)c-&gt;cprt&gt;&gt;32;
       buf[5] = c-&gt;cprt;
       prepcmd(buf, 6);
       coherence();
       memmove(c-&gt;ram + Cmdoff, buf, sizeof buf);

       for(i = 0; i &lt; 15; i++){
               if(cmd-&gt;i[1] != Noconf){
                       i = gbit32(cmd-&gt;c);
                       qunlock(&amp;c-&gt;cmdl);
                       if(cmd-&gt;i[1] != 0)
                               dprint("[%ux]", i);
                       return i;
               }
               delay(1);
       }
       qunlock(&amp;c-&gt;cmdl);
       print("m10g: cmd timeout [%ux %ux] cmd=%d\n", cmd-&gt;i[0], cmd-&gt;i[1], type);
       return ~0;
}

static uint
maccmd(Ctlr *c, int type, uchar *m)
{
       uint buf[16], i;
       Cmd * cmd;

       qlock(&amp;c-&gt;cmdl);
       cmd = c-&gt;cmd;
       cmd-&gt;i[1] = Noconf;
       memset(buf, 0, sizeof buf);
       buf[0] = type;
       buf[1] = m[0]&lt;&lt;24 | m[1]&lt;&lt;16 | m[2]&lt;&lt;8 | m[3];
       buf[2] = m[4]&lt;&lt;8 | m[5];
       buf[4] = (uvlong)c-&gt;cprt&gt;&gt;32;
       buf[5] = c-&gt;cprt;
       prepcmd(buf, 6);
       coherence();
       memmove(c-&gt;ram + Cmdoff, buf, sizeof buf);

       for(i = 0; i &lt; 15; i++){
               if(cmd-&gt;i[1] != Noconf){
                       i = gbit32(cmd-&gt;c);
                       qunlock(&amp;c-&gt;cmdl);
                       if(cmd-&gt;i[1] != 0)
                               dprint("[%ux]", i);
                       return i;
               }
               delay(1);
       }
       qunlock(&amp;c-&gt;cmdl);
       print("m10g: maccmd timeout [%ux %ux] cmd=%d\n", cmd-&gt;i[0], cmd-&gt;i[1], type);
       return ~0;
}

static uint
rdmacmd(Ctlr *c, int on)
{
       uint buf[16], i;

       memset(buf, 0, sizeof buf);
       c-&gt;cmd-&gt;i[0] = 0;
       coherence();
       buf[0] = (uvlong)c-&gt;cprt&gt;&gt;32;
       buf[1] = c-&gt;cprt;
       buf[2] = Noconf;
       buf[3] = (uvlong)c-&gt;cprt&gt;&gt;32;
       buf[4] = c-&gt;cprt;
       buf[5] = on;
       prepcmd(buf, 6);
       memmove(c-&gt;ram + Rdmaoff, buf, sizeof buf);

       for(i = 0; i &lt; 20; i++){
               if(c-&gt;cmd-&gt;i[0] == Noconf){
print("rdmacmd(%d) completed %d %ux\n", on, i, gbit32(c-&gt;cmd-&gt;c));
                       return gbit32(c-&gt;cmd-&gt;c);
               }
               delay(1);
       }
       print("m10g: rdmacmd timeout\n");
       return ~0;
}

static int
kickthebaby(Pcidev *p, Ctlr *c)
{
       /* don't kick the baby! */
       uint code;

       pcicfgw8(p, 0x10 + c-&gt;boot, 0x3);
       pcicfgw32(p, 0x18 + c-&gt;boot, 0xfffffff0);
       code = pcicfgr32(p, 0x14 + c-&gt;boot);

       dprint("m10g: reboot status = %ux\n", code);
       if(code != 0xfffffff0)
               return -1;
       return 0;
}

typedef struct{
       uchar   len[4];
       uchar   type[4];
       char    version[128];
       uchar   globals[4];
       uchar   ramsz[4];
       uchar   specs[4];
       uchar   specssz[4];
       uchar   idx;
       uchar   norabbit;
       uchar   unaligntlp;
       uchar   pcilinkalg;
       uchar   cntaddr[4];
       uchar   cbinfo[4];
       uchar   handoid[2];
       uchar   handocap[2];
       uchar   msixtab[4];
       uchar   bss[4];
       uchar   features[4];
       uchar   eehdr[4];
} Fwhdr;

enum{
       Tmx     = 0x4d582020,
       Tpcie   = 0x70636965,
       Teth    = 0x45544820,
       Tmcp0   = 0x4d435030,
};

static char*
fwtype(uint type)
{
       switch(type){
       case Tmx:
               return "mx";
       case Tpcie:
               return "PCIe";
       case Teth:
               return "eth";
       case Tmcp0:
               return "mcp0";
       }
       return "*GOK*";
}

static int
chkfw(Ctlr *c)
{
       uint off, type;
       Fwhdr *h;

       off = gbit32(c-&gt;ram + Hdroff);
       dprint("m10g: firmware %ux\n", off);
       if(off == 0 || off&amp;3 || off + sizeof *h &gt;= c-&gt;ramsz){
               print("m10g: bad firmware %#ux\n", off);
               return -1;
       }
       h = (Fwhdr*)(c-&gt;ram + off);
       type = gbit32(h-&gt;type);
       dprint("\t" "type       %s\n", fwtype(type));
       dprint("\t" "vers       %s\n", h-&gt;version);
       dprint("\t" "ramsz      %ux\n", gbit32(h-&gt;ramsz));
       if(type != Teth){
               print("m10g: bad card type %s\n", fwtype(type));
               return -1;
       }
       rdmacmd(c, 0);
return 0;
}

static int
reset(Ether*, Ctlr *c)
{
       if(chkfw(c) == -1){
err:
               print("m10g: reset error\n");
               return -1;
       }
       if(cmd(c, Creset, 0, 0) == ~0){
               print("reset fails\n");
               goto err;
       }

       if(cmd(c, CSintrqsz, 0, c-&gt;done.n*sizeof *c-&gt;done.entry) == ~0)
               goto err;
       if(cmd(c, CSintrqdma, 0, c-&gt;done.busaddr) == ~0)
               goto err;
       c-&gt;irqack = (uint*)(c-&gt;ram + cmd(c, CGirqackoff, 0, 0));
       c-&gt;irqdeass = (uint*)(c-&gt;ram + cmd(c, CGirqdeassoff, 0, 0));
       c-&gt;coal = (uint*)(c-&gt;ram + cmd(c, CGcoaloff, 0, 0));
       *c-&gt;coal = pbit32(20);

       if(rdmacmd(c, 1) == ~0)
               goto err;
       memset(c-&gt;done.entry, 0, c-&gt;done.n*sizeof *c-&gt;done.entry);

       if(maccmd(c, CSmac, c-&gt;ra) == ~0)
               goto err;
       if(cmd(c, Cenablefc, 0, 0) == ~0)
               goto err;
       if(cmd(c, CSmtu, 0, 2048) == ~0)
               goto err;
       return 0;
}

static void
ctlrfree(Ctlr *c)
{
       /* free up all the Block*s, too; tricky */
       free(c-&gt;tx.host);
       free(c-&gt;sm.host);
       free(c-&gt;bg.host);
       free(c-&gt;cmd);
       free(c-&gt;done.entry);
       free(c-&gt;stats);
       free(c);
       USED(c);
}

static int
setmem(Pcidev *p, Ctlr *c)
{
       uint i;
       uintptr raddr;
       ulong mem;
       Done *d;

       c-&gt;tx.segsz = 2048;
       c-&gt;ramsz = 2 MB - (2*48 K + 32 K) - 0x100;
       if(c-&gt;ramsz &gt; p-&gt;mem[0].size)
               return -1;

       raddr = p-&gt;mem[0].bar &amp; ~0x0F;
       mem = upamalloc(raddr, p-&gt;mem[0].size, 0);
       if(mem == 0){
               print("m10g: can't map %p %ud\n", raddr, p-&gt;mem[0].size);
               return -1;
       }
       c-&gt;port = raddr;
       c-&gt;ram = (void*)mem;
       c-&gt;cmd = malign(sizeof *c-&gt;cmd);
       c-&gt;cprt = PCIWADDR(c-&gt;cmd);

       d = &amp;c-&gt;done;
       d-&gt;n = Maxslots;
       d-&gt;m = d-&gt;n - 1;
       i = d-&gt;n*sizeof *d-&gt;entry;
       d-&gt;entry = malign(i);
       memset(d-&gt;entry, 0, i);
       d-&gt;busaddr = PCIWADDR(d-&gt;entry);

       c-&gt;stats = malign(sizeof *c-&gt;stats);
       memset(c-&gt;stats, 0, sizeof *c-&gt;stats);
       c-&gt;statsprt = PCIWADDR(c-&gt;stats);

       memmove(c-&gt;eprom, c-&gt;ram + c-&gt;ramsz - Epromsz, Epromsz - 2);
       return parseeprom(c);
}

/*
* this is highly optimized to reduce bus cycles with
* w/c memory while respecting the lanai z model a's
* limit of 32-bytes writes &gt; 32 bytes must be handled
* by card f/w.  partial writes are also handled by f/w.
*/

static void
replenish(Rx *rx)
{
       uint buf[16], i, idx, e, f;
       Block *b;

       e = (rx-&gt;i - rx-&gt;cnt) &amp; ~7;
       e += rx-&gt;n;
       ilock(rx-&gt;pool);
       while(e){
               idx = rx-&gt;cnt &amp; rx-&gt;m;
               for(i = 0; i &lt; 8; i++){
                       b = allocb(2048);
                       buf[i*2 + 0] = pbit32h(PCIWADDR(b-&gt;wp));
                       buf[i*2 + 1] = pbit32(PCIWADDR(b-&gt;wp));
                       rx-&gt;host[idx + i] = b;
               }
               f = buf[1];
               buf[1] = ~0;
               memmove(rx-&gt;lanai + 2*idx, buf, sizeof buf / 2);
               coherence();
               memmove(rx-&gt;lanai + 2*(idx + 4), buf + 8, sizeof buf / 2);
               rx-&gt;lanai[2*idx + 1] = f;
               coherence();
               rx-&gt;cnt += 8;
               e -= 8;
       }
       iunlock(rx-&gt;pool);
}

static int
nextpow(int j)
{
       int i;

       for(i = 0; j &gt; 1&lt;&lt;i; i++)
               ;
       return 1&lt;&lt;i;
}

static void*
emalign(int sz)
{
       void *v;

       v = malign(sz);
       if(v == 0)
               panic("m10g: no memory");
       memset(v, 0, sz);
       return v;
}

static void
open0(Ether *, Ctlr *c)
{
       int entries;

       entries = cmd(c, CGsendrgsz, 0, 0)/sizeof *c-&gt;tx.lanai;
       c-&gt;tx.lanai = (Send*)(c-&gt;ram + cmd(c, CGsendoff, 0, 0));
       c-&gt;tx.host = emalign(entries*sizeof *c-&gt;tx.host);
       c-&gt;tx.bring = emalign(entries*sizeof *c-&gt;tx.bring);
       c-&gt;tx.n = entries;
       c-&gt;tx.m = entries - 1;

       entries = cmd(c, CGrxrgsz, 0, 0)/8;
       c-&gt;sm.pool = &amp;smpool;
       cmd(c, CSsmallsz, 0, c-&gt;sm.pool-&gt;size);
       c-&gt;sm.lanai = (uint*)(c-&gt;ram + cmd(c, CGsmallrxoff, 0, 0));
       c-&gt;sm.n = entries;
       c-&gt;sm.m = entries - 1;
       c-&gt;sm.host = emalign(entries*sizeof *c-&gt;sm.host);

       c-&gt;bg.pool = &amp;bgpool;
       c-&gt;bg.pool-&gt;size =  nextpow(1500 + 2);    /* 2 byte alignment pad */
       cmd(c, CSbigsz, 0, c-&gt;bg.pool-&gt;size);
       c-&gt;bg.lanai = (uint*)(c-&gt;ram + cmd(c, CGbigrxoff, 0, 0));
       c-&gt;bg.n = entries;
       c-&gt;bg.m = entries - 1;
       c-&gt;bg.host = emalign(entries*sizeof *c-&gt;bg.host);

       cmd(c, CSstatsdma2, sizeof *c-&gt;stats, c-&gt;statsprt);
       c-&gt;linkstat = ~0;
       c-&gt;nrdma = 15;

       cmd(c, Cetherup, 0, 0);
}

static Rx*
whichrx(Ctlr *c, int sz)
{
       if(sz &lt;= smpool.size)
               return &amp;c-&gt;sm;
       return &amp;c-&gt;bg;
}

static Block*
nextblock(Ctlr *c)
{
       uint i;
       ushort l/*, k*/;
       Slot *s;
       Done *d;
       Block *b;
       Rx *rx;

       d = &amp;c-&gt;done;
       i = d-&gt;i&amp;d-&gt;m;
       s = (Slot*)(d-&gt;entry + i);
       l = s-&gt;len;
       if(l == 0)
               return 0;
//      k = s-&gt;cksum;
       s-&gt;len = 0;
       d-&gt;i++;
       l = gbit16((uchar*)&amp;l);
       rx = whichrx(c, l);
       if(rx-&gt;i - rx-&gt;cnt &lt;= rx-&gt;n){
               print("m10g: overrun\n");
               return 0;
       }
       i = rx-&gt;i&amp;rx-&gt;m;
       b = rx-&gt;host[i];
       rx-&gt;host[i] = 0;
       if(b == 0)
               panic("m10g: rx to no block");
       rx-&gt;i++;

//      b-&gt;flag |= Bipck|Btcpck|Budpck;
//      b-&gt;checksum = k;
       b-&gt;rp += 2;
       b-&gt;wp += 2 + l;
       b-&gt;lim = b-&gt;wp;           /* lie like a dog */
       return b;
}

static void
etheriq(Ether *e, Block *b, int)
{
       toringbuf(e, b-&gt;rp, BLEN(b));
       freeb(b);
}

static void
irqrx(Ether *e)
{
       Block *b;
       Ctlr *c;

       c = e-&gt;ctlr;

       replenish(&amp;c-&gt;sm);
//      replenish(&amp;c-&gt;bg);
       while(b = nextblock(c))
               etheriq(e, b, 1);
       c-&gt;irqack[0] = pbit32(3);
}

static uint
txstarving(Tx *tx, uint u)
{
       uint d;

       d = tx-&gt;n - (tx-&gt;i - tx-&gt;cnt);
       return d &lt;= u;
}

static int
txcleanup(Tx *tx, uint n)
{
       uint j, l;
       Block *b;

       for(l = 0; l &lt; tx-&gt;m; l++){
               if(tx-&gt;npkt == n)
                       break;
               if(tx-&gt;cnt == tx-&gt;i){
                       dprint("m10g: txcleanup cnt == i %ud\n", tx-&gt;i);
                       break;
               }
               j = tx-&gt;cnt &amp; tx-&gt;m;
               if(b = tx-&gt;bring[j]){
                       tx-&gt;bring[j] = 0;
                       tx-&gt;nbytes += BLEN(b);
                       freeb(b);
                       tx-&gt;npkt++;
               }
               tx-&gt;cnt++;
       }
       if(l == 0 &amp;&amp; !tx-&gt;starve)
               dprint("m10g: spurious cleanup\n");
       if(l &gt;= tx-&gt;m)
               print("m10g: tx ovrun: %ud %ud\n", n, tx-&gt;npkt);
       if(tx-&gt;starve &amp;&amp; !txstarving(tx, tx-&gt;n/2)){
               tx-&gt;starve = 0;
               return 1;
       }
       return 0;
}

static int
txcansleep(void *v)
{
       Ctlr *c;

       c = v;
       if(c-&gt;tx.starve == 0)
               return -1;
       return 0;
}

static void
submittx(Tx *tx, int n)
{
       int i0, i, m;
       uint v;
       Send *l, *h;

       m = tx-&gt;m;
       i0 = tx-&gt;i&amp;m;
       l = tx-&gt;lanai;
       h = tx-&gt;host;
       v = h[i0].fword;
       h[i0].flags = 0;
       for(i = n - 1; i &gt;= 0; i--)
               memmove(l+(i+i0&amp;m), h+(i+i0&amp;m), sizeof *h);
       coherence();
       l[i0].fword = v;
       tx-&gt;i += n;
       coherence();
}

static Block*
rbget(Ether *e)
{
       RingBuf *r;
       Block *b;

       r = e-&gt;tb + e-&gt;ti;
       if(r-&gt;owner != Interface)
               return nil;
       b = fromringbuf(e);
       r-&gt;owner = Host;
       e-&gt;ti = NEXT(e-&gt;ti, e-&gt;ntb);
       return b;
}

static void
m10gtransmit(Ether *e)
{
       uchar flags;
       ushort slen;
       uint nseg, end, bus, len, segsz;
       Ctlr *c;
       Block *b;
       Tx *tx;
       Send *s0, *s, *se;

       c = e-&gt;ctlr;
       tx = &amp;c-&gt;tx;
       segsz = tx-&gt;segsz;
       s = tx-&gt;host + (tx-&gt;i&amp;tx-&gt;m);
       se = tx-&gt;host + tx-&gt;n;
       for(;;){
               if(txstarving(tx, 16)){
                       tx-&gt;starvei = tx-&gt;i;
                       tx-&gt;starve = 1;
                       continue;
               }
               if((b = rbget(e)) == nil)
                       break;
               flags = SFfirst|SFnotso;
               len = BLEN(b);
               if(len &lt; 1520)
                       flags |= SFsmall;
               bus = PCIWADDR(b-&gt;rp);
               s0 = s;
               nseg = 0;
               for(; len; len -= slen){
                       end = bus+segsz &amp; ~(segsz-1);
                       slen = end - bus;
                       if(slen &gt; len)
                               slen = len;
                       s-&gt;low = pbit32(bus);
                       s-&gt;high = pbit32h(bus);
                       s-&gt;len = pbit16(slen);
                       s-&gt;flags = flags;
                       s-&gt;nrdma = 1;

                       bus += slen;
                       if(++s == se)
                               s = tx-&gt;host;
                       flags &amp;= ~SFfirst;
                       nseg++;
               }
               s0-&gt;nrdma = nseg;
               tx-&gt;bring[tx-&gt;i+nseg-1 &amp; tx-&gt;m] = b;
               submittx(tx, nseg);
               tx-&gt;submit++;
       }
}

static void
checkstats(Ether *, Ctlr *c, Stats *s)
{
       uint i;

       if(s-&gt;updated == 0)
               return;

       i = gbit32(s-&gt;linkstat);
       if(c-&gt;linkstat != i){
               c-&gt;speed[i&gt;0]++;
               if(c-&gt;linkstat = i){
                       dprint("m10g: link up\n");
                       c-&gt;tx.starve = 0;
                       wakeup(&amp;c-&gt;txrendez);
               }else
                       dprint("m10g: link down\n");
       }
       i = gbit32(s-&gt;nrdma);
       if(i != c-&gt;nrdma){
               dprint("m10g: rdma timeout %d\n", i);
               c-&gt;nrdma = i;
       }
}

static void
waitintx(Ctlr *c)
{
       int i, n;

       for(i = 0; i &lt; 1048576; i++){
               coherence();
               n = gbit32(c-&gt;stats-&gt;txcnt);
               if(n != c-&gt;tx.npkt || c-&gt;tx.starve)
                       if(txcleanup(&amp;c-&gt;tx, n))
                               wakeup(&amp;c-&gt;txrendez);
               if(c-&gt;stats-&gt;valid == 0)
                       break;
       }
}

static void
m10ginterrupt(Ureg *, void *v)
{
       int valid;
       Ctlr *c;
       Ether *e;

       e = v;
       c = e-&gt;ctlr;

       valid = c-&gt;stats-&gt;valid;
//print("m10gi: %d &amp;&amp; %d\n", c-&gt;state == Runed, valid);
       if(c-&gt;state != Runed || valid == 0)
               return;
       if(c-&gt;msi == 0)
               *c-&gt;irqdeass = 0;
       else
               c-&gt;stats-&gt;valid = 0;
       waitintx(c);
       checkstats(e, c, c-&gt;stats);
       c-&gt;irqack[1] = pbit32(3);
       if(valid&amp;1)
               irqrx(e);
}

static void
m10gattach(Ether *e)
{
       Ctlr *c;

       dprint("m10g: attach\n");
       qlock(e-&gt;ctlr);
       c = e-&gt;ctlr;
       if(c-&gt;state != Detached){
               qunlock(c);
               return;
       }
       if(reset(e, c) == -1){
               c-&gt;state = Detached;
               return;
       }
       c-&gt;state = Attached;
       open0(e, c);
       c-&gt;state = Runed;
       qunlock(c);
}

static int
m10gdetach(Ctlr *c)
{
       Ctlr *p;

       cmd(c, Creset, 0, 0);
       if(c == ctlrs)
               ctlrs = c-&gt;next;
       else{
               for(p = ctlrs; p-&gt;next; p = p-&gt;next)
                       if(p-&gt;next == c)
                               break;
               p-&gt;next = c-&gt;next;
       }
//      vunmap(c-&gt;ram, c-&gt;pcidev-&gt;mem[0].size);
       ctlrfree(c);
       return -1;
}

static void
m10gshutdown(Ether *e)
{
       m10gdetach(e-&gt;ctlr);
}

static void
m10gpci(void)
{
       Ctlr **t, *c;
       Pcidev *p;

       t = &amp;ctlrs;
       for(p = 0; p = pcimatch(p, 0x14c1, 0x0008); ){
               c = malloc(sizeof *c);
               if(c == nil)
                       continue;
               c-&gt;pcidev = p;
       //      c-&gt;boot = pcicap(p, PciCapVND);
       //      kickthebaby(p, c);
               pcisetbme(p);
               if(setmem(p, c) == -1 || reset(nil, c) == -1){
                       print("m10g: init failed\n");
                       free(c);
                       continue;
               }
               *t = c;
               t = &amp;c-&gt;next;
       }
}

/*static*/ int
m10gpnp(Ether *e)
{
       Ctlr *c;
       static int once;

       if(once == 0){
               once++;
               m10gpci();
       }
       for(c = ctlrs; c != nil; c = c-&gt;next)
               if(c-&gt;active)
                       continue;
               else if(e-&gt;port == 0 || e-&gt;port == c-&gt;port)
                       break;
       if(c == nil)
               return -1;
       c-&gt;active = 1;

       e-&gt;ctlr = c;
       e-&gt;port = c-&gt;port;
       e-&gt;irq = c-&gt;pcidev-&gt;intl;
       e-&gt;tbdf = c-&gt;pcidev-&gt;tbdf;
       e-&gt;mbps = 10000;
       memmove(e-&gt;ea, c-&gt;ra, Eaddrlen);

       e-&gt;attach = m10gattach;
       e-&gt;detach = m10gshutdown;
       e-&gt;transmit = m10gtransmit;
       e-&gt;interrupt = m10ginterrupt;
       e-&gt;detach = m10gshutdown;

       return 0;
}

//void
//etherm10glink(void)
//{
//      addethercard("m10g", m10gpnp);
//}
<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:[email protected]">[email protected]</a>.</span>
</font></center>
</body>
</html>