<?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/nemo/e.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/nemo/e.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 -->
/*
* USB Enhanced Host Controller Interface (EHCI) driver
* High speed USB 2.0.
*
* BUGS:
* - Too many delays and ilocks.
* - bandwidth admission control must be done per-frame.
* - requires polling (some controllers miss interrupts).
* - must warn of power overruns.
*/

#include        "u.h"
#include        "../port/lib.h"
#include        "mem.h"
#include        "dat.h"
#include        "fns.h"
#include        "io.h"
#include        "../port/error.h"
#include        "usb.h"
#include        "usbehci.h"

typedef struct Ctlio Ctlio;
typedef struct Ctlr Ctlr;
typedef struct Itd Itd;
typedef struct Sitd Sitd;
typedef struct Qtd Qtd;
typedef struct Td Td;
typedef struct Qh Qh;
typedef struct Fstn Fstn;
typedef union Ed Ed;
typedef struct Edpool Edpool;
typedef struct Qio Qio;
typedef struct Qtree Qtree;
typedef struct Isoio Isoio;
typedef struct Poll Poll;

/*
* EHCI interface registers and bits
*/
enum
{
       /* Queue states (software) */
       Qidle           = 0,
       Qinstall,
       Qrun,
       Qdone,
       Qclose,
       Qfree,

       Enabledelay     = 100,          /* waiting for a port to enable */
       Abortdelay      = 5,            /* delay after cancelling Tds (ms) */

       Incr            = 64,           /* for pools of Tds, Qhs, etc. */
       Align           = 128,          /* in bytes for all those descriptors */

       /* Keep them as a power of 2, lower than ctlr-&gt;nframes */
       /* Also, keep Nisoframes &gt;= Nintrleafs */
       Nintrleafs      = 32,           /* nb. of leaf frames in intr. tree */
       Nisoframes      = 64,           /* nb. of iso frames (in window) */

       /*
        * HW constants
        */

       /* Itd bits (csw[]) */
       Itdactive       = 0x80000000,   /* execution enabled */
       Itddberr        = 0x40000000,   /* data buffer error */
       Itdbabble       = 0x20000000,   /* babble error */
       Itdtrerr        = 0x10000000,   /* transaction error */
       Itdlenshift     = 16,           /* transaction length */
       Itdlenmask      = 0xFFF,
       Itdioc          = 0x00008000,   /* interrupt on complete */
       Itdpgshift      = 12,           /* page select field */
       Itdoffshift     = 0,            /* transaction offset */
       /* Itd bits, buffer[] */
       Itdepshift      = 8,            /* endpoint address (buffer[0]) */
       Itddevshift     = 0,            /* device address (buffer[0]) */
       Itdin           = 0x800,        /* is input (buffer[1]) */
       Itdout          = 0,
       Itdmaxpktshift  = 0,            /* max packet (buffer[1]) */
       Itdntdsshift    = 0,            /* nb. of tds per µframe (buffer[2]) */

       Itderrors       = Itddberr|Itdbabble|Itdtrerr,

       /* Sitd bits (epc) */
       Stdin           = 0x80000000,   /* input direction */
       Stdportshift    = 24,           /* hub port number */
       Stdhubshift     = 16,           /* hub address */
       Stdepshift      = 8,            /* endpoint address */
       Stddevshift     = 0,            /* device address */
       /* Sitd bits (mfs) */
       Stdssmshift     = 0,            /* split start mask */
       Stdscmshift     = 8,            /* split complete mask */
       /* Sitd bits (csw) */
       Stdioc          = 0x80000000,   /* interrupt on complete */
       Stdpg           = 0x40000000,   /* page select */
       Stdlenshift     = 16,           /* total bytes to transfer */
       Stdlenmask      = 0x3FF,
       Stdactive       = 0x00000080,   /* active */
       Stderr          = 0x00000040,   /* tr. translator error */
       Stddberr        = 0x00000020,   /* data buffer error */
       Stdbabble       = 0x00000010,   /* babble error */
       Stdtrerr        = 0x00000008,   /* transanction error */
       Stdmmf          = 0x00000004,   /* missed µframe */
       Stddcs          = 0x00000002,   /* do complete split */

       Stderrors       = Stderr|Stddberr|Stdbabble|Stdtrerr|Stdmmf,

       /* Sitd bits buffer[1] */
       Stdtpall        = 0x00000000,   /* all payload here (188 bytes) */
       Stdtpbegin      = 0x00000008,   /* first payload for fs trans. */
       Stdtcntmask     = 0x00000007,   /* T-count */

       /* Td bits (csw) */
       Tddata1         = 0x80000000,   /* data toggle 1 */
       Tddata0         = 0x00000000,   /* data toggle 0 */
       Tdlenshift      = 16,           /* total bytes to transfer */
       Tdlenmask       = 0x7FFF,
       Tdmaxpkt        = 0x5000,       /* max buffer for a Td */
       Tdioc           = 0x00008000,   /* interrupt on complete */
       Tdpgshift       = 12,           /* current page */
       Tdpgmask        = 7,
       Tderr1          = 0x00000400,   /* bit 0 of error counter */
       Tderr2          = 0x00000800,   /* bit 1 of error counter */
       Tdtokout        = 0x00000000,   /* direction out */
       Tdtokin         = 0x00000100,   /* direction in */
       Tdtoksetup      = 0x00000200,   /* setup packet */
       Tdtok           = 0x00000300,   /* token bits */
       Tdactive                = 0x00000080,   /* active */
       Tdhalt          = 0x00000040,   /* halted */
       Tddberr         = 0x00000020,   /* data buffer error */
       Tdbabble        = 0x00000010,   /* babble error */
       Tdtrerr         = 0x00000008,   /* transanction error */
       Tdmmf           = 0x00000004,   /* missed µframe */
       Tddcs           = 0x00000002,   /* do complete split */
       Tdping          = 0x00000001,   /* do ping */

       Tderrors        = Tdhalt|Tddberr|Tdbabble|Tdtrerr|Tdmmf,

       /* Qh bits (eps0) */
       Qhrlcmask       = 0xF,          /* nak reload count */
       Qhrlcshift      = 28,           /* nak reload count */
       Qhnhctl         = 0x08000000,   /* not-high speed ctl */
       Qhmplmask       = 0x7FF,        /* max packet */
       Qhmplshift      = 16,
       Qhhrl           = 0x00008000,   /* head of reclamation list */
       Qhdtc           = 0x00004000,   /* data toggle ctl. */
       Qhint           = 0x00000080,   /* inactivate on next transition */
       Qhspeedmask     = 0x00003000,   /* speed bits */
       Qhfull          = 0x00000000,   /* full speed */
       Qhlow           = 0x00001000,   /* low speed */
       Qhhigh          = 0x00002000,   /* high speed */

       /* Qh bits (eps1) */
       Qhmultshift     = 30,           /* multiple tds per µframe */
       Qhmultmask      = 3,
       Qhportshift     = 23,           /* hub port number */
       Qhhubshift      = 16,           /* hub address */
       Qhscmshift      = 8,            /* split completion mask bits */
       Qhismshift      = 0,            /* interrupt sched. mask bits */
};

/*
* Endpoint tree (software)
*/
struct Qtree
{
       int     nel;
       int     depth;
       ulong*  bw;
       Qh**    root;
};

/*
* One per endpoint per direction, to control I/O.
*/
struct Qio
{
       QLock;                  /* for the entire I/O process */
       Rendez;                 /* wait for completion */
       Qh*     qh;             /* Td list (field const after init) */
       int     usbid;          /* usb address for endpoint/device */
       int     toggle;         /* Tddata0/Tddata1 */
       int     tok;            /* Tdtoksetup, Tdtokin, Tdtokout */
       ulong   iotime;         /* last I/O time; to hold interrupt polls */
       int     debug;          /* debug flag from the endpoint */
       char*   err;            /* error string */
       char*   tag;            /* debug (no room in Qh for this) */
       ulong   bw;
};

struct Ctlio
{
       Qio;                    /* a single Qio for each RPC */
       uchar*  data;           /* read from last ctl req. */
       int     ndata;          /* number of bytes read */
};

struct Isoio
{
       QLock;
       Rendez;                 /* wait for space/completion/errors */
       int     usbid;          /* address used for device/endpoint */
       int     tok;            /* Tdtokin or Tdtokout */
       int     state;          /* Qrun -&gt; Qdone -&gt; Qrun... -&gt; Qclose */
       int     nframes;        /* number of frames ([S]Itds) used */
       uchar*  data;           /* iso data buffers if not embedded */
       char*   err;            /* error string */
       int     nerrs;          /* nb of consecutive I/O errors */
       ulong   maxsize;                /* ntds * ep-&gt;maxpkt */
       long    nleft;          /* number of bytes left from last write */
       int     debug;          /* debug flag from the endpoint */
       int     hs;             /* is high speed? */
       Isoio*  next;           /* in list of active Isoios */
       ulong   td0frno;        /* first frame used in ctlr */
       union{
               Itd*    tdi;    /* next td processed by interrupt */
               Sitd*   stdi;
       };
       union{
               Itd*    tdu;    /* next td for user I/O in tdps */
               Sitd*   stdu;
       };
       union{
               Itd**   itdps;  /* itdps[i]: ptr to Itd for i-th frame or nil */
               Sitd**  sitdps; /* sitdps[i]: ptr to Sitd for i-th frame or nil */
               ulong** tdps;   /* same thing, as seen by hw */
       };
};

struct Poll
{
       Lock;
       Rendez;
       int must;
       int does;
};

struct Ctlr
{
       Rendez;                 /* for waiting to async advance doorbell */
       Lock;                   /* for ilock. qh lists and basic ctlr I/O */
       QLock   portlck;        /* for port resets/enable... (and doorbell) */
       int     active;         /* in use or not */
       Ecapio* capio;          /* Capability i/o regs */
       Eopio*  opio;           /* Operational i/o regs */

       int     nframes;        /* 1024, 512, or 256 frames in the list */
       ulong*  frames;         /* periodic frame list (hw) */
       Qh*     qhs;            /* async Qh circular list for bulk/ctl */
       Qtree*  tree;           /* tree of Qhs for the periodic list */
       int     ntree;          /* number of dummy qhs in tree */
       Qh*     intrqhs;                /* list of (not dummy) qhs in tree  */
       Isoio*  iso;            /* list of active Iso I/O */
       ulong   load;
       ulong   isoload;
       int     nintr;          /* number of interrupts attended */
       int     ntdintr;                /* number of intrs. with something to do */
       int     nqhintr;                /* number of async td intrs. */
       int     nisointr;       /* number of periodic td intrs. */
       int     nreqs;
       Poll    poll;
};

struct Edpool
{
       Lock;
       Ed*     free;
       int     nalloc;
       int     ninuse;
       int     nfree;
};

/*
* We use the 64-bit version for Itd, Sitd, Td, and Qh.
* If the ehci is 64-bit capable it assumes we are using those
* structures even when the system is 32 bits.
*/

/*
* Iso transfer descriptor. hw. 92 bytes, 104 bytes total
* aligned to 32.
*/
struct Itd
{
       ulong   link;           /* to next hw struct */
       ulong   csw[8];         /* sts/length/pg/off. updated by hw */
       ulong   buffer[7];      /* buffer pointers, addrs, maxsz */
       ulong   xbuffer[7];     /* high 32 bits of buffer for 64-bits */

       /* software */
       Itd*    next;
       ulong   ndata;          /* number of bytes in data */
       ulong   mdata;          /* max number of bytes in data */
       uchar*  data;
};

/*
* Split transaction iso transfer descriptor.
* hw: 36 bytes, 52 bytes total. aligned to 32.
*/
struct Sitd
{
       ulong   link;           /* to next hw struct */
       ulong   epc;            /* static endpoint state. addrs */
       ulong   mfs;            /* static endpoint state. µ-frame sched. */
       ulong   csw;            /* transfer state. updated by hw */
       ulong   buffer[2];      /* buf. ptr/offset. offset updated by hw */
                               /* buf ptr/TP/Tcnt. TP/Tcnt updated by hw */
       ulong   blink;          /* back pointer */
       ulong   xbuffer[2];     /* high 32 bits of buffer for 64-bits */

       /* software */
       Sitd*   next;
       ulong   ndata;          /* number of bytes in data */
       ulong   mdata;          /* max number of bytes in data */
       uchar*  data;
};

/*
* Queue element transfer descriptor.
* hw: first 52 bytes; total 68+sbuff bytes aligned to 32 bytes.
*/
struct Td
{
       ulong   nlink;          /* to next Td */
       ulong   alink;          /* alternate link to next Td */
       ulong   csw;            /* cmd/sts. updated by hw */
       ulong   buffer[5];      /* buf ptrs. offset updated by hw */
       ulong   xbuffer[5];     /* high 32 bits of buffer for 64-bits */

       Td*     next;           /* in qh or Isoio or free list */
       ulong   ndata;          /* bytes available/used at data */
       uchar*  data;           /* pointer to actual data */
       uchar*  buff;           /* allocated data buffer or nil */
       uchar   sbuff[1];       /* first byte of embedded buffer */
};

/*
* Queue head. Aligned to 32 bytes.
* hw uses the first 68 bytes, 92 total.
*/
struct Qh
{
       ulong   link;           /* to next Qh in round robin */
       ulong   eps0;           /* static endpoint state. addrs */
       ulong   eps1;           /* static endpoint state. µ-frame sched. */

       /* updated by hw */
       ulong   clink;          /* current Td (No Term bit here!) */
       ulong   nlink;          /* to next Td */
       ulong   alink;          /* alternate link to next Td */
       ulong   csw;            /* cmd/sts. updated by hw */
       ulong   buffer[5];      /* buf ptrs. offset updated by hw */
       ulong   xbuffer[5];     /* high 32 bits of buffer for 64-bits */

       /* software */
       Qh*     next;           /* in controller list/tree of Qhs */
       int     state;          /* Qidle -&gt; Qinstall -&gt; Qrun -&gt; Qdone | Qclose */
       Qio*    io;             /* for this queue */
       Td*     tds;            /* for this queue */
       int     sched;          /* slot for for intr. Qhs */
       Qh*     inext;          /* next in list of intr. qhs */
};

/*
* We can avoid frame span traversal nodes if we don't span frames.
* Just schedule transfer that can fit on the current frame and
* wait a little bit otherwise.
*/

/*
* Software. Ehci descriptors provided by pool.
* There are soo few because we avoid using Fstn.
*/
union Ed
{
       Ed*     next;           /* in free list */
       Qh      qh;
       Td      td;
       Itd     itd;
       Sitd    sitd;
       uchar   align[Align];
};

typedef struct Kwusb Kwusb;
typedef struct Usbwin Usbwin;
struct Kwusb {                  /* at offset 0x300 from Addrusb */
       ulong   bcs;            /* bridge ctl &amp; sts */
       uchar   _pad0[0x310-0x304];

       ulong   bic;            /* bridge intr. cause */
       ulong   bim;            /* bridge intr. mask */
       ulong   _pad1;
       ulong   bea;            /* bridge error addr. */
       struct Usbwin {
               ulong   ctl;
               ulong   base;
               ulong   _pad2[2];
       } win[4];
       ulong   phycfg;         /* phy config. */
       uchar   _pad3[0x400-0x364];

       ulong   pwrctl;         /* power control */
       uchar   _pad4[0x410-0x404];
       ulong   phypll;         /* phy pll control */
       uchar   _pad5[0x420-0x414];
       ulong   phytxctl;       /* phy transmit control */
       uchar   _pad6[0x430-0x424];
       ulong   phyrxctl;       /* phy receive control */
       uchar   _pad7[0x440-0x434];
       ulong   phyivref;       /* phy ivref control */
};

enum {
       /* Kwusb-&gt;win[i].ctl bits */
       Winenable       = 1&lt;&lt;0,
};

#define diprint         if(debug || iso-&gt;debug)print
#define ddiprint        if(debug&gt;1 || iso-&gt;debug&gt;1)print
#define dqprint         if(debug || (qh-&gt;io &amp;&amp; qh-&gt;io-&gt;debug))print
#define ddqprint        if(debug&gt;1 || (qh-&gt;io &amp;&amp; qh-&gt;io-&gt;debug&gt;1))print
#define TRUNC(x, sz)    ((x) &amp; ((sz)-1))
#define LPTR(q)         ((ulong*)KADDR((q) &amp; ~0x1F))

static int debug;
static Edpool edpool;
static Ctlr* ctlrs[Nhcis];
static char Ebug[] = "not yet implemented";
static char* qhsname[] = { "idle", "install", "run", "done", "close", "FREE" };


static void
xcachewbse(void *p, long sz)
{
       cachedwbse(p, sz);
       l2cacheuwbse(p, sz);
}

static void
xcacheinvse(void *p, long sz)
{
       l2cacheuinvse(p, sz);
       cachedinvse(p, sz);
}

static void
xcachewb(void)
{
       cachedwb();
       l2cacheuwb();
}

static void
xcachewbinv(void)
{
       l2cacheuinv();
       cachedinv();
}

static void
xcachewbinvse(void *p, long sz)
{
       xcachewbse(p, sz);
       xcacheinvse(p, sz);
}

static void
ehcirun(Ctlr *ctlr, int on)
{
       int i;
       Eopio *opio;

       ddprint("ehci %#p %s\n", ctlr-&gt;capio, on ? "starting" : "halting");
       opio = ctlr-&gt;opio;
       xcachewbinvse(&amp;opio-&gt;cmd, sizeof opio-&gt;cmd);
       if(on)
               opio-&gt;cmd |= Crun;
       else
               opio-&gt;cmd = Cstop;
       xcachewbse(&amp;opio-&gt;cmd, sizeof opio-&gt;cmd);
       for(i = 0; i &lt; 100; i++){
               xcacheinvse(&amp;opio-&gt;sts, sizeof opio-&gt;sts);
               if(on == 0 &amp;&amp; (opio-&gt;sts &amp; Shalted) != 0)
                       break;
               else if(on != 0 &amp;&amp; (opio-&gt;sts &amp; Shalted) == 0)
                       break;
               else
                       delay(1);
       }
       if(i == 100)
               print("ehci %#p %s cmd timed out\n",
                       ctlr-&gt;capio, on ? "run" : "halt");
       if(debug){
               xcachewbinvse(&amp;opio-&gt;cmd, sizeof opio-&gt;cmd);
               xcachewbinvse(&amp;opio-&gt;sts, sizeof opio-&gt;sts);
               ddprint("ehci %#p cmd %#ulx sts %#ulx\n",
                       ctlr-&gt;capio, opio-&gt;cmd, opio-&gt;sts);
       }
}

static void*
edalloc(void)
{
       Ed *ed, *pool;
       int i;

       lock(&amp;edpool);
       if(edpool.free == nil){
               pool = xspanalloc(Incr*sizeof(Ed), Align, 0);
               if(pool == nil)
                       panic("edalloc");
               for(i=Incr; --i&gt;=0;){
                       pool[i].next = edpool.free;
                       edpool.free = &amp;pool[i];
               }
               edpool.nalloc += Incr;
               edpool.nfree += Incr;
               dprint("ehci: edalloc: %d eds\n", edpool.nalloc);
       }
       ed = edpool.free;
       edpool.free = ed-&gt;next;
       edpool.ninuse++;
       edpool.nfree--;
       unlock(&amp;edpool);

       memset(ed, 0, sizeof(Ed));      /* safety */
       assert(((ulong)ed &amp; 0xF) == 0);
       return ed;
}

static void
edfree(void *a)
{
       Ed *ed;

       ed = a;
       lock(&amp;edpool);
       ed-&gt;next = edpool.free;
       edpool.free = ed;
       edpool.ninuse--;
       edpool.nfree++;
       unlock(&amp;edpool);
}

/*
* Allocate and do some initialization.
* Free after releasing buffers used.
*/

static Itd*
itdalloc(void)
{
       Itd *td;

       td = edalloc();
       td-&gt;link = Lterm;
       return td;
}

static void
itdfree(Itd *td)
{
       edfree(td);
}

static Sitd*
sitdalloc(void)
{
       Sitd *td;

       td = edalloc();
       td-&gt;link = td-&gt;blink = Lterm;
       return td;
}

static void
sitdfree(Sitd *td)
{
       edfree(td);
}

static Td*
tdalloc(void)
{
       Td *td;

       td = edalloc();
       td-&gt;nlink = td-&gt;alink = Lterm;
       return td;
}

static void
tdfree(Td *td)
{
       if(td == nil)
               return;
       free(td-&gt;buff);
       edfree(td);
}

static void
tdlinktd(Td *td, Td *next)
{
       td-&gt;next = next;
       td-&gt;alink = Lterm;
       if(next == nil)
               td-&gt;nlink = Lterm;
       else
               td-&gt;nlink = PADDR(next);
       xcachewbse(td, sizeof *td);
}

static Qh*
qhlinkqh(Qh *qh, Qh *next)
{
       qh-&gt;next = next;
       qh-&gt;link = PADDR(next)|Lqh;
       xcachewbse(&amp;qh-&gt;link, sizeof qh-&gt;link);
       return qh;
}

static void
qhsetaddr(Qh *qh, ulong addr)
{
       ulong eps0;
       ulong ep;
       ulong dev;

       xcachewbinvse(&amp;qh-&gt;eps0, sizeof qh-&gt;eps0);
       eps0 = qh-&gt;eps0 &amp; ~((Epmax&lt;&lt;8)|Devmax);
       ep = (addr &gt;&gt; 7) &amp; Epmax;
       dev = addr &amp; Devmax;
       eps0 |= ep &lt;&lt; 8;
       eps0 |= dev;
       qh-&gt;eps0 = eps0;
       xcachewbse(&amp;qh-&gt;eps0, sizeof qh-&gt;eps0);
}

/*
* return smallest power of 2 &lt;= n
*/
static int
flog2lower(int n)
{
       int i;

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

static int
pickschedq(Qtree *qt, int pollival, ulong bw, ulong limit)
{
       int i, j, d, upperb, q;
       ulong best, worst, total;

       d = flog2lower(pollival);
       if(d &gt; qt-&gt;depth)
               d = qt-&gt;depth;
       q = -1;
       worst = 0;
       best = ~0;
       upperb = (1 &lt;&lt; (d+1)) - 1;
       for(i = (1 &lt;&lt; d) - 1; i &lt; upperb; i++){
               total = qt-&gt;bw[0];
               for(j = i; j &gt; 0; j = (j - 1) / 2)
                       total += qt-&gt;bw[j];
               if(total &lt; best){
                       best = total;
                       q = i;
               }
               if(total &gt; worst)
                       worst = total;
       }
       if(worst + bw &gt;= limit)
               return -1;
       return q;
}

static int
schedq(Ctlr *ctlr, Qh *qh, int pollival)
{
       int q;
       Qh *tqh;
       ulong bw;

       bw = qh-&gt;io-&gt;bw;
       q = pickschedq(ctlr-&gt;tree, pollival, 0, ~0);
       ddqprint("ehci: sched %#p q %d, ival %d, bw %uld\n",
               qh-&gt;io, q, pollival, bw);
       if(q &lt; 0){
               print("ehci: no room for ed\n");
               return -1;
       }
       ctlr-&gt;tree-&gt;bw[q] += bw;
       tqh = ctlr-&gt;tree-&gt;root[q];
       qh-&gt;sched = q;
       qhlinkqh(qh, tqh-&gt;next);
       qhlinkqh(tqh, qh);
       qh-&gt;inext = ctlr-&gt;intrqhs;
       ctlr-&gt;intrqhs = qh;
       return 0;
}

static void
unschedq(Ctlr *ctlr, Qh *qh)
{
       int q;
       Qh *prev, *this, *next;
       Qh **l;
       ulong bw;

       bw = qh-&gt;io-&gt;bw;
       q = qh-&gt;sched;
       if(q &lt; 0)
               return;
       ctlr-&gt;tree-&gt;bw[q] -= bw;

       prev = ctlr-&gt;tree-&gt;root[q];
       this = prev-&gt;next;
       while(this != nil &amp;&amp; this != qh){
               prev = this;
               this = this-&gt;next;
       }
       if(this == nil)
               print("ehci: unschedq %d: not found\n", q);
       else{
               next = this-&gt;next;
               qhlinkqh(prev, next);
       }
       for(l = &amp;ctlr-&gt;intrqhs; *l != nil; l = &amp;(*l)-&gt;inext)
               if(*l == qh){
                       *l = (*l)-&gt;inext;
                       return;
               }
       print("ehci: unschedq: qh %#p not found\n", qh);
}

static ulong
qhmaxpkt(Qh *qh)
{
       return (qh-&gt;eps0 &gt;&gt; Qhmplshift) &amp; Qhmplmask;
}

static void
qhsetmaxpkt(Qh *qh, int maxpkt)
{
       ulong eps0;

       eps0 = qh-&gt;eps0 &amp; ~(Qhmplmask &lt;&lt; Qhmplshift);
       eps0 |= (maxpkt &amp; Qhmplmask) &lt;&lt; Qhmplshift;
       qh-&gt;eps0 = eps0;
       xcachewbse(&amp;qh-&gt;eps0, sizeof qh-&gt;eps0);
}

/*
* Initialize the round-robin circular list of ctl/bulk Qhs
* if ep is nil. Otherwise, allocate and link a new Qh in the ctlr.
*/
static Qh*
qhalloc(Ctlr *ctlr, Ep *ep, Qio *io, char* tag)
{
       Qh *qh;
       int ttype;

       qh = edalloc();
       qh-&gt;nlink = Lterm;
       qh-&gt;alink = Lterm;
       qh-&gt;csw = Tdhalt;
       qh-&gt;state = Qidle;
       qh-&gt;sched = -1;
       qh-&gt;io = io;
       if(ep != nil){
               qh-&gt;eps0 = 0;
               qhsetmaxpkt(qh, ep-&gt;maxpkt);
               if(ep-&gt;dev-&gt;speed == Lowspeed)
                       qh-&gt;eps0 |= Qhlow;
               if(ep-&gt;dev-&gt;speed == Highspeed)
                       qh-&gt;eps0 |= Qhhigh;
               else if(ep-&gt;ttype == Tctl)
                       qh-&gt;eps0 |= Qhnhctl;
               qh-&gt;eps0 |= Qhdtc;
               qh-&gt;eps0 |= (8 &lt;&lt; Qhrlcshift); /* 8 naks max */
               qhsetaddr(qh, io-&gt;usbid);
               qh-&gt;eps1 = (ep-&gt;ntds &amp; Qhmultmask) &lt;&lt; Qhmultshift;
               qh-&gt;eps1 |= ep-&gt;dev-&gt;port &lt;&lt; Qhportshift;
               qh-&gt;eps1 |= ep-&gt;dev-&gt;hub &lt;&lt; Qhhubshift;
               qh-&gt;eps1 |= 034 &lt;&lt; Qhscmshift;
               if(ep-&gt;ttype == Tintr)
                       qh-&gt;eps1 |= (1 &lt;&lt; Qhismshift); /* intr. start µf. */
               if(io != nil)
                       io-&gt;tag = tag;
       }
       ilock(ctlr);
       ttype = Tctl;
       if(ep != nil)
               ttype = ep-&gt;ttype;
       switch(ttype){
       case Tctl:
       case Tbulk:
               if(ctlr-&gt;qhs == nil){
                       ctlr-&gt;qhs = qhlinkqh(qh, qh);
                       qh-&gt;eps0 |= Qhhigh | Qhhrl;
                       xcachewbse(qh, sizeof *qh);
                       ctlr-&gt;opio-&gt;link = PADDR(qh)|Lqh;
                       xcachewbse(&amp;ctlr-&gt;opio-&gt;link, sizeof ctlr-&gt;opio-&gt;link);
               }else{
                       xcachewbse(qh, sizeof *qh);
                       qhlinkqh(qh, ctlr-&gt;qhs-&gt;next);
                       qhlinkqh(ctlr-&gt;qhs, qh);
               }
               break;
       case Tintr:
               xcachewbse(qh, sizeof *qh);
               schedq(ctlr, qh, ep-&gt;pollival);
               break;
       default:
               print("ehci: qhalloc called for ttype != ctl/bulk\n");
       }
       iunlock(ctlr);
       return qh;
}

static int
qhadvanced(void *a)
{
       Ctlr *ctlr;

       ctlr = a;
       xcachewbinvse(&amp;ctlr-&gt;opio-&gt;cmd, sizeof ctlr-&gt;opio-&gt;cmd);
       return (ctlr-&gt;opio-&gt;cmd &amp; Ciasync) == 0;
}

/*
* called when a qh is removed, to be sure the hw is not
* keeping pointers into it.
*/
static void
qhcoherency(Ctlr *ctlr)
{
       int i;

       qlock(&amp;ctlr-&gt;portlck);
       xcachewbinvse(&amp;ctlr-&gt;opio-&gt;cmd, sizeof ctlr-&gt;opio-&gt;cmd);
       ctlr-&gt;opio-&gt;cmd |= Ciasync;       /* ask for intr. on async advance */
       xcachewbse(&amp;ctlr-&gt;opio-&gt;cmd, sizeof ctlr-&gt;opio-&gt;cmd);
       for(i = 0; i &lt; 3 &amp;&amp; qhadvanced(ctlr) == 0; i++)
               if(!waserror()){
                       tsleep(ctlr, qhadvanced, ctlr, Abortdelay);
                       poperror();
               }
       dprint("ehci: qhcoherency: doorbell %d\n", qhadvanced(ctlr));
       if(i == 3)
               print("ehci: async advance doorbell did not ring\n");
       ctlr-&gt;opio-&gt;cmd &amp;= ~Ciasync;  /* try to clean */
       xcachewbse(&amp;ctlr-&gt;opio-&gt;cmd, sizeof ctlr-&gt;opio-&gt;cmd);
       qunlock(&amp;ctlr-&gt;portlck);
}

static void
qhfree(Ctlr *ctlr, Qh *qh)
{
       Td *td;
       Td *ltd;
       Qh *q;

       if(qh == nil)
               return;
       ilock(ctlr);
       if(qh-&gt;sched &lt; 0){
               for(q = ctlr-&gt;qhs; q != nil; q = q-&gt;next)
                       if(q-&gt;next == qh)
                               break;
               if(q == nil)
                       panic("qhfree: nil q");
               q-&gt;next = qh-&gt;next;
               q-&gt;link = qh-&gt;link;
               xcachewbse(&amp;q-&gt;link, sizeof q-&gt;link);
       }else
               unschedq(ctlr, qh);
       iunlock(ctlr);

       qhcoherency(ctlr);

       for(td = qh-&gt;tds; td != nil; td = ltd){
               ltd = td-&gt;next;
               tdfree(td);
       }

       edfree(qh);
}

static void
qhlinktd(Qh *qh, Td *td)
{
       ulong csw;
       int i;

       xcachewbinvse(qh, sizeof *qh);
       if(td == nil){
               qh-&gt;tds = nil;
               qh-&gt;csw |= Tdhalt;
               qh-&gt;csw &amp;= ~Tdactive;
               xcachewbse(&amp;qh-&gt;csw, sizeof qh-&gt;csw);
       }else{
               qh-&gt;tds = td;
               csw = qh-&gt;csw &amp; (Tddata1|Tdping);        /* save */
               qh-&gt;csw = Tdhalt;
               xcachewbse(&amp;qh-&gt;csw, sizeof qh-&gt;csw);
               qh-&gt;clink = 0;
               qh-&gt;alink = Lterm;
               qh-&gt;nlink = PADDR(td);
               for(i = 0; i &lt; nelem(qh-&gt;buffer); i++)
                       qh-&gt;buffer[i] = 0;
               xcachewbse(qh, sizeof *qh);
               qh-&gt;csw = csw &amp; ~(Tdhalt|Tdactive);      /* activate next */
               xcachewbse(&amp;qh-&gt;csw, sizeof qh-&gt;csw);
       }
}

static char*
seprintlink(char *s, char *se, char *name, ulong l, int typed)
{
       s = seprint(s, se, "%s %ulx", name, l);
       if((l &amp; Lterm) != 0)
               return seprint(s, se, "T");
       if(typed == 0)
               return s;
       switch(l &amp; (3&lt;&lt;1)){
       case Litd:
               return seprint(s, se, "I");
       case Lqh:
               return seprint(s, se, "Q");
       case Lsitd:
               return seprint(s, se, "S");
       default:
               return seprint(s, se, "F");
       }
}

static char*
seprintitd(char *s, char *se, Itd *td)
{
       int i;
       char flags[6];
       ulong b0;
       ulong b1;
       char *rw;

       if(td == nil)
               return seprint(s, se, "&lt;nil itd&gt;\n");
       xcachewbinvse(td, sizeof *td);
       b0 = td-&gt;buffer[0];
       b1 = td-&gt;buffer[1];

       s = seprint(s, se, "itd %#p", td);
       rw = (b1 &amp; Itdin) ? "in" : "out";
       s = seprint(s, se, " %s ep %uld dev %uld max %uld mult %uld",
               rw, (b0&gt;&gt;8)&amp;Epmax, (b0&amp;Devmax),
               td-&gt;buffer[1] &amp; 0x7ff, b1 &amp; 3);
       s = seprintlink(s, se, " link", td-&gt;link, 1);
       s = seprint(s, se, "\n");
       for(i = 0; i &lt; nelem(td-&gt;csw); i++){
               memset(flags, '-', 5);
               if((td-&gt;csw[i] &amp; Itdactive) != 0)
                       flags[0] = 'a';
               if((td-&gt;csw[i] &amp; Itdioc) != 0)
                       flags[1] = 'i';
               if((td-&gt;csw[i] &amp; Itddberr) != 0)
                       flags[2] = 'd';
               if((td-&gt;csw[i] &amp; Itdbabble) != 0)
                       flags[3] = 'b';
               if((td-&gt;csw[i] &amp; Itdtrerr) != 0)
                       flags[4] = 't';
               flags[5] = 0;
               s = seprint(s, se, "\ttd%d %s", i, flags);
               s = seprint(s, se, " len %uld", (td-&gt;csw[i] &gt;&gt; 16) &amp; 0x7ff);
               s = seprint(s, se, " pg %uld", (td-&gt;csw[i] &gt;&gt; 12) &amp; 0x7);
               s = seprint(s, se, " off %uld\n", td-&gt;csw[i] &amp; 0xfff);
       }
       s = seprint(s, se, "\tbuffs:");
       for(i = 0; i &lt; nelem(td-&gt;buffer); i++)
               s = seprint(s, se, " %#ulx", td-&gt;buffer[i] &gt;&gt; 12);
       return seprint(s, se, "\n");
}

static char*
seprintsitd(char *s, char *se, Sitd *td)
{
       static char pc[4] = { 'a', 'b', 'm', 'e' };
       char rw;
       char pg;
       char ss;
       char flags[8];

       if(td == nil)
               return seprint(s, se, "&lt;nil sitd&gt;\n");
       xcachewbinvse(td, sizeof *td);
       s = seprint(s, se, "sitd %#p", td);
       rw = (td-&gt;epc &amp; Stdin) ? 'r' : 'w';
       s = seprint(s, se, " %c ep %uld dev %uld",
               rw, (td-&gt;epc&gt;&gt;8)&amp;0xf, td-&gt;epc&amp;0x7f);
       s = seprint(s, se, " max %uld", (td-&gt;csw &gt;&gt; 16) &amp; 0x3ff);
       s = seprint(s, se, " hub %uld", (td-&gt;epc &gt;&gt; 16) &amp; 0x7f);
       s = seprint(s, se, " port %uld\n", (td-&gt;epc &gt;&gt; 24) &amp; 0x7f);
       memset(flags, '-', 7);
       if((td-&gt;csw &amp; Stdactive) != 0)
               flags[0] = 'a';
       if((td-&gt;csw &amp; Stdioc) != 0)
               flags[1] = 'i';
       if((td-&gt;csw &amp; Stderr) != 0)
               flags[2] = 'e';
       if((td-&gt;csw &amp; Stddberr) != 0)
               flags[3] = 'd';
       if((td-&gt;csw &amp; Stdbabble) != 0)
               flags[4] = 'b';
       if((td-&gt;csw &amp; Stdtrerr) != 0)
               flags[5] = 't';
       if((td-&gt;csw &amp; Stdmmf) != 0)
               flags[6] = 'n';
       flags[7] = 0;
       ss = (td-&gt;csw &amp; Stddcs) ? 'c' : 's';
       pg = (td-&gt;csw &amp; Stdpg) ? '1' : '0';
       s = seprint(s, se, "\t%s %cs pg%c", flags, ss, pg);
       s = seprint(s, se, " b0 %#ulx b1 %#ulx off %uld\n",
               td-&gt;buffer[0] &gt;&gt; 12, td-&gt;buffer[1] &gt;&gt; 12, td-&gt;buffer[0] &amp; 0xfff);
       s = seprint(s, se, "\ttpos %c tcnt %uld",
               pc[(td-&gt;buffer[0]&gt;&gt;3)&amp;3], td-&gt;buffer[1] &amp; 7);
       s = seprint(s, se, " ssm %#ulx csm %#ulx cspm %#ulx",
               td-&gt;mfs &amp; 0xff, (td-&gt;mfs&gt;&gt;8) &amp; 0xff, (td-&gt;csw&gt;&gt;8) &amp; 0xff);
       s = seprintlink(s, se, " link", td-&gt;link, 1);
       s = seprintlink(s, se, " blink", td-&gt;blink, 0);
       return seprint(s, se, "\n");
}

static long
maxtdlen(Td *td)
{
       xcachewbinvse(&amp;td-&gt;csw, sizeof td-&gt;csw);      /* not really neeeded */
       return (td-&gt;csw &gt;&gt; Tdlenshift) &amp; Tdlenmask;
}

static long
tdlen(Td *td)
{
       if(td-&gt;data == nil)
               return 0;
       return td-&gt;ndata - maxtdlen(td);
}

static char*
seprinttd(char *s, char *se, Td *td, char *tag)
{
       static char *tok[4] = { "out", "in", "setup", "BUG" };
       char flags[9];
       char t;
       char ss;
       int i;

       if(td == nil)
               return seprint(s, se, "%s &lt;nil td&gt;\n", tag);
       xcachewbinvse(td, sizeof *td);
       s = seprint(s, se, "%s %#p", tag, td);
       s = seprintlink(s, se, " nlink", td-&gt;nlink, 0);
       s = seprintlink(s, se, " alink", td-&gt;alink, 0);
       s = seprint(s, se, " %s", tok[(td-&gt;csw &amp; Tdtok) &gt;&gt; 8]);
       if((td-&gt;csw &amp; Tdping) != 0)
               s = seprint(s, se, " png");
       memset(flags, '-', 8);
       if((td-&gt;csw &amp; Tdactive) != 0)
               flags[0] = 'a';
       if((td-&gt;csw &amp; Tdioc) != 0)
               flags[1] = 'i';
       if((td-&gt;csw &amp; Tdhalt) != 0)
               flags[2] = 'h';
       if((td-&gt;csw &amp; Tddberr) != 0)
               flags[3] = 'd';
       if((td-&gt;csw &amp; Tdbabble) != 0)
               flags[4] = 'b';
       if((td-&gt;csw &amp; Tdtrerr) != 0)
               flags[5] = 't';
       if((td-&gt;csw &amp; Tdmmf) != 0)
               flags[6] = 'n';
       if((td-&gt;csw &amp; (Tderr2|Tderr1)) == 0)
               flags[7] = 'z';
       flags[8] = 0;
       t = (td-&gt;csw &amp; Tddata1) ? '1' : '0';
       ss = (td-&gt;csw &amp; Tddcs) ? 'c' : 's';
       s = seprint(s, se, "\n\td%c %s %cs", t, flags, ss);
       s = seprint(s, se, " max %uld", maxtdlen(td));
       s = seprint(s, se, " pg %uld off %#ulx\n",
               (td-&gt;csw &gt;&gt; Tdpgshift) &amp; Tdpgmask, td-&gt;buffer[0] &amp; 0xFFF);
       s = seprint(s, se, "\tbuffs:");
       for(i = 0; i &lt; nelem(td-&gt;buffer); i++)
               s = seprint(s, se, " %#ulx", td-&gt;buffer[i]&gt;&gt;12);
       if(td-&gt;data != nil)
               s = seprintdata(s, se, td-&gt;data, td-&gt;ndata);
       return seprint(s, se, "\n");
}

static void
dumptd(Td *td, char *pref)
{
       char buf[256];
       char *se;
       int i;

       i = 0;
       se = buf+sizeof(buf);
       for(; td != nil; td = td-&gt;next){
               seprinttd(buf, se, td, pref);
               print("%s", buf);
               if(i++ &gt; 20){
                       print("...more tds...\n");
                       break;
               }
       }
}

static void
qhdump(Qh *qh)
{
       static char *speed[] = {"full", "low", "high", "BUG"};
       char buf[256];
       char *s;
       char *se;
       char *tag;
       Td td;

       if(qh == nil){
               print("&lt;nil qh&gt;\n");
               return;
       }
       xcachewbinvse(qh, sizeof *qh);
       if(qh-&gt;io == nil)
               tag = "qh";
       else
               tag = qh-&gt;io-&gt;tag;
       se = buf+sizeof(buf);
       s = seprint(buf, se, "%s %#p", tag, qh);
       s = seprint(s, se, " ep %uld dev %uld",
               (qh-&gt;eps0&gt;&gt;8)&amp;0xf, qh-&gt;eps0&amp;0x7f);
       s = seprint(s, se, " hub %uld", (qh-&gt;eps1 &gt;&gt; 16) &amp; 0x7f);
       s = seprint(s, se, " port %uld", (qh-&gt;eps1 &gt;&gt; 23) &amp; 0x7f);
       s = seprintlink(s, se, " link", qh-&gt;link, 1);
       seprint(s, se, "  clink %#ulx", qh-&gt;clink);
       print("%s\n", buf);
       s = seprint(buf, se, "\tnrld %uld", (qh-&gt;eps0 &gt;&gt; Qhrlcshift) &amp; Qhrlcmask);
       s = seprint(s, se, " nak %uld", (qh-&gt;alink &gt;&gt; 1) &amp; 0xf);
       s = seprint(s, se, " max %uld ", qhmaxpkt(qh));
       if((qh-&gt;eps0 &amp; Qhnhctl) != 0)
               s = seprint(s, se, "c");
       if((qh-&gt;eps0 &amp; Qhhrl) != 0)
               s = seprint(s, se, "h");
       if((qh-&gt;eps0 &amp; Qhdtc) != 0)
               s = seprint(s, se, "d");
       if((qh-&gt;eps0 &amp; Qhint) != 0)
               s = seprint(s, se, "i");
       s = seprint(s, se, " %s", speed[(qh-&gt;eps0 &gt;&gt; 12) &amp; 3]);
       s = seprint(s, se, " mult %uld", (qh-&gt;eps1 &gt;&gt; Qhmultshift) &amp; Qhmultmask);
       seprint(s, se, " scm %#ulx ism %#ulx\n",
               (qh-&gt;eps1 &gt;&gt; 8 &amp; 0xff), qh-&gt;eps1 &amp; 0xff);
       print("%s\n", buf);
       memset(&amp;td, 0, sizeof(td));
       memmove(&amp;td, &amp;qh-&gt;nlink, 32);        /* overlay area */
       seprinttd(buf, se, &amp;td, "\tovl");
       print("%s", buf);
}

static void
isodump(Isoio* iso, int all)
{
       Itd *td, *tdi, *tdu;
       Sitd *std, *stdi, *stdu;
       char buf[256];
       int i;

       if(iso == nil){
               print("&lt;nil iso&gt;\n");
               return;
       }
       print("iso %#p %s %s speed state %d nframes %d maxsz %uld",
               iso, iso-&gt;tok == Tdtokin ? "in" : "out",
               iso-&gt;hs ? "high" : "full",
               iso-&gt;state, iso-&gt;nframes, iso-&gt;maxsize);
       print(" td0 %uld tdi %#p tdu %#p data %#p\n",
               iso-&gt;td0frno, iso-&gt;tdi, iso-&gt;tdu, iso-&gt;data);
       if(iso-&gt;err != nil)
               print("\terr %s\n", iso-&gt;err);
       if(iso-&gt;err != nil)
               print("\terr='%s'\n", iso-&gt;err);
       if(all == 0)
               if(iso-&gt;hs != 0){
                       tdi = iso-&gt;tdi;
                       seprintitd(buf, buf+sizeof(buf), tdi);
                       print("\ttdi %s\n", buf);
                       tdu = iso-&gt;tdu;
                       seprintitd(buf, buf+sizeof(buf), tdu);
                       print("\ttdu %s\n", buf);
               }else{
                       stdi = iso-&gt;stdi;
                       seprintsitd(buf, buf+sizeof(buf), stdi);
                       print("\tstdi %s\n", buf);
                       stdu = iso-&gt;stdu;
                       seprintsitd(buf, buf+sizeof(buf), stdu);
                       print("\tstdu %s\n", buf);
               }
       else{
               for(i = 0; i &lt; Nisoframes; i++)
                       if(iso-&gt;tdps[i] != nil)
                       if(iso-&gt;hs != 0){
                               td = iso-&gt;itdps[i];
                               seprintitd(buf, buf+sizeof(buf), td);
                               if(td == iso-&gt;tdi)
                                       print("i-&gt;");
                               if(td == iso-&gt;tdu)
                                       print("i-&gt;");
                               print("[%d]\t%s", i, buf);
                       }else{
                               std = iso-&gt;sitdps[i];
                               seprintsitd(buf, buf+sizeof(buf), std);
                               if(std == iso-&gt;stdi)
                                       print("i-&gt;");
                               if(std == iso-&gt;stdu)
                                       print("u-&gt;");
                               print("[%d]\t%s", i, buf);
                       }
       }
}

static void
dump(Hci *hp)
{
       Ctlr *ctlr;
       Isoio *iso;
       Eopio *opio;
       int i;
       char buf[128];
       char *s;
       char *se;
       Qh *qh;

       ctlr = hp-&gt;aux;
       opio = ctlr-&gt;opio;
       ilock(ctlr);
       print("ehci port %#p frames %#p (%d fr.) nintr %d ntdintr %d",
               ctlr-&gt;capio, ctlr-&gt;frames, ctlr-&gt;nframes,
               ctlr-&gt;nintr, ctlr-&gt;ntdintr);
       print(" nqhintr %d nisointr %d\n", ctlr-&gt;nqhintr, ctlr-&gt;nisointr);
       print("\tcmd %#ulx sts %#ulx intr %#ulx frno %uld",
               opio-&gt;cmd, opio-&gt;sts, opio-&gt;intr, opio-&gt;frno);
       print(" base %#ulx link %#ulx fr0 %#ulx\n",
               opio-&gt;frbase, opio-&gt;link, ctlr-&gt;frames[0]);
       se = buf+sizeof(buf);
       s = seprint(buf, se, "\t");
       for(i = 0; i &lt; hp-&gt;nports; i++){
               s = seprint(s, se, "p%d %#ulx ", i, opio-&gt;portsc[i]);
               if(hp-&gt;nports &gt; 4 &amp;&amp; i == hp-&gt;nports/2 - 1)
                       s = seprint(s, se, "\n\t");
       }
       print("%s\n", buf);
       qh = ctlr-&gt;qhs;
       i = 0;
       do{
               qhdump(qh);
               qh = qh-&gt;next;
       }while(qh != ctlr-&gt;qhs &amp;&amp; i++ &lt; 100);
       if(i &gt; 100)
               print("...too many Qhs...\n");
       if(ctlr-&gt;intrqhs != nil)
               print("intr qhs:\n");
       for(qh = ctlr-&gt;intrqhs; qh != nil; qh = qh-&gt;inext)
               qhdump(qh);
       if(ctlr-&gt;iso != nil)
               print("iso:\n");
       for(iso = ctlr-&gt;iso; iso != nil; iso = iso-&gt;next)
               isodump(ctlr-&gt;iso, 0);
       print("%d eds in tree\n", ctlr-&gt;ntree);
       iunlock(ctlr);
       lock(&amp;edpool);
       print("%d eds allocated = %d in use + %d free\n",
               edpool.nalloc, edpool.ninuse, edpool.nfree);
       unlock(&amp;edpool);
}

static char*
errmsg(int err)
{
       if(err == 0)
               return "ok";
       if(err &amp; Tddberr)
               return "data buffer error";
       if(err &amp; Tdbabble)
               return "babble detected";
       if(err &amp; Tdtrerr)
               return "transaction error";
       if(err &amp; Tdmmf)
               return "missed µframe";
       if(err &amp; Tdhalt)
               return Estalled;        /* [uo]hci report this error */
       return Eio;
}

static char*
ierrmsg(int err)
{
       if(err == 0)
               return "ok";
       if(err &amp; Itddberr)
               return "data buffer error";
       if(err &amp; Itdbabble)
               return "babble detected";
       if(err &amp; Itdtrerr)
               return "transaction error";
       return Eio;
}

static char*
serrmsg(int err)
{
       if(err &amp; Stderr)
               return "translation translator error";
       /* other errors have same numbers than Td errors */
       return errmsg(err);
}

static int
isocanread(void *a)
{
       Isoio *iso;

       iso = a;
       if(iso-&gt;state == Qclose)
               return 1;
       if(iso-&gt;state == Qrun &amp;&amp; iso-&gt;tok == Tdtokin){
               if(iso-&gt;hs != 0 &amp;&amp; iso-&gt;tdi != iso-&gt;tdu)
                       return 1;
               if(iso-&gt;hs == 0 &amp;&amp; iso-&gt;stdi != iso-&gt;stdu)
                       return 1;
       }
       return 0;
}

static int
isocanwrite(void *a)
{
       Isoio *iso;

       iso = a;
       if(iso-&gt;state == Qclose)
               return 1;
       if(iso-&gt;state == Qrun &amp;&amp; iso-&gt;tok == Tdtokout){
               if(iso-&gt;hs != 0 &amp;&amp; iso-&gt;tdu-&gt;next != iso-&gt;tdi)
                       return 1;
               if(iso-&gt;hs == 0 &amp;&amp; iso-&gt;stdu-&gt;next != iso-&gt;stdi)
                       return 1;
       }
       return 0;
}

static void
itdinit(Isoio *iso, Itd *td)
{
       ulong pa;
       int p;
       int t;
       ulong tsize;
       ulong size;

       /*
        * BUG: This does not put an integral number of samples
        * on each µframe unless samples per packet % 8 == 0
        * Also, all samples are packed early on each frame.
        */
       p = 0;
       size = td-&gt;ndata = td-&gt;mdata;
       pa = PADDR(td-&gt;data);
       for(t = 0; size &gt; 0 &amp;&amp; t &lt; 8; t++){
               tsize = size;
               if(tsize &gt; iso-&gt;maxsize)
                       tsize = iso-&gt;maxsize;
               size -= tsize;
               td-&gt;csw[t] = tsize &lt;&lt; Itdlenshift;
               assert(p &lt; nelem(td-&gt;buffer));
               td-&gt;csw[t] |= p &lt;&lt; Itdpgshift;
               td-&gt;csw[t] |= (pa &amp; 0xFFF) &lt;&lt; Itdoffshift;
               td-&gt;csw[t] |= Itdactive|Itdioc;
               xcachewbse(&amp;td-&gt;csw[t], sizeof td-&gt;csw[t]);
               if(((pa+tsize) &amp; ~0xFFF) != (pa &amp; ~0xFFF))
                       p++;
               pa += tsize;
       }
}

static void
sitdinit(Isoio *iso, Sitd *td)
{
       td-&gt;ndata = td-&gt;mdata &amp; Stdlenmask;
       td-&gt;buffer[0] = PADDR(td-&gt;data);
       td-&gt;buffer[1] = (td-&gt;buffer[0] &amp; ~0xFFF) + 0x1000;
       if(iso-&gt;tok == Tdtokin || td-&gt;ndata &lt;= 188)
               td-&gt;buffer[1] |= Stdtpall;
       else
               td-&gt;buffer[1] |= Stdtpbegin;
       if(iso-&gt;tok == Tdtokin)
               td-&gt;buffer[1] |= 1;
       else
               td-&gt;buffer[1] |= ((td-&gt;ndata + 187 ) / 188) &amp; Stdtcntmask;
       td-&gt;csw = (td-&gt;ndata &lt;&lt; Stdlenshift) | Stdactive | Stdioc;
       xcachewbse(td-&gt;buffer, 2 * sizeof td-&gt;buffer[0]);
       xcachewbse(&amp;td-&gt;csw, sizeof td-&gt;csw);
}

static int
itdactive(Itd *td)
{
       int i;

       for(i = 0; i &lt; nelem(td-&gt;csw); i++){
               xcachewbinvse(&amp;td-&gt;csw[i], sizeof td-&gt;csw[i]);
               if((td-&gt;csw[i] &amp; Itdactive) != 0)
                       return 1;
       }
       return 0;
}

static int
isohsinterrupt(Ctlr *ctlr, Isoio *iso)
{
       Itd *tdi;
       int err;
       int i;
       int t;
       int nframes;

       tdi = iso-&gt;tdi;
       assert(tdi != nil);
       if(itdactive(tdi))      /* not all tds are done */
               return 0;
       ctlr-&gt;nisointr++;
       ddiprint("isohsintr: iso %#p: tdi %#p tdu %#p\n", iso, tdi, iso-&gt;tdu);
       if(iso-&gt;state != Qrun &amp;&amp; iso-&gt;state != Qdone)
               panic("isofsintr: iso state");
       if(debug &gt; 1 || iso-&gt;debug &gt; 1)
               isodump(iso, 0);

       nframes = iso-&gt;nframes / 2;          /* limit how many we look */
       if(nframes &gt; Nisoframes)
               nframes = Nisoframes;

       if(iso-&gt;tok == Tdtokin)
               tdi-&gt;ndata = 0;
       /* else, it has the number of bytes transferred */

       for(i = 0; i &lt; nframes &amp;&amp; itdactive(tdi) == 0; i++){
               err = 0;
               if(iso-&gt;tok == Tdtokin)
                       tdi-&gt;ndata += (tdi-&gt;csw[i] &gt;&gt; Itdlenshift)&amp;Itdlenmask;
               for(t = 0; t &lt; nelem(tdi-&gt;csw); t++){
                       tdi-&gt;csw[i] &amp;= ~Itdioc;
                       err |= tdi-&gt;csw[i] &amp; Itderrors;
               }
               xcachewbse(&amp;tdi-&gt;csw[0], sizeof tdi-&gt;csw);
               if(err == 0)
                       iso-&gt;nerrs = 0;
               else if(iso-&gt;nerrs++ &gt; iso-&gt;nframes/2){
                       if(iso-&gt;err == nil){
                               iso-&gt;err = ierrmsg(err);
                               diprint("isohsintr: tdi %#p error %#ux %s\n",
                                       tdi, err, iso-&gt;err);
                               diprint("ctlr load %uld\n", ctlr-&gt;load);
                       }
                       tdi-&gt;ndata = 0;
               }else
                       tdi-&gt;ndata = 0;
               if(tdi-&gt;next == iso-&gt;tdu || tdi-&gt;next-&gt;next == iso-&gt;tdu){
                       memset(iso-&gt;tdu-&gt;data, 0, iso-&gt;tdu-&gt;mdata);
                       itdinit(iso, iso-&gt;tdu);
                       iso-&gt;tdu = iso-&gt;tdu-&gt;next;
                       iso-&gt;nleft = 0;
               }
               tdi = tdi-&gt;next;
       }
       ddiprint("isohsintr: %d frames processed\n", nframes);
       if(i == nframes){
               tdi-&gt;csw[0] |= Itdioc;
               xcachewbse(&amp;tdi-&gt;csw[0], sizeof tdi-&gt;csw[0]);
       }
       iso-&gt;tdi = tdi;
       if(isocanwrite(iso) || isocanread(iso)){
               diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso,
                       iso-&gt;tdi, iso-&gt;tdu);
               wakeup(iso);
       }
       return 1;
}

static int
isofsinterrupt(Ctlr *ctlr, Isoio *iso)
{
       Sitd *stdi;
       int err;
       int i;
       int nframes;

       stdi = iso-&gt;stdi;
       assert(stdi != nil);
       xcachewbinvse(&amp;stdi-&gt;csw, sizeof stdi-&gt;csw);
       if((stdi-&gt;csw &amp; Stdactive) != 0)         /* nothing new done */
               return 0;
       ctlr-&gt;nisointr++;
       ddiprint("isofsintr: iso %#p: tdi %#p tdu %#p\n", iso, stdi, iso-&gt;stdu);
       if(iso-&gt;state != Qrun &amp;&amp; iso-&gt;state != Qdone)
               panic("isofsintr: iso state");
       if(debug &gt; 1 || iso-&gt;debug &gt; 1)
               isodump(iso, 0);

       nframes = iso-&gt;nframes / 2;          /* limit how many we look */
       if(nframes &gt; Nisoframes)
               nframes = Nisoframes;

       xcachewbinvse(&amp;stdi-&gt;csw, sizeof stdi-&gt;csw);
       for(i = 0; i &lt; nframes &amp;&amp; (stdi-&gt;csw &amp; Stdactive) == 0; i++){
               stdi-&gt;csw &amp;= ~Stdioc;
               xcachewbse(&amp;stdi-&gt;csw, sizeof stdi-&gt;csw);
               err = stdi-&gt;csw &amp; Stderrors;
               if(err == 0){
                       iso-&gt;nerrs = 0;
                       if(iso-&gt;tok == Tdtokin)
                               stdi-&gt;ndata = (stdi-&gt;csw&gt;&gt;Stdlenshift)&amp;Stdlenmask;
                       /* else len is assumed correct */
               }else if(iso-&gt;nerrs++ &gt; iso-&gt;nframes/2){
                       if(iso-&gt;err == nil){
                               iso-&gt;err = serrmsg(err);
                               diprint("isofsintr: tdi %#p error %#ux %s\n",
                                       stdi, err, iso-&gt;err);
                               diprint("ctlr load %uld\n", ctlr-&gt;load);
                       }
                       stdi-&gt;ndata = 0;
               }else
                       stdi-&gt;ndata = 0;

               if(stdi-&gt;next == iso-&gt;stdu || stdi-&gt;next-&gt;next == iso-&gt;stdu){
                       memset(iso-&gt;stdu-&gt;data, 0, iso-&gt;stdu-&gt;mdata);
                       xcachewbse(iso-&gt;stdu-&gt;data, iso-&gt;stdu-&gt;mdata);
                       sitdinit(iso, iso-&gt;stdu);
                       iso-&gt;stdu = iso-&gt;stdu-&gt;next;
                       iso-&gt;nleft = 0;
               }
               stdi = stdi-&gt;next;
               if(stdi != nil)
                       xcachewbinvse(&amp;stdi-&gt;csw, sizeof stdi-&gt;csw);
       }
       ddiprint("isofsintr: %d frames processed\n", nframes);
       if(i == nframes){
               stdi-&gt;csw |= Stdioc;
               xcachewbse(&amp;stdi-&gt;csw, sizeof stdi-&gt;csw);
       }
       iso-&gt;stdi = stdi;
       if(isocanwrite(iso) || isocanread(iso)){
               diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso,
                       iso-&gt;stdi, iso-&gt;stdu);
               wakeup(iso);
       }
       return 1;
}

static int
qhinterrupt(Ctlr *ctlr, Qh *qh)
{
       Td *td;
       int err;

       if(qh-&gt;state != Qrun)
               panic("qhinterrupt: qh state");
       if(qh-&gt;tds == nil)
               panic("qhinterrupt: no tds");
       xcachewbinvse(&amp;qh-&gt;tds-&gt;csw, sizeof qh-&gt;tds-&gt;csw);
       if((qh-&gt;tds-&gt;csw &amp; Tdactive) == 0)
               ddqprint("qhinterrupt port %#p qh %#p\n",ctlr-&gt;capio, qh);
       for(td = qh-&gt;tds; td != nil; td = td-&gt;next){
               xcachewbinvse(&amp;td-&gt;csw, sizeof td-&gt;csw);
               if(td-&gt;csw &amp; Tdactive)
                       return 0;
               if((td-&gt;csw &amp; Tderrors) != 0){
                       err = td-&gt;csw &amp; Tderrors;
                       if(qh-&gt;io-&gt;err == nil){
                               qh-&gt;io-&gt;err = errmsg(td-&gt;csw &amp; Tderrors);
                               dqprint("qhintr: td %#p csw %#ulx error %#ux %s\n",
                                       td, td-&gt;csw, err, qh-&gt;io-&gt;err);
                       }
                       break;
               }
               td-&gt;ndata = tdlen(td);
               if(td-&gt;ndata &lt; maxtdlen(td)){     /* EOT */
                       td = td-&gt;next;
                       break;
               }
       }
       /*
        * Done. Make void the Tds not used (errors or EOT) and wakeup epio.
        */
       for(; td != nil; td = td-&gt;next)
               td-&gt;ndata = 0;
       qh-&gt;state = Qdone;
       wakeup(qh-&gt;io);
       return 1;
}

static int
ehciintr(Hci *hp)
{
       Ctlr *ctlr;
       Eopio *opio;
       Isoio *iso;
       ulong sts;
       Qh *qh;
       int i;
       int some;

       ctlr = hp-&gt;aux;
       opio = ctlr-&gt;opio;

       /*
        * Will we know in USB 3.0 who the interrupt was for?.
        * Do they still teach indexing in CS?
        * This is Intel's doing.
        */
       ilock(ctlr);
       ctlr-&gt;nintr++;
       xcachewbinvse(&amp;opio-&gt;sts, sizeof opio-&gt;sts);
       sts = opio-&gt;sts &amp; Sintrs;
       if(sts == 0){           /* not ours; shared intr. */
               iunlock(ctlr);
               return 0;
       }
       opio-&gt;sts = sts;
       xcachewbse(&amp;opio-&gt;sts, sizeof opio-&gt;sts);
       if((sts &amp; Sherr) != 0)
               print("ehci: port %#p fatal host system error\n", ctlr-&gt;capio);
       if((sts &amp; Shalted) != 0)
               print("ehci: port %#p: halted\n", ctlr-&gt;capio);
       if((sts &amp; Sasync) != 0){
               dprint("ehci: doorbell\n");
               wakeup(ctlr);
       }
       /*
        * We enter always this if, even if it seems the
        * interrupt does not report anything done/failed.
        * Some controllers don't post interrupts right.
        */
       some = 0;
       if((sts &amp; (Serrintr|Sintr)) != 0){
               ctlr-&gt;ntdintr++;
               if(debug &gt; 1){
                       print("ehci port %#p frames %#p nintr %d ntdintr %d",
                               ctlr-&gt;capio, ctlr-&gt;frames,
                               ctlr-&gt;nintr, ctlr-&gt;ntdintr);
                       print(" nqhintr %d nisointr %d\n",
                               ctlr-&gt;nqhintr, ctlr-&gt;nisointr);
                       print("\tcmd %#ulx sts %#ulx intr %#ulx frno %uld",
                               opio-&gt;cmd, opio-&gt;sts, opio-&gt;intr, opio-&gt;frno);
               }

               /* process the Iso transfers */
               for(iso = ctlr-&gt;iso; iso != nil; iso = iso-&gt;next)
                       if(iso-&gt;state == Qrun || iso-&gt;state == Qdone)
                               if(iso-&gt;hs != 0)
                                       some += isohsinterrupt(ctlr, iso);
                               else
                                       some += isofsinterrupt(ctlr, iso);

               /* process the qhs in the periodic tree */
               for(qh = ctlr-&gt;intrqhs; qh != nil; qh = qh-&gt;inext)
                       if(qh-&gt;state == Qrun)
                               some += qhinterrupt(ctlr, qh);

               /* process the async Qh circular list */
               qh = ctlr-&gt;qhs;
               i = 0;
               do{
                       if(qh-&gt;state == Qrun)
                               some += qhinterrupt(ctlr, qh);
                       qh = qh-&gt;next;
               }while(qh != ctlr-&gt;qhs &amp;&amp; i++ &lt; 100);
               if(i &gt; 100)
                       print("echi: interrupt: qh loop?\n");
       }
       iunlock(ctlr);
       return some;
}

static void
interrupt(Ureg*, void* a)
{
       ehciintr(a);
}

static int
portenable(Hci *hp, int port, int on)
{
       Ctlr *ctlr;
       Eopio *opio;
       int s;

       ctlr = hp-&gt;aux;
       opio = ctlr-&gt;opio;
       xcachewbinvse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
       s = opio-&gt;portsc[port-1];
       qlock(&amp;ctlr-&gt;portlck);
       if(waserror()){
               qunlock(&amp;ctlr-&gt;portlck);
               nexterror();
       }
       dprint("ehci %#p port %d enable=%d; sts %#x\n",
               ctlr-&gt;capio, port, on, s);
       ilock(ctlr);
       if(s &amp; (Psstatuschg | Pschange))
               opio-&gt;portsc[port-1] = s;
       xcachewbse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
       if(on)
               opio-&gt;portsc[port-1] |= Psenable;
       else
               opio-&gt;portsc[port-1] &amp;= ~Psenable;
       xcachewbse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
       microdelay(64);
       iunlock(ctlr);
       tsleep(&amp;up-&gt;sleep, return0, 0, Enabledelay);
       if(debug){
               xcachewbinvse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
               print("ehci %#p port %d enable=%d: sts %#ulx\n",
                       ctlr-&gt;capio, port, on, opio-&gt;portsc[port-1]);
       }
       qunlock(&amp;ctlr-&gt;portlck);
       poperror();
       return 0;
}

/*
* If we detect during status that the port is low-speed or
* during reset that it's full-speed, the device is not for
* ourselves. The companion controller will take care.
* Low-speed devices will not be seen by usbd. Full-speed
* ones are seen because it's only after reset that we know what
* they are (usbd may notice a device not enabled in this case).
*/
static void
portlend(Ctlr *ctlr, int port, char *ss)
{
       Eopio *opio;
       ulong s;

       opio = ctlr-&gt;opio;

       dprint("ehci %#p port %d: %s speed device: no longer owned\n",
               ctlr-&gt;capio, port, ss);
       xcachewbinvse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
       s = opio-&gt;portsc[port-1];
       s &amp;= ~(Pschange|Psstatuschg);
       s |= Psowner;
       opio-&gt;portsc[port-1] = s;
       xcachewbse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);

}

static int
portreset(Hci *hp, int port, int on)
{
       ulong s;
       Eopio *opio;
       Ctlr *ctlr;
       int i;

       if(on == 0)
               return 0;

       ctlr = hp-&gt;aux;
       opio = ctlr-&gt;opio;
       qlock(&amp;ctlr-&gt;portlck);
       if(waserror()){
               iunlock(ctlr);
               qunlock(&amp;ctlr-&gt;portlck);
               nexterror();
       }
       xcachewbinvse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
       s = opio-&gt;portsc[port-1];
       dprint("ehci %#p port %d reset; sts %#ulx\n", ctlr-&gt;capio, port, s);
       ilock(ctlr);
       s &amp;= ~(Psenable|Psreset);
       opio-&gt;portsc[port-1] = s|Psreset;
       xcachewbse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
       for(i = 0; i &lt; 10; i++){
               delay(10);
               xcachewbinvse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
               if((opio-&gt;portsc[port-1] &amp; Psreset) == 0)
                       break;
       }
       opio-&gt;portsc[port-1] &amp;= ~Psreset;
       xcachewbinvse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
       delay(10);
       if((opio-&gt;portsc[port-1] &amp; Psenable) == 0)
               portlend(ctlr, port, "full");

       iunlock(ctlr);
       dprint("ehci %#p after port %d reset; sts %#ulx\n",
               ctlr-&gt;capio, port, opio-&gt;portsc[port-1]);
       qunlock(&amp;ctlr-&gt;portlck);
       poperror();
       return 0;
}

static int
portstatus(Hci *hp, int port)
{
       int s;
       int r;
       Eopio *opio;
       Ctlr *ctlr;

       ctlr = hp-&gt;aux;
       opio = ctlr-&gt;opio;
       qlock(&amp;ctlr-&gt;portlck);
       if(waserror()){
               iunlock(ctlr);
               qunlock(&amp;ctlr-&gt;portlck);
               nexterror();
       }
       ilock(ctlr);
       xcachewbinvse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
       s = opio-&gt;portsc[port-1];
       if(s &amp; (Psstatuschg | Pschange)){
               opio-&gt;portsc[port-1] = s;
               xcachewbse(&amp;opio-&gt;portsc[port-1], sizeof opio-&gt;portsc[0]);
               ddprint("ehci %#p port %d status %#x\n", ctlr-&gt;capio, port, s);
       }
       /*
        * If the port is a low speed port we yield ownership now
        * to the [uo]hci companion controller and pretend it's not here.
        */
       if((s &amp; Pspresent) != 0 &amp;&amp; (s &amp; Pslinemask) == Pslow){
               portlend(ctlr, port, "low");
               s &amp;= ~Pspresent;                    /* not for us this time */
       }
       iunlock(ctlr);
       qunlock(&amp;ctlr-&gt;portlck);
       poperror();

       /*
        * We must return status bits as a
        * get port status hub request would do.
        */
       r = 0;
       if(s &amp; Pspresent)
               r |= HPpresent|HPhigh;
       if(s &amp; Psenable)
               r |= HPenable;
       if(s &amp; Pssuspend)
               r |= HPsuspend;
       if(s &amp; Psreset)
               r |= HPreset;
       if(s &amp; Psstatuschg)
               r |= HPstatuschg;
       if(s &amp; Pschange)
               r |= HPchange;
       return r;
}

static char*
seprintio(char *s, char *e, Qio *io, char *pref)
{
       s = seprint(s,e,"%s io %#p qh %#p id %#x", pref, io, io-&gt;qh, io-&gt;usbid);
       s = seprint(s,e," iot %ld", io-&gt;iotime);
       s = seprint(s,e," tog %#x tok %#x err %s", io-&gt;toggle, io-&gt;tok, io-&gt;err);
       return s;
}

static char*
seprintep(char *s, char *e, Ep *ep)
{
       Qio *io;
       Ctlio *cio;
       Ctlr *ctlr;

       ctlr = ep-&gt;hp-&gt;aux;
       ilock(ctlr);
       if(ep-&gt;aux == nil){
               *s = 0;
               iunlock(ctlr);
               return s;
       }
       switch(ep-&gt;ttype){
       case Tctl:
               cio = ep-&gt;aux;
               s = seprintio(s, e, cio, "c");
               s = seprint(s, e, "\trepl %d ndata %d\n", ep-&gt;rhrepl, cio-&gt;ndata);
               break;
       case Tbulk:
       case Tintr:
               io = ep-&gt;aux;
               if(ep-&gt;mode != OWRITE)
                       s = seprintio(s, e, &amp;io[OREAD], "r");
               if(ep-&gt;mode != OREAD)
                       s = seprintio(s, e, &amp;io[OWRITE], "w");
               break;
       case Tiso:
               *s = 0;
               break;
       }
       iunlock(ctlr);
       return s;
}

/*
* halt condition was cleared on the endpoint. update our toggles.
*/
static void
clrhalt(Ep *ep)
{
       Qio *io;
       ep-&gt;clrhalt = 0;
       switch(ep-&gt;ttype){
       case Tintr:
       case Tbulk:
               io = ep-&gt;aux;
               if(ep-&gt;mode != OREAD){
                       qlock(&amp;io[OWRITE]);
                       io[OWRITE].toggle = Tddata0;
                       deprint("ep clrhalt for io %#p\n", io+OWRITE);
                       qunlock(&amp;io[OWRITE]);
               }
               if(ep-&gt;mode != OWRITE){
                       qlock(&amp;io[OREAD]);
                       io[OREAD].toggle = Tddata0;
                       deprint("ep clrhalt for io %#p\n", io+OREAD);
                       qunlock(&amp;io[OREAD]);
               }
               break;
       }
}

static void
xdump(char* pref, void *qh)
{
       int i;
       ulong *u;

       u = qh;
       print("%s %#p:", pref, u);
       for(i = 0; i &lt; 16; i++)
               if((i%4) == 0)
                       print("\n %#8.8ulx", u[i]);
               else
                       print(" %#8.8ulx", u[i]);
       print("\n");
}

static long
episohscpy(Ctlr *ctlr, Ep *ep, Isoio* iso, uchar *b, long count)
{
       int nr;
       long tot;
       Itd *tdu;

       for(tot = 0; iso-&gt;tdi != iso-&gt;tdu &amp;&amp; tot &lt; count; tot += nr){
               tdu = iso-&gt;tdu;
               if(itdactive(tdu))
                       break;
               nr = tdu-&gt;ndata;
               if(tot + nr &gt; count)
                       nr = count - tot;
               if(nr == 0)
                       print("ehci: ep%d.%d: too many polls\n",
                               ep-&gt;dev-&gt;nb, ep-&gt;nb);
               else{
                       xcacheinvse(tdu-&gt;data, tdu-&gt;ndata);
                       iunlock(ctlr);          /* We could page fault here */
                       memmove(b+tot, tdu-&gt;data, nr);
                       ilock(ctlr);
                       if(nr &lt; tdu-&gt;ndata)
                               memmove(tdu-&gt;data, tdu-&gt;data+nr, tdu-&gt;ndata - nr);
                       tdu-&gt;ndata -= nr;
               }
               if(tdu-&gt;ndata == 0){
                       itdinit(iso, tdu);
                       iso-&gt;tdu = tdu-&gt;next;
               }
       }
       return tot;
}

static long
episofscpy(Ctlr *ctlr, Ep *ep, Isoio* iso, uchar *b, long count)
{
       int nr;
       long tot;
       Sitd *stdu;

       for(tot = 0; iso-&gt;stdi != iso-&gt;stdu &amp;&amp; tot &lt; count; tot += nr){
               stdu = iso-&gt;stdu;
               xcachewbinvse(&amp;stdu-&gt;csw, sizeof stdu-&gt;csw);
               if(stdu-&gt;csw &amp; Stdactive){
                       diprint("ehci: episoread: %#p tdu active\n", iso);
                       break;
               }
               nr = stdu-&gt;ndata;
               if(tot + nr &gt; count)
                       nr = count - tot;
               if(nr == 0)
                       print("ehci: ep%d.%d: too many polls\n",
                               ep-&gt;dev-&gt;nb, ep-&gt;nb);
               else{
                       iunlock(ctlr);          /* We could page fault here */
                       xcacheinvse(stdu-&gt;data, stdu-&gt;ndata);
                       memmove(b+tot, stdu-&gt;data, stdu-&gt;ndata);
                       ilock(ctlr);
                       if(nr &lt; stdu-&gt;ndata)
                               memmove(stdu-&gt;data,stdu-&gt;data+nr,stdu-&gt;ndata - nr);
                       stdu-&gt;ndata -= nr;
               }
               if(stdu-&gt;ndata == 0){
                       sitdinit(iso, stdu);
                       iso-&gt;stdu = stdu-&gt;next;
               }
       }
       return tot;
}

static long
episoread(Ep *ep, Isoio *iso, void *a, long count)
{
       Ctlr *ctlr;
       uchar *b;
       long tot;

       iso-&gt;debug = ep-&gt;debug;
       diprint("ehci: episoread: %#p ep%d.%d\n", iso, ep-&gt;dev-&gt;nb, ep-&gt;nb);

       b = a;
       ctlr = ep-&gt;hp-&gt;aux;
       qlock(iso);
       if(waserror()){
               qunlock(iso);
               nexterror();
       }
       iso-&gt;err = nil;
       iso-&gt;nerrs = 0;
       ilock(ctlr);
       if(iso-&gt;state == Qclose){
               iunlock(ctlr);
               error(iso-&gt;err ? iso-&gt;err : Eio);
       }
       iso-&gt;state = Qrun;
       while(isocanread(iso) == 0){
               iunlock(ctlr);
               diprint("ehci: episoread: %#p sleep\n", iso);
               if(waserror()){
                       if(iso-&gt;err == nil)
                               iso-&gt;err = "I/O timed out";
                       ilock(ctlr);
                       break;
               }
               tsleep(iso, isocanread, iso, ep-&gt;tmout);
               poperror();
               ilock(ctlr);
       }
       if(iso-&gt;state == Qclose){
               iunlock(ctlr);
               error(iso-&gt;err ? iso-&gt;err : Eio);
       }
       iso-&gt;state = Qdone;
       assert(iso-&gt;tdu != iso-&gt;tdi);

       if(iso-&gt;hs != 0)
               tot = episohscpy(ctlr, ep, iso, b, count);
       else
               tot = episofscpy(ctlr, ep, iso, b, count);
       iunlock(ctlr);
       qunlock(iso);
       poperror();
       diprint("uhci: episoread: %#p %uld bytes err '%s'\n", iso, tot, iso-&gt;err);
       if(iso-&gt;err != nil)
               error(iso-&gt;err);
       return tot;
}

/*
* iso-&gt;tdu is the next place to put data. When it gets full
* it is activated and tdu advanced.
*/
static long
putsamples(Isoio *iso, uchar *b, long count)
{
       long tot;
       long n;

       for(tot = 0; isocanwrite(iso) &amp;&amp; tot &lt; count; tot += n){
               n = count-tot;
               if(iso-&gt;hs != 0){
                       if(n &gt; iso-&gt;tdu-&gt;mdata - iso-&gt;nleft)
                               n = iso-&gt;tdu-&gt;mdata - iso-&gt;nleft;
                       memmove(iso-&gt;tdu-&gt;data+iso-&gt;nleft, b+tot, n);
                       xcachewbse(iso-&gt;tdu-&gt;data+iso-&gt;nleft, n);
                       iso-&gt;nleft += n;
                       if(iso-&gt;nleft == iso-&gt;tdu-&gt;mdata){
                               itdinit(iso, iso-&gt;tdu);
                               iso-&gt;nleft = 0;
                               iso-&gt;tdu = iso-&gt;tdu-&gt;next;
                       }
               }else{
                       if(n &gt; iso-&gt;stdu-&gt;mdata - iso-&gt;nleft)
                               n = iso-&gt;stdu-&gt;mdata - iso-&gt;nleft;
                       memmove(iso-&gt;stdu-&gt;data+iso-&gt;nleft, b+tot, n);
                       xcachewbse(iso-&gt;tdu-&gt;data+iso-&gt;nleft, n);
                       iso-&gt;nleft += n;
                       if(iso-&gt;nleft == iso-&gt;stdu-&gt;mdata){
                               sitdinit(iso, iso-&gt;stdu);
                               iso-&gt;nleft = 0;
                               iso-&gt;stdu = iso-&gt;stdu-&gt;next;
                       }
               }
       }
       return tot;
}

/*
* Queue data for writing and return error status from
* last writes done, to maintain buffered data.
*/
static long
episowrite(Ep *ep, Isoio *iso, void *a, long count)
{
       Ctlr *ctlr;
       uchar *b;
       int tot;
       int nw;
       char *err;

       iso-&gt;debug = ep-&gt;debug;
       diprint("ehci: episowrite: %#p ep%d.%d\n", iso, ep-&gt;dev-&gt;nb, ep-&gt;nb);

       ctlr = ep-&gt;hp-&gt;aux;
       qlock(iso);
       if(waserror()){
               qunlock(iso);
               nexterror();
       }
       ilock(ctlr);
       if(iso-&gt;state == Qclose){
               iunlock(ctlr);
               error(iso-&gt;err ? iso-&gt;err : Eio);
       }
       iso-&gt;state = Qrun;
       b = a;
       for(tot = 0; tot &lt; count; tot += nw){
               while(isocanwrite(iso) == 0){
                       iunlock(ctlr);
                       diprint("ehci: episowrite: %#p sleep\n", iso);
                       if(waserror()){
                               if(iso-&gt;err == nil)
                                       iso-&gt;err = "I/O timed out";
                               ilock(ctlr);
                               break;
                       }
                       tsleep(iso, isocanwrite, iso, ep-&gt;tmout);
                       poperror();
                       ilock(ctlr);
               }
               err = iso-&gt;err;
               iso-&gt;err = nil;
               if(iso-&gt;state == Qclose || err != nil){
                       iunlock(ctlr);
                       error(err ? err : Eio);
               }
               if(iso-&gt;state != Qrun)
                       panic("episowrite: iso not running");
               iunlock(ctlr);          /* We could page fault here */
               nw = putsamples(iso, b+tot, count-tot);
               ilock(ctlr);
       }
       if(iso-&gt;state != Qclose)
               iso-&gt;state = Qdone;
       iunlock(ctlr);
       err = iso-&gt;err;              /* in case it failed early */
       iso-&gt;err = nil;
       qunlock(iso);
       poperror();
       if(err != nil)
               error(err);
       diprint("ehci: episowrite: %#p %d bytes\n", iso, tot);
       return tot;
}

static int
nexttoggle(int toggle, int count, int maxpkt)
{
       int np;

       np = count / maxpkt;
       if(np == 0)
               np = 1;
       if((np % 2) == 0)
               return toggle;
       if(toggle == Tddata1)
               return Tddata0;
       else
               return Tddata1;
}

static Td*
epgettd(Qio *io, int flags, void *a, int count, int maxpkt)
{
       Td *td;
       ulong pa;
       int i;
       if(count &gt; Tdmaxpkt)
               panic("ehci: epgettd: too many bytes");
       td = tdalloc();
       td-&gt;csw = flags;
       td-&gt;csw |= io-&gt;toggle | io-&gt;tok | (count &lt;&lt; Tdlenshift);
       td-&gt;csw |= Tderr2|Tderr1;

       /*
        * use the space wasted by alignment as an
        * embedded buffer if count bytes fit in there.
        */
       assert(Align &gt; sizeof(Td));
       if(count &lt;= Align - sizeof(Td))
               td-&gt;data = td-&gt;sbuff;
       else
               td-&gt;data = td-&gt;buff = smalloc(Tdmaxpkt);

       pa = PADDR(td-&gt;data);
       for(i = 0; i &lt; nelem(td-&gt;buffer); i++){
               td-&gt;buffer[i] = pa;
               if(i &gt; 0)
                       td-&gt;buffer[i] &amp;= ~0xFFF;
               pa += 0x1000;
       }
       xcachewbse(td, sizeof *td);
       td-&gt;ndata = count;
       if(a != nil &amp;&amp; count &gt; 0){
               memmove(td-&gt;data, a, count);
               xcachewbse(td-&gt;data, count);
       }
       io-&gt;toggle = nexttoggle(io-&gt;toggle, count, maxpkt);
       return td;
}

/*
* Try to get them idle
*/
static void
aborttds(Qh *qh)
{
       Td *td;

       qh-&gt;state = Qdone;
       if(qh-&gt;sched &gt;= 0 &amp;&amp; (qh-&gt;eps0&amp;Qhspeedmask) != Qhhigh){
               qh-&gt;eps0 |= Qhint;   /* inactivate on next pass */
               xcachewbse(&amp;qh-&gt;eps0, sizeof qh-&gt;eps0);
       }
       for(td = qh-&gt;tds; td != nil; td = td-&gt;next){
               xcachewbinvse(&amp;td-&gt;csw, sizeof td-&gt;csw);
               if(td-&gt;csw &amp; Tdactive)
                       td-&gt;ndata = 0;
               td-&gt;csw |= Tdhalt;
               xcachewbse(&amp;td-&gt;csw, sizeof td-&gt;csw);
       }
}

/*
* Some controllers do not post the usb/error interrupt after
* the work has been done. It seems that we must poll for them.
*/
static int
workpending(void *a)
{
       Ctlr *ctlr;

       ctlr = a;
       return ctlr-&gt;nreqs &gt; 0;
}

static void
ehcipoll(void* a)
{
       Hci *hp;
       Ctlr *ctlr;
       Poll *poll;
       int i;

       hp = a;
       ctlr = hp-&gt;aux;
       poll = &amp;ctlr-&gt;poll;
       for(;;){
               if(ctlr-&gt;nreqs == 0){
                       if(0)ddprint("ehcipoll %#p sleep\n", ctlr-&gt;capio);
                       sleep(poll, workpending, ctlr);
                       if(0)ddprint("ehcipoll %#p awaken\n", ctlr-&gt;capio);
               }
               for(i = 0; i &lt; 16 &amp;&amp; ctlr-&gt;nreqs &gt; 0; i++)
                       if(ehciintr(hp) == 0)
                                break;
               do{
                       tsleep(&amp;up-&gt;sleep, return0, 0, 1);
                       ehciintr(hp);
               }while(ctlr-&gt;nreqs &gt; 0);
       }
}

static void
pollcheck(Hci *hp)
{
       Ctlr *ctlr;
       Poll *poll;

       ctlr = hp-&gt;aux;
       poll = &amp;ctlr-&gt;poll;

       if(poll-&gt;must != 0 &amp;&amp; poll-&gt;does == 0){
               lock(poll);
               if(poll-&gt;must != 0 &amp;&amp; poll-&gt;does == 0){
                       poll-&gt;does++;
                       print("ehci %#p: polling\n", ctlr-&gt;capio);
                       kproc("ehcipoll", ehcipoll, hp);
               }
               unlock(poll);
       }
}

static int
epiodone(void *a)
{
       Qh *qh;

       qh = a;
       return qh-&gt;state != Qrun;
}

static void
epiowait(Hci *hp, Qio *io, int tmout, ulong load)
{
       Qh *qh;
       int timedout;
       Ctlr *ctlr;

       ctlr = hp-&gt;aux;
       qh = io-&gt;qh;
       ddqprint("ehci io %#p sleep on qh %#p state %s\n",
               io, qh, qhsname[qh-&gt;state]);
       timedout = 0;
       if(waserror()){
               dqprint("ehci io %#p qh %#p timed out\n", io, qh);
               timedout++;
       }else{
               if(tmout == 0)
                       sleep(io, epiodone, qh);
               else
                       tsleep(io, epiodone, qh, tmout);
               poperror();
       }

       ilock(ctlr);
       /* Are we missing interrupts? */
       if(qh-&gt;state == Qrun){
               iunlock(ctlr);
               ehciintr(hp);
               ilock(ctlr);
               if(qh-&gt;state == Qdone){
                       dqprint("ehci %#p: polling required\n", ctlr-&gt;capio);
                       ctlr-&gt;poll.must = 1;
                       pollcheck(hp);
               }
       }

       if(qh-&gt;state == Qrun){
               dqprint("ehci io %#p qh %#p timed out (no intr?)\n", io, qh);
               timedout = 1;
       }else if(qh-&gt;state != Qdone &amp;&amp; qh-&gt;state != Qclose)
               panic("ehci: epio: queue state %d", qh-&gt;state);
       if(timedout){
               aborttds(io-&gt;qh);
               io-&gt;err = "request timed out";
               iunlock(ctlr);
               if(!waserror()){
                       tsleep(&amp;up-&gt;sleep, return0, 0, Abortdelay);
                       poperror();
               }
               ilock(ctlr);
       }
       if(qh-&gt;state != Qclose)
               qh-&gt;state = Qidle;
       qhlinktd(qh, nil);
       ctlr-&gt;load -= load;
       ctlr-&gt;nreqs--;
       iunlock(ctlr);
}

/*
* Non iso I/O.
* To make it work for control transfers, the caller may
* lock the Qio for the entire control transfer.
*/
static long
epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
{
       Td *td, *ltd, *td0, *ntd;
       Ctlr *ctlr;
       Qh* qh;
       long n, tot;
       char buf[128];
       uchar *c;
       int saved, ntds, tmout;
       ulong load;
       char *err;

       qh = io-&gt;qh;
       ctlr = ep-&gt;hp-&gt;aux;
       io-&gt;debug = ep-&gt;debug;
       tmout = ep-&gt;tmout;
       ddeprint("epio: %s ep%d.%d io %#p count %ld load %uld\n",
               io-&gt;tok == Tdtokin ? "in" : "out",
               ep-&gt;dev-&gt;nb, ep-&gt;nb, io, count, ctlr-&gt;load);
       if((debug &gt; 1 || ep-&gt;debug &gt; 1) &amp;&amp; io-&gt;tok != Tdtokin){
               seprintdata(buf, buf+sizeof(buf), a, count);
               print("echi epio: user data: %s\n", buf);
       }
       if(mustlock){
               qlock(io);
               if(waserror()){
                       qunlock(io);
                       nexterror();
               }
       }
       io-&gt;err = nil;
       ilock(ctlr);
       if(qh-&gt;state == Qclose){     /* Tds released by cancelio */
               iunlock(ctlr);
               error(io-&gt;err ? io-&gt;err : Eio);
       }
       if(qh-&gt;state != Qidle)
               panic("epio: qh not idle");
       qh-&gt;state = Qinstall;
       iunlock(ctlr);

       c = a;
       td0 = ltd = nil;
       load = tot = 0;
       do{
               n = (Tdmaxpkt / ep-&gt;maxpkt) * ep-&gt;maxpkt;
               if(count-tot &lt; n)
                       n = count-tot;
               if(io-&gt;tok != Tdtokin)
                       td = epgettd(io, Tdactive, c+tot, n, ep-&gt;maxpkt);
               else
                       td = epgettd(io, Tdactive, nil, n, ep-&gt;maxpkt);
               if(td0 == nil)
                       td0 = td;
               else
                       tdlinktd(ltd, td);
               ltd = td;
               tot += n;
               load += ep-&gt;load;
       }while(tot &lt; count);
       if(td0 == nil || ltd == nil)
               panic("epio: no td");

       ltd-&gt;csw |= Tdioc;   /* the last one interrupts */
       xcachewbse(&amp;ltd-&gt;csw, sizeof ltd-&gt;csw);

       ddeprint("ehci: load %uld ctlr load %uld\n", load, ctlr-&gt;load);
       if(debug &gt; 1 || ep-&gt;debug &gt; 1)
               dumptd(td0, "epio: put: ");

       ilock(ctlr);
       if(qh-&gt;state != Qclose){
               io-&gt;iotime = TK2MS(MACHP(0)-&gt;ticks);
               qh-&gt;state = Qrun;
               qhlinktd(qh, td0);
               ctlr-&gt;nreqs++;
               ctlr-&gt;load += load;
       }
       iunlock(ctlr);

       if(ctlr-&gt;poll.does)
               wakeup(&amp;ctlr-&gt;poll);

       epiowait(ep-&gt;hp, io, tmout, load);
       if(debug &gt; 1 || ep-&gt;debug &gt; 1){
               dumptd(td0, "epio: got: ");
               qhdump(qh);
       }

       tot = 0;
       c = a;
       saved = 0;
       ntds = 0;
       for(td = td0; td != nil; td = ntd){
               ntds++;
               /*
                * Use td tok, not io tok, because of setup packets.
                * Also, if the Td was stalled or active (previous Td
                * was a short packet), we must save the toggle as it is.
                */
               xcachewbinvse(&amp;ltd-&gt;csw, sizeof ltd-&gt;csw);
               if(td-&gt;csw &amp; (Tdhalt|Tdactive)){
                       if(saved++ == 0)
                               io-&gt;toggle = td-&gt;csw &amp; Tddata1;
               }else{
                       tot += td-&gt;ndata;
                       if((td-&gt;csw &amp; Tdtok) == Tdtokin &amp;&amp; td-&gt;ndata &gt; 0){
                               xcachewbinvse(td-&gt;data, td-&gt;ndata);
                               memmove(c, td-&gt;data, td-&gt;ndata);
                               c += td-&gt;ndata;
                       }
               }
               ntd = td-&gt;next;
               tdfree(td);
       }
       err = io-&gt;err;
       if(mustlock){
               qunlock(io);
               poperror();
       }
       ddeprint("epio: io %#p: %d tds: return %ld err '%s'\n",
               io, ntds, tot, err);
       if(err == Estalled)
               return 0;       /* that's our convention */
       if(err != nil)
               error(err);
       if(tot &lt; 0)
               error(Eio);
       return tot;
}

static long
epread(Ep *ep, void *a, long count)
{
       Ctlio *cio;
       Qio *io;
       Isoio *iso;
       char buf[160];
       ulong delta;

       ddeprint("ehci: epread\n");
       if(ep-&gt;aux == nil)
               panic("epread: not open");

       pollcheck(ep-&gt;hp);

       switch(ep-&gt;ttype){
       case Tctl:
               cio = ep-&gt;aux;
               qlock(cio);
               if(waserror()){
                       qunlock(cio);
                       nexterror();
               }
               ddeprint("epread ctl ndata %d\n", cio-&gt;ndata);
               if(cio-&gt;ndata &lt; 0)
                       error("request expected");
               else if(cio-&gt;ndata == 0){
                       cio-&gt;ndata = -1;
                       count = 0;
               }else{
                       if(count &gt; cio-&gt;ndata)
                               count = cio-&gt;ndata;
                       if(count &gt; 0)
                               memmove(a, cio-&gt;data, count);
                       /* BUG for big transfers */
                       free(cio-&gt;data);
                       cio-&gt;data = nil;
                       cio-&gt;ndata = 0;      /* signal EOF next time */
               }
               qunlock(cio);
               poperror();
               if(debug&gt;1 || ep-&gt;debug){
                       seprintdata(buf, buf+sizeof(buf), a, count);
                       print("epread: %s\n", buf);
               }
               return count;
       case Tbulk:
               io = ep-&gt;aux;
               if(ep-&gt;clrhalt)
                       clrhalt(ep);
               return epio(ep, &amp;io[OREAD], a, count, 1);
       case Tintr:
               io = ep-&gt;aux;
               delta = TK2MS(MACHP(0)-&gt;ticks) - io[OREAD].iotime + 1;
               if(delta &lt; ep-&gt;pollival / 2)
                       tsleep(&amp;up-&gt;sleep, return0, 0, ep-&gt;pollival/2 - delta);
               if(ep-&gt;clrhalt)
                       clrhalt(ep);
               return epio(ep, &amp;io[OREAD], a, count, 1);
       case Tiso:
               iso = ep-&gt;aux;
               return episoread(ep, iso, a, count);
       }
       return -1;
}

/*
* Control transfers are one setup write (data0)
* plus zero or more reads/writes (data1, data0, ...)
* plus a final write/read with data1 to ack.
* For both host to device and device to host we perform
* the entire transfer when the user writes the request,
* and keep any data read from the device for a later read.
* We call epio three times instead of placing all Tds at
* the same time because doing so leads to crc/tmout errors
* for some devices.
* Upon errors on the data phase we must still run the status
* phase or the device may cease responding in the future.
*/
static long
epctlio(Ep *ep, Ctlio *cio, void *a, long count)
{
       uchar *c;
       long len;

       ddeprint("epctlio: cio %#p ep%d.%d count %ld\n",
               cio, ep-&gt;dev-&gt;nb, ep-&gt;nb, count);
       if(count &lt; Rsetuplen)
               error("short usb comand");
       qlock(cio);
       free(cio-&gt;data);
       cio-&gt;data = nil;
       cio-&gt;ndata = 0;
       if(waserror()){
               qunlock(cio);
               free(cio-&gt;data);
               cio-&gt;data = nil;
               cio-&gt;ndata = 0;
               nexterror();
       }

       /* set the address if unset and out of configuration state */
       if(ep-&gt;dev-&gt;state != Dconfig &amp;&amp; ep-&gt;dev-&gt;state != Dreset)
               if(cio-&gt;usbid == 0){
                       cio-&gt;usbid = ((ep-&gt;nb&amp;Epmax)&lt;&lt;7)|(ep-&gt;dev-&gt;nb&amp;Devmax);
                       qhsetaddr(cio-&gt;qh, cio-&gt;usbid);
               }
       /* adjust maxpkt if the user has learned a different one */
       if(qhmaxpkt(cio-&gt;qh) != ep-&gt;maxpkt)
               qhsetmaxpkt(cio-&gt;qh, ep-&gt;maxpkt);
       c = a;
       cio-&gt;tok = Tdtoksetup;
       cio-&gt;toggle = Tddata0;
       if(epio(ep, cio, a, Rsetuplen, 0) &lt; Rsetuplen)
               error(Eio);
       a = c + Rsetuplen;
       count -= Rsetuplen;

       cio-&gt;toggle = Tddata1;
       if(c[Rtype] &amp; Rd2h){
               cio-&gt;tok = Tdtokin;
               len = GET2(c+Rcount);
               if(len &lt;= 0)
                       error("bad length in d2h request");
               if(len &gt; Maxctllen)
                       error("d2h data too large to fit in ehci");
               a = cio-&gt;data = smalloc(len+1);
       }else{
               cio-&gt;tok = Tdtokout;
               len = count;
       }
       if(len &gt; 0)
               if(waserror())
                       len = -1;
               else{
                       len = epio(ep, cio, a, len, 0);
                       poperror();
               }
       if(c[Rtype] &amp; Rd2h){
               count = Rsetuplen;
               cio-&gt;ndata = len;
               cio-&gt;tok = Tdtokout;
       }else{
               if(len &lt; 0)
                       count = -1;
               else
                       count = Rsetuplen + len;
               cio-&gt;tok = Tdtokin;
       }
       cio-&gt;toggle = Tddata1;
       epio(ep, cio, nil, 0, 0);
       qunlock(cio);
       poperror();
       ddeprint("epctlio cio %#p return %ld\n", cio, count);
       return count;
}

static long
epwrite(Ep *ep, void *a, long count)
{
       Qio *io;
       Ctlio *cio;
       Isoio *iso;
       ulong delta;

       pollcheck(ep-&gt;hp);

       ddeprint("ehci: epwrite ep%d.%d\n", ep-&gt;dev-&gt;nb, ep-&gt;nb);
       if(ep-&gt;aux == nil)
               panic("ehci: epwrite: not open");
       switch(ep-&gt;ttype){
       case Tctl:
               cio = ep-&gt;aux;
               return epctlio(ep, cio, a, count);
       case Tbulk:
               io = ep-&gt;aux;
               if(ep-&gt;clrhalt)
                       clrhalt(ep);
               return epio(ep, &amp;io[OWRITE], a, count, 1);
       case Tintr:
               io = ep-&gt;aux;
               delta = TK2MS(MACHP(0)-&gt;ticks) - io[OWRITE].iotime + 1;
               if(delta &lt; ep-&gt;pollival)
                       tsleep(&amp;up-&gt;sleep, return0, 0, ep-&gt;pollival - delta);
               if(ep-&gt;clrhalt)
                       clrhalt(ep);
               return epio(ep, &amp;io[OWRITE], a, count, 1);
       case Tiso:
               iso = ep-&gt;aux;
               return episowrite(ep, iso, a, count);
       }
       return -1;
}

static void
isofsinit(Ep *ep, Isoio *iso)
{
       long left;
       Sitd *td;
       Sitd *ltd;
       int i;
       ulong frno;

       left = 0;
       ltd = nil;
       frno = iso-&gt;td0frno;
       for(i = 0; i &lt; iso-&gt;nframes; i++){
               td = iso-&gt;sitdps[frno] = sitdalloc();
               td-&gt;data = iso-&gt;data + i * ep-&gt;maxpkt;
               td-&gt;epc = ep-&gt;dev-&gt;port &lt;&lt; Stdportshift;
               td-&gt;epc |= ep-&gt;dev-&gt;hub &lt;&lt; Stdhubshift;
               td-&gt;epc |= ep-&gt;nb &lt;&lt; Stdepshift;
               td-&gt;epc |= ep-&gt;dev-&gt;nb &lt;&lt; Stddevshift;
               td-&gt;mfs = (034 &lt;&lt; Stdscmshift) | (1 &lt;&lt; Stdssmshift);
               if(ep-&gt;mode == OREAD){
                       td-&gt;epc |= Stdin;
                       td-&gt;mdata = ep-&gt;maxpkt;
               }else{
                       td-&gt;mdata = (ep-&gt;hz+left) * ep-&gt;pollival / 1000;
                       td-&gt;mdata *= ep-&gt;samplesz;
                       left = (ep-&gt;hz+left) * ep-&gt;pollival % 1000;
                       if(td-&gt;mdata &gt; ep-&gt;maxpkt){
                               print("ehci: ep%d.%d: size &gt; maxpkt\n",
                                       ep-&gt;dev-&gt;nb, ep-&gt;nb);
                               print("size = %ld max = %ld\n",
                                       td-&gt;mdata,ep-&gt;maxpkt);
                               td-&gt;mdata = ep-&gt;maxpkt;
                       }
               }
               xcachewbse(td, sizeof *td);
               sitdinit(iso, td);
               if(ltd != nil)
                       ltd-&gt;next = td;
               ltd = td;
               frno = TRUNC(frno+ep-&gt;pollival, Nisoframes);
       }
       ltd-&gt;next = iso-&gt;sitdps[iso-&gt;td0frno];
}

static void
isohsinit(Ep *ep, Isoio *iso)
{
       long left;
       Itd *td;
       Itd *ltd;
       ulong i;
       ulong pa;
       int p;
       ulong frno;
       int ival;

       iso-&gt;hs = 1;
       ival = 1;
       if(ep-&gt;pollival &gt; 8)
               ival = ep-&gt;pollival/8;
       left = 0;
       ltd = nil;
       frno = iso-&gt;td0frno;
       for(i = 0; i &lt; iso-&gt;nframes; i++){
               td = iso-&gt;itdps[frno] = itdalloc();
               td-&gt;data = iso-&gt;data + i * 8  * iso-&gt;maxsize;
               pa = PADDR(td-&gt;data) &amp; ~0xFFF;
               for(p = 0; p &lt; 8; p++)
                       td-&gt;buffer[i] = pa + p * 0x1000;
               td-&gt;buffer[0] = PADDR(iso-&gt;data) &amp; ~0xFFF;
               td-&gt;buffer[0] |= ep-&gt;nb &lt;&lt; Itdepshift;
               td-&gt;buffer[0] |= ep-&gt;dev-&gt;nb &lt;&lt; Itddevshift;
               if(ep-&gt;mode == OREAD)
                       td-&gt;buffer[1] |= Itdin;
               else
                       td-&gt;buffer[1] |= Itdout;
               td-&gt;buffer[1] |= ep-&gt;maxpkt &lt;&lt; Itdmaxpktshift;
               td-&gt;buffer[2] |= ep-&gt;ntds &lt;&lt; Itdntdsshift;

               if(ep-&gt;mode == OREAD)
                       td-&gt;mdata = 8 * iso-&gt;maxsize;
               else{
                       td-&gt;mdata = (ep-&gt;hz + left) * ep-&gt;pollival / 1000;
                       td-&gt;mdata *= ep-&gt;samplesz;
                       left = (ep-&gt;hz + left) * ep-&gt;pollival % 1000;
               }
               xcachewbse(td, sizeof *td);
               itdinit(iso, td);
               if(ltd != nil)
                       ltd-&gt;next = td;
               ltd = td;
               frno = TRUNC(frno + ival, Nisoframes);
       }
}

static void
isoopen(Ctlr *ctlr, Ep *ep)
{
       Isoio *iso;
       int ival;       /* pollival in ms */
       int n;
       ulong frno;
       int i;
       int w;
       int woff;
       int tpf;                /* tds per frame */

       iso = ep-&gt;aux;
       switch(ep-&gt;mode){
       case OREAD:
               iso-&gt;tok = Tdtokin;
               break;
       case OWRITE:
               iso-&gt;tok = Tdtokout;
               break;
       default:
               error("iso i/o is half-duplex");
       }
       iso-&gt;usbid = (ep-&gt;nb&lt;&lt;7)|(ep-&gt;dev-&gt;nb &amp; Devmax);
       iso-&gt;state = Qidle;
       iso-&gt;debug = ep-&gt;debug;
       ival = ep-&gt;pollival;
       tpf = 1;
       if(ep-&gt;dev-&gt;speed == Highspeed){
               tpf = 8;
               if(ival &lt;= 8)
                       ival = 1;
               else
                       ival /= 8;
       }
       assert(ival != 0);
       iso-&gt;nframes = Nisoframes / ival;
       if(iso-&gt;nframes &lt; 3)
               error("uhci isoopen bug");      /* we need at least 3 tds */
       iso-&gt;maxsize = ep-&gt;ntds * ep-&gt;maxpkt;
       if(ctlr-&gt;load + ep-&gt;load &gt; 800)
               print("usb: ehci: bandwidth may be exceeded\n");
       ilock(ctlr);
       ctlr-&gt;load += ep-&gt;load;
       ctlr-&gt;isoload += ep-&gt;load;
       ctlr-&gt;nreqs++;
       dprint("ehci: load %uld isoload %uld\n", ctlr-&gt;load, ctlr-&gt;isoload);
       diprint("iso nframes %d pollival %uld ival %d maxpkt %uld ntds %d\n",
               iso-&gt;nframes, ep-&gt;pollival, ival, ep-&gt;maxpkt, ep-&gt;ntds);
       iunlock(ctlr);
       if(ctlr-&gt;poll.does)
               wakeup(&amp;ctlr-&gt;poll);

       /*
        * From here on this cannot raise errors
        * unless we catch them and release here all memory allocated.
        */
       assert(ep-&gt;maxpkt &gt; 0 &amp;&amp; ep-&gt;ntds &gt; 0 &amp;&amp; ep-&gt;ntds &lt; 4);
       assert(ep-&gt;maxpkt &lt;= 1024);
       iso-&gt;tdps = smalloc(sizeof(uintptr) * Nisoframes);
       iso-&gt;data = smalloc(iso-&gt;nframes * tpf * ep-&gt;ntds * ep-&gt;maxpkt);
       iso-&gt;td0frno = TRUNC(ctlr-&gt;opio-&gt;frno + 10, Nisoframes);
       /* read: now; write: 1s ahead */

       if(ep-&gt;dev-&gt;speed == Highspeed)
               isohsinit(ep, iso);
       else
               isofsinit(ep, iso);
       iso-&gt;tdu = iso-&gt;tdi = iso-&gt;itdps[iso-&gt;td0frno];
       iso-&gt;stdu = iso-&gt;stdi = iso-&gt;sitdps[iso-&gt;td0frno];

       ilock(ctlr);
       frno = iso-&gt;td0frno;
       for(i = 0; i &lt; iso-&gt;nframes; i++){
               *iso-&gt;tdps[frno] = ctlr-&gt;frames[frno];
               frno = TRUNC(frno+ival, Nisoframes);
       }

       /*
        * Iso uses a virtual frame window of Nisoframes, and we must
        * fill the actual ctlr frame array by placing ctlr-&gt;nframes/Nisoframes
        * copies of the window in the frame array.
        */
       assert(ctlr-&gt;nframes &gt;= Nisoframes &amp;&amp; Nisoframes &gt;= iso-&gt;nframes);
       assert(Nisoframes &gt;= Nintrleafs);
       n = ctlr-&gt;nframes / Nisoframes;
       for(w = 0; w &lt; n; w++){
               frno = iso-&gt;td0frno;
               woff = w * Nisoframes;
               for(i = 0; i &lt; iso-&gt;nframes ; i++){
                       assert(woff+frno &lt; ctlr-&gt;nframes);
                       assert(iso-&gt;tdps[frno] != nil);
                       if(ep-&gt;dev-&gt;speed == Highspeed)
                               ctlr-&gt;frames[woff+frno] = PADDR(iso-&gt;tdps[frno])|Litd;
                       else
                               ctlr-&gt;frames[woff+frno] = PADDR(iso-&gt;tdps[frno])|Lsitd;
                       frno = TRUNC(frno+ep-&gt;pollival, Nisoframes);
               }
       }
       xcachewb();
       iso-&gt;next = ctlr-&gt;iso;
       ctlr-&gt;iso = iso;
       iso-&gt;state = Qdone;
       iunlock(ctlr);
       if(debug &gt; 1 || iso-&gt;debug &gt;1)
               isodump(iso, 0);


}

/*
* Allocate the endpoint and set it up for I/O
* in the controller. This must follow what's said
* in Ep regarding configuration, including perhaps
* the saved toggles (saved on a previous close of
* the endpoint data file by epclose).
*/
static void
epopen(Ep *ep)
{
       Ctlr *ctlr;
       Ctlio *cio;
       Qio *io;
       int usbid;

       ctlr = ep-&gt;hp-&gt;aux;
       deprint("ehci: epopen ep%d.%d\n", ep-&gt;dev-&gt;nb, ep-&gt;nb);
       if(ep-&gt;aux != nil)
               panic("ehci: epopen called with open ep");
       if(waserror()){
               free(ep-&gt;aux);
               ep-&gt;aux = nil;
               nexterror();
       }
       switch(ep-&gt;ttype){
       case Tnone:
               error("endpoint not configured");
       case Tiso:
               ep-&gt;aux = smalloc(sizeof(Isoio));
               isoopen(ctlr, ep);
               break;
       case Tctl:
               cio = ep-&gt;aux = smalloc(sizeof(Ctlio));
               cio-&gt;debug = ep-&gt;debug;
               cio-&gt;ndata = -1;
               cio-&gt;data = nil;
               if(ep-&gt;dev-&gt;isroot != 0 &amp;&amp; ep-&gt;nb == 0)        /* root hub */
                       break;
               cio-&gt;qh = qhalloc(ctlr, ep, cio, "epc");
               break;
       case Tbulk:
               ep-&gt;pollival = 1;    /* assume this; doesn't really matter */
               /* and fall... */
       case Tintr:
               io = ep-&gt;aux = smalloc(sizeof(Qio)*2);
               io[OREAD].debug = io[OWRITE].debug = ep-&gt;debug;
               usbid = ((ep-&gt;nb&amp;Epmax)&lt;&lt;7)|(ep-&gt;dev-&gt;nb &amp;Devmax);
               assert(ep-&gt;pollival != 0);
               if(ep-&gt;mode != OREAD){
                       if(ep-&gt;toggle[OWRITE] != 0)
                               io[OWRITE].toggle = Tddata1;
                       else
                               io[OWRITE].toggle = Tddata0;
                       io[OWRITE].tok = Tdtokout;
                       io[OWRITE].usbid = usbid;
                       io[OWRITE].bw = ep-&gt;maxpkt*1000/ep-&gt;pollival; /* bytes/s */
                       io[OWRITE].qh = qhalloc(ctlr, ep, io+OWRITE, "epw");
               }
               if(ep-&gt;mode != OWRITE){
                       if(ep-&gt;toggle[OREAD] != 0)
                               io[OREAD].toggle = Tddata1;
                       else
                               io[OREAD].toggle = Tddata0;
                       io[OREAD].tok = Tdtokin;
                       io[OREAD].usbid = usbid;
                       io[OREAD].bw = ep-&gt;maxpkt*1000/ep-&gt;pollival; /* bytes/s */
                       io[OREAD].qh = qhalloc(ctlr, ep, io+OREAD, "epr");
               }
               break;
       }
       if(debug&gt;1 || ep-&gt;debug)
               dump(ep-&gt;hp);
       deprint("ehci: epopen done\n");
       poperror();
}

static void
cancelio(Ctlr *ctlr, Qio *io)
{
       Qh *qh;

       ilock(ctlr);
       qh = io-&gt;qh;
       if(io == nil || io-&gt;qh == nil || io-&gt;qh-&gt;state == Qclose){
               iunlock(ctlr);
               return;
       }
       dqprint("ehci: cancelio for qh %#p state %s\n",
               qh, qhsname[qh-&gt;state]);
       aborttds(qh);
       qh-&gt;state = Qclose;
       iunlock(ctlr);
       if(!waserror()){
               tsleep(&amp;up-&gt;sleep, return0, 0, Abortdelay);
               poperror();
       }
       wakeup(io);
       qlock(io);
       /* wait for epio if running */
       qunlock(io);

       qhfree(ctlr, qh);
       io-&gt;qh = nil;
}

static void
cancelisoio(Ctlr *ctlr, Isoio *iso, int pollival, ulong load)
{
       Isoio **il;
       ulong *lp;
       int i;
       int frno;
       int w;
       int n;
       int woff;
       ulong *tp;
       Itd *td;
       Sitd *std;
       int t;

       ilock(ctlr);
       if(iso-&gt;state == Qclose){
               iunlock(ctlr);
               return;
       }
       ctlr-&gt;nreqs--;
       if(iso-&gt;state != Qrun &amp;&amp; iso-&gt;state != Qdone)
               panic("bad iso state");
       iso-&gt;state = Qclose;
       if(ctlr-&gt;isoload &lt; load)
               panic("ehci: low isoload");
       ctlr-&gt;isoload -= load;
       ctlr-&gt;load -= load;
       for(il = &amp;ctlr-&gt;iso; *il != nil; il = &amp;(*il)-&gt;next)
               if(*il == iso)
                       break;
       if(*il == nil)
               panic("cancleiso: not found");
       *il = iso-&gt;next;

       frno = iso-&gt;td0frno;
       xcachewbinv();
       for(i = 0; i &lt; iso-&gt;nframes; i++){
               tp = iso-&gt;tdps[frno];
               if(iso-&gt;hs != 0){
                       td = iso-&gt;itdps[frno];
                       for(t = 0; t &lt; nelem(td-&gt;csw); t++)
                               td-&gt;csw[1] &amp;= ~(Itdioc|Itdactive);
               }else{
                       std = iso-&gt;sitdps[frno];
                       std-&gt;csw &amp;= ~(Stdioc|Stdactive);
               }
               for(lp=&amp;ctlr-&gt;frames[frno]; !(*lp &amp; Lterm); lp = &amp;LPTR(*lp)[0])
                       if(LPTR(*lp) == tp)
                               break;
               if(*lp &amp; Lterm)
                       panic("cancelisoio: td not found");
               *lp = tp[0];
               /*
                * Iso uses a virtual frame window of Nisoframes, and we must
                * restore pointers in copies of the window kept at ctlr-&gt;frames.
                */
               if(lp == &amp;ctlr-&gt;frames[frno]){
                       n = ctlr-&gt;nframes / Nisoframes;
                       for(w = 1; w &lt; n; w++){
                               woff = w * Nisoframes;
                               ctlr-&gt;frames[woff+frno] = *lp;
                       }
               }
               frno = TRUNC(frno+pollival, Nisoframes);
       }
       iunlock(ctlr);
       xcachewb();
       /*
        * wakeup anyone waiting for I/O and
        * wait to be sure no I/O is in progress in the controller.
        * and then wait to be sure episo* is no longer running.
        */
       wakeup(iso);
       diprint("cancelisoio iso %#p waiting for I/O to cease\n", iso);
       tsleep(&amp;up-&gt;sleep, return0, 0, 5);
       qlock(iso);
       qunlock(iso);
       diprint("cancelisoio iso %#p releasing iso\n", iso);

       frno = iso-&gt;td0frno;
       for(i = 0; i &lt; iso-&gt;nframes; i++){
               if(iso-&gt;hs != 0)
                       itdfree(iso-&gt;itdps[frno]);
               else
                       sitdfree(iso-&gt;sitdps[frno]);
               iso-&gt;tdps[frno] = nil;
               frno = TRUNC(frno+pollival, Nisoframes);
       }
       free(iso-&gt;tdps);
       iso-&gt;tdps = nil;
       free(iso-&gt;data);
       iso-&gt;data = nil;
}

static void
epclose(Ep *ep)
{
       Qio *io;
       Ctlio *cio;
       Isoio *iso;
       Ctlr *ctlr;

       ctlr = ep-&gt;hp-&gt;aux;
       deprint("ehci: epclose ep%d.%d\n", ep-&gt;dev-&gt;nb, ep-&gt;nb);

       if(ep-&gt;aux == nil)
               panic("ehci: epclose called with closed ep");
       switch(ep-&gt;ttype){
       case Tctl:
               cio = ep-&gt;aux;
               cancelio(ctlr, cio);
               free(cio-&gt;data);
               cio-&gt;data = nil;
               break;
       case Tintr:
       case Tbulk:
               io = ep-&gt;aux;
               ep-&gt;toggle[OREAD] = ep-&gt;toggle[OWRITE] = 0;
               if(ep-&gt;mode != OWRITE){
                       cancelio(ctlr, &amp;io[OREAD]);
                       if(io[OREAD].toggle == Tddata1)
                               ep-&gt;toggle[OREAD] = 1;
               }
               if(ep-&gt;mode != OREAD){
                       cancelio(ctlr, &amp;io[OWRITE]);
                       if(io[OWRITE].toggle == Tddata1)
                               ep-&gt;toggle[OWRITE] = 1;
               }
               break;
       case Tiso:
               iso = ep-&gt;aux;
               cancelisoio(ctlr, iso, ep-&gt;pollival, ep-&gt;load);
               break;
               break;
       default:
               panic("epclose: bad ttype");
       }
       free(ep-&gt;aux);
       ep-&gt;aux = nil;
}

static void
scanpci(void)           /* actually just use fixed addresses on sheeva */
{
       Ctlr *ctlr;
       static int already = 0;

       if(already)
               return;
       already = 1;

       ctlr = mallocz(sizeof(Ctlr), 1);
       /* the sheeva's usb 2.0 otg uses a superset of the ehci registers */
       ctlr-&gt;capio = (Ecapio *)(Addrusb + 0x100);
       ctlr-&gt;opio  = (Eopio *) (Addrusb + 0x140);
       dprint("usbehci: port %#p\n", ctlr-&gt;capio);

       ctlrs[0] = ctlr;
}

/*
* return smallest power of 2 &gt;= n
*/
static int
flog2(int n)
{
       int i;

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

/*
* build the periodic scheduling tree:
* framesize must be a multiple of the tree size
*/
static void
mkqhtree(Ctlr *ctlr)
{
       int i, n, d, o, leaf0, depth;
       Qh **tree;
       Qtree *qt;
       Qh *qh;
       ulong leafs[Nintrleafs];

       depth = flog2(Nintrleafs);
       n = (1 &lt;&lt; (depth+1)) - 1;
       qt = mallocz(sizeof(*qt), 1);
       if(qt == nil)
               panic("ehci: mkqhtree: no memory");
       qt-&gt;nel = n;
       qt-&gt;depth = depth;
       qt-&gt;bw = mallocz(n * sizeof(qt-&gt;bw), 1);
       qt-&gt;root = tree = mallocz(n * sizeof(Qh *), 1);
       if(qt-&gt;bw == nil || tree == nil)
               panic("ehci: mkqhtree: no memory");
       for(i = 0; i &lt; n; i++){
               qh = tree[i] = edalloc();
               if(qh == nil)
                       panic("ehci: mkqhtree: no memory");
               qh-&gt;nlink = qh-&gt;alink = qh-&gt;link = Lterm;
               qh-&gt;csw = Tdhalt;
               qh-&gt;state = Qidle;
               if(i &gt; 0)
                       qhlinkqh(tree[i], tree[(i-1)/2]);
       }
       ctlr-&gt;ntree = i;
       dprint("ehci: tree: %d endpoints allocated\n", i);

       /* distribute leaves evenly round the frame list */
       leaf0 = n / 2;
       for(i = 0; i &lt; Nintrleafs; i++){
               o = 0;
               for(d = 0; d &lt; depth; d++){
                       o &lt;&lt;= 1;
                       if(i &amp; (1 &lt;&lt; d))
                               o |= 1;
               }
               if(leaf0 + o &gt;= n){
                       print("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
                       break;
               }
               leafs[i] = PADDR(tree[leaf0 + o]) | Lqh;
       }
       assert((ctlr-&gt;nframes % Nintrleafs) == 0);
       for(i = 0; i &lt; ctlr-&gt;nframes; i += Nintrleafs)
               memmove(ctlr-&gt;frames + i, leafs, sizeof(leafs));
       ctlr-&gt;tree = qt;
       xcachewb();
}

static void
ehcimeminit(Ctlr *ctlr)
{
       int frsize;
       Eopio *opio;
       int i;

       opio = ctlr-&gt;opio;
       frsize = ctlr-&gt;nframes*sizeof(ulong);
       assert((frsize &amp; 0xFFF) == 0);              /* must be 4k aligned */
       ctlr-&gt;frames = xspanalloc(frsize, frsize, 0);
       if(ctlr-&gt;frames == nil)
               panic("ehci reset: no memory");

       for (i = 0; i &lt; ctlr-&gt;nframes; i++)
               ctlr-&gt;frames[i] = Lterm;
       opio-&gt;frbase = PADDR(ctlr-&gt;frames);
       opio-&gt;frno = 0;

       qhalloc(ctlr, nil, nil, nil);   /* init async list */
       mkqhtree(ctlr);                 /* init sync list */
       edfree(edalloc());              /* try to get some ones pre-allocated */

       dprint("ehci %#p flb %#ulx frno %#ulx\n",
               ctlr-&gt;capio, opio-&gt;frbase, opio-&gt;frno);
}

static void
init(Hci *hp)
{
       Ctlr *ctlr;
       Eopio *opio;
       int i;

       hp-&gt;highspeed = 1;
       ctlr = hp-&gt;aux;
       opio = ctlr-&gt;opio;
       dprint("ehci %#p init\n", ctlr-&gt;capio);

       ilock(ctlr);
       /*
        * Unless we activate frroll interrupt
        * some machines won't post other interrupts.
        */
       opio-&gt;intr = Iusb|Ierr|Iportchg|Ihcerr|Iasync;
       opio-&gt;cmd |= Cpse;
       opio-&gt;cmd |= Case;
       ehcirun(ctlr, 1);
       opio-&gt;config = Callmine;     /* reclaim all ports */

       for (i = 0; i &lt; hp-&gt;nports; i++)
               opio-&gt;portsc[i] = Pspower;
       iunlock(ctlr);
       xcachewbse(opio, sizeof *opio);
       if(debug &gt; 1)
               dump(hp);

}

#define WINTARG(ctl)    (((ctl) &gt;&gt; 4) &amp; 017)
#define WINATTR(ctl)    (((ctl) &gt;&gt; 8) &amp; 0377)
#define WIN64KSIZE(ctl) (((ctl) &gt;&gt; 16) + 1)

#define SIZETO64KSIZE(size) ((size) / (64*1024) - 1)

static void
addrmapdump(void)
{
       int i;
       ulong ctl, targ, attr, size64k;
       Kwusb *map;
       Usbwin *win;

       map = (Kwusb *)(Addrusb + 0x300);
       for (i = 0; i &lt; nelem(map-&gt;win); i++) {
               win = &amp;map-&gt;win[i];
               ctl = win-&gt;ctl;
               if (ctl &amp; Winenable) {
                       targ = WINTARG(ctl);
                       attr = WINATTR(ctl);
                       size64k = WIN64KSIZE(ctl);
                       print("usbehci: address map window %d: "
                               "targ %ld attr %#lux size %,ld addr %#lux\n",
                               i, targ, attr, size64k * 64*1024, win-&gt;base);
               }
       }
}

/* assumes ctlr is ilocked */
static void
ctlrreset(Ctlr *ctlr)
{
       int i;
       Eopio *opio;

       opio = ctlr-&gt;opio;
       opio-&gt;cmd |= Chcreset;
       /* wait for it to come out of reset */
       for(i = 0; i &lt; 100 &amp;&amp; opio-&gt;cmd &amp; Chcreset; i++)
               delay(1);
       if(i &gt;= 100)
               print("ehci %#p controller reset timed out\n", ctlr-&gt;capio);
       /*
        * Marvell errata FE-USB-340 workaround: 1 &lt;&lt; 4 magic:
        * disable streaming.  Magic 3 (usb host mode) from the linux driver
        * makes it work.  Ick.
        */
       opio-&gt;usbmode |= 1 &lt;&lt; 4 | 3;
       coherence();
}

/*
* configure window `win' as 256MB dram with attribute `attr' and
* base address
*/
static void
setaddrwin(Kwusb *kw, int win, int attr, ulong base)
{
       kw-&gt;win[win].ctl = Winenable | Targdram &lt;&lt; 4 | attr &lt;&lt; 8 |
               SIZETO64KSIZE(256*MB) &lt;&lt; 16;
       kw-&gt;win[win].base = base;
}

static void
ehcireset(Ctlr *ctlr)
{
       int i;
       ulong v;
       Eopio *opio;
       Kwusb *kw;

       ilock(ctlr);
       dprint("ehci %#p reset\n", ctlr-&gt;capio);
       opio = ctlr-&gt;opio;

       kw = (Kwusb *)(Addrusb + 0x300);
       kw-&gt;bic = 0;
       kw-&gt;bim = (1&lt;&lt;4) - 1;          /* enable all defined intrs */
       ctlrreset(ctlr);

       /*
        * clear high 32 bits of address signals if it's 64 bits capable.
        * This is probably not needed but it does not hurt and others do it.
        */
       if((ctlr-&gt;capio-&gt;capparms &amp; C64) != 0){
               dprint("ehci: 64 bits\n");
               opio-&gt;seg = 0;
       }

       /* requesting more interrupts per µframe may miss interrupts */
       opio-&gt;cmd |= Citc8;          /* 1 intr. per ms */
       switch(opio-&gt;cmd &amp; Cflsmask){
       case Cfls1024:
               ctlr-&gt;nframes = 1024;
               break;
       case Cfls512:
               ctlr-&gt;nframes = 512;
               break;
       case Cfls256:
               ctlr-&gt;nframes = 256;
               break;
       default:
               panic("ehci: unknown fls %ld", opio-&gt;cmd &amp; Cflsmask);
       }
       dprint("ehci: %d frames\n", ctlr-&gt;nframes);

       /*
        * set up the USB address map (bridge address decoding)
        */
       for (i = 0; i &lt; nelem(kw-&gt;win); i++)
               kw-&gt;win[i].ctl = kw-&gt;win[i].base = 0;
       coherence();

       setaddrwin(kw, 0, Attrcs0, 0);
       setaddrwin(kw, 1, Attrcs1, 256*MB);
//      setaddrwin(kw, 2, Attrcs0, (ulong)KADDR(0));
//      setaddrwin(kw, 3, Attrcs1, (ulong)KADDR(256*MB));
       coherence();

       if (kw-&gt;bcs &amp; (1 &lt;&lt; 4))
               print("usbehci: not swapping bytes\n");
       else
               print("usbehci: swapping bytes\n");
       addrmapdump();                          /* verify sanity */

       kw-&gt;pwrctl |= 1 &lt;&lt; 0 | 1 &lt;&lt; 1;           /* Pu | PuPll */
       coherence();

       /*
        * Marvell guideline GL-USB-160.
        */
       kw-&gt;phypll |= 1 &lt;&lt; 21;         /* VCOCAL_START: PLL calibration */
       coherence();
       microdelay(100);
       kw-&gt;phypll &amp;= ~(1 &lt;&lt; 21);

       v = kw-&gt;phytxctl &amp; ~(017 &lt;&lt; 27 | 7);       /* REG_EXT_FS_RCALL &amp; AMP_2_0 */
       /*
        * AMP_2_0 = 4 for 6281-A0 (but 3 for A1).
        * also set REG_EXT_FS_RCALL_EN | REG_RCAL_START.
        */
       kw-&gt;phytxctl = v | 1 &lt;&lt; 26 | 1 &lt;&lt; 12 | 4;        // TODO
       coherence();
       microdelay(100);
       kw-&gt;phytxctl &amp;= ~(1 &lt;&lt; 12);

       v = kw-&gt;phyrxctl &amp; ~(3 &lt;&lt; 2 | 017 &lt;&lt; 4); /* LPF_COEF_1_0 &amp; SQ_THRESH_3_0 */
       kw-&gt;phyrxctl = v | 1 &lt;&lt; 2 | 8 &lt;&lt; 4;

       v = kw-&gt;phyivref &amp; ~(3 &lt;&lt; 8);              /* TXVDD12 */
       kw-&gt;phyivref = v | 1 &lt;&lt; 8;             /* TODO: 1 for 6281-A0; 3 for A1 */

       coherence();

       ehcirun(ctlr, 0);
       ctlrreset(ctlr);

       iunlock(ctlr);
}

static void
setdebug(Hci*, int d)
{
       debug = d;
}

static void
shutdown(Hci *hp)
{
       Ctlr *ctlr;
       Eopio *opio;

       ctlr = hp-&gt;aux;
       ilock(ctlr);
       ctlrreset(ctlr);
       delay(100);
       ehcirun(ctlr, 0);
       opio = ctlr-&gt;opio;
       opio-&gt;frbase = 0;
       xcachewbse(opio, sizeof *opio);
       iunlock(ctlr);
}

static int
reset(Hci *hp)
{
       static Lock resetlck;
       int i;
       Ctlr *ctlr;
       Ecapio *capio;

       ilock(&amp;resetlck);
       scanpci();

       /*
        * Any adapter matches if no hp-&gt;port is supplied,
        * otherwise the ports must match.
        */
       ctlr = nil;
       for(i = 0; i &lt; Nhcis &amp;&amp; ctlrs[i] != nil; i++){
               ctlr = ctlrs[i];
               if(ctlr-&gt;active == 0)
               if(hp-&gt;port == 0 || hp-&gt;port == (uintptr)ctlr-&gt;capio){
                       ctlr-&gt;active = 1;
                       break;
               }
       }
       iunlock(&amp;resetlck);
       if(ctlrs[i] == nil || i == Nhcis)
               return -1;

       hp-&gt;aux = ctlr;
       hp-&gt;port = (uintptr)ctlr-&gt;capio;
       hp-&gt;irq = IRQ0usb0;
       hp-&gt;tbdf = 0;

       capio = ctlr-&gt;capio;
       hp-&gt;nports = capio-&gt;parms &amp; Cnports;

       ddprint("echi: %s, ncc %lud npcc %lud\n",
               capio-&gt;parms &amp; 0x10000 ? "leds" : "no leds",
               (capio-&gt;parms &gt;&gt; 12) &amp; 0xf, (capio-&gt;parms &gt;&gt; 8) &amp; 0xf);
       ddprint("ehci: routing %s, %sport power ctl, %d ports\n",
               capio-&gt;parms &amp; 0x40 ? "explicit" : "automatic",
               capio-&gt;parms &amp; 0x10 ? "" : "no ", hp-&gt;nports);

       ehcireset(ctlr);
       ehcimeminit(ctlr);

       /*
        * Linkage to the generic HCI driver.
        */
       hp-&gt;init = init;
       hp-&gt;dump = dump;
       hp-&gt;interrupt = interrupt;
       hp-&gt;epopen = epopen;
       hp-&gt;epclose = epclose;
       hp-&gt;epread = epread;
       hp-&gt;epwrite = epwrite;
       hp-&gt;seprintep = seprintep;
       hp-&gt;portenable = portenable;
       hp-&gt;portreset = portreset;
       hp-&gt;portstatus = portstatus;
       hp-&gt;shutdown = shutdown;
       hp-&gt;debug = setdebug;
       hp-&gt;type = "ehci";
       return 0;
}

void
usbehcilink(void)
{
       addhcitype("ehci", reset);
}
<!-- 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>