/*
* USB Enhanced Host Controller Interface (EHCI) driver
* High speed USB 2.0.
*
* Note that all of our unlock routines call coherence.
*
* 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.
*/
Enabledelay = 100, /* waiting for a port to enable */
Abortdelay = 10, /* 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->nframes */
/* Also, keep Nisoframes >= Nintrleafs */
Nintrleafs = 32, /* nb. of leaf frames in intr. tree */
Nisoframes = 64, /* nb. of iso frames (in window) */
/*
* 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 -> Qdone -> Qrun... -> 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->maxpkt */
long nleft; /* number of bytes left from last write */
int debug; /* debug flag from the endpoint */
int delay; /* max number of bytes to buffer */
int hs; /* is high speed? */
Isoio* next; /* in list of active Isoios */
int ival; /* ep->pollival (/8 for HS) */
int uframes; /* ep->uframes */
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 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, 108 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 */
ulong _pad0; /* pad to next cache line */
/* cache-line boundary here */
/* software */
Itd* next;
ulong ndata; /* number of bytes in data */
ulong mdata; /* max number of bytes in data */
ushort posi, posp;
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 */
/* cache-line boundary after xbuffer[0] */
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 */
/* cache-line boundary here */
ulong xbuffer[5]; /* high 32 bits of buffer for 64-bits */
/* software */
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: 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 tclink; /* 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 */
/* cache-line boundary after buffer[0] */
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 -> Qinstall -> Qrun -> 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 transfers 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];
};
/*
* 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;
static int
ctlrinterrupt(Ctlr *ctlr)
{
Eopio *opio;
Isoio *iso;
ulong sts;
Qh *qh;
int i, some;
opio = ctlr->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.
*/
sts = opio->sts & Sintrs;
if(sts == 0) /* not ours; shared intr. */
return 0;
opio->sts = sts;
coherence();
ctlr->nintr++;
if((sts & Sherr) != 0)
iprint("ehci: port %#p fatal host system error\n", ctlr->capio);
if((sts & Shalted) != 0)
iprint("ehci: port %#p: halted\n", ctlr->capio);
if((sts & 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 & (Serrintr|Sintr)) != 0){
ctlr->ntdintr++;
if(ehcidebug > 1){
iprint("ehci port %#p frames %#p nintr %d ntdintr %d",
ctlr->capio, ctlr->frames,
ctlr->nintr, ctlr->ntdintr);
iprint(" nqhintr %d nisointr %d\n",
ctlr->nqhintr, ctlr->nisointr);
iprint("\tcmd %#lux sts %#lux intr %#lux frno %uld",
opio->cmd, opio->sts, opio->intr, opio->frno);
}
/* process the Iso transfers */
for(iso = ctlr->iso; iso != nil; iso = iso->next)
if(iso->state == Qrun || iso->state == Qdone)
if(iso->hs != 0)
some += isohsinterrupt(ctlr, iso);
else
some += isofsinterrupt(ctlr, iso);
/* process the qhs in the periodic tree */
for(qh = ctlr->intrqhs; qh != nil; qh = qh->inext)
if(qh->state == Qrun)
some += qhinterrupt(ctlr, qh);
/* process the async Qh circular list */
qh = ctlr->qhs;
i = 0;
do{
if(qh == nil)
break;
if(qh->state == Qrun)
some += qhinterrupt(ctlr, qh);
qh = qh->next;
}while(qh != ctlr->qhs && i++ < 100);
if(i > 100)
iprint("echi: interrupt: qh loop?\n");
}
return some;
}
static int
ehciintr(Hci *hp)
{
Ctlr *ctlr;
int some;
/*
* 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->opio;
dprint("ehci %#p port %d: %s speed device: no longer owned\n",
ctlr->capio, port, ss);
s = opio->portsc[port-1] & ~(Pschange|Psstatuschg);
opio->portsc[port-1] = s | Psowner;
coherence();
}
static int
portreset(Hci *hp, int port, int on)
{
ulong *portscp;
Eopio *opio;
Ctlr *ctlr;
int i;
if(on == 0)
return 0;
ctlr = hp->aux;
opio = ctlr->opio;
eqlock(&ctlr->portlck);
if(waserror()){
iunlock(ctlr);
qunlock(&ctlr->portlck);
nexterror();
}
portscp = &opio->portsc[port-1];
dprint("ehci %#p port %d reset; sts %#lux\n", ctlr->capio, port, *portscp);
ilock(ctlr);
/* Shalted must be zero, else Psreset will stay set */
if (opio->sts & Shalted)
iprint("ehci %#p: halted yet trying to reset port\n",
ctlr->capio);
*portscp = (*portscp & ~Psenable) | Psreset; /* initiate reset */
/*
* usb 2 spec: reset must finish within 20 ms.
* linux says spec says it can take 50 ms. for hubs.
*/
delay(50);
*portscp &= ~Psreset; /* terminate reset */
iunlock(ctlr);
dprint("ehci %#p after port %d reset; sts %#lux\n",
ctlr->capio, port, *portscp);
qunlock(&ctlr->portlck);
poperror();
return 0;
}
static int
portstatus(Hci *hp, int port)
{
int s, r;
Eopio *opio;
Ctlr *ctlr;
ctlr = hp->aux;
opio = ctlr->opio;
eqlock(&ctlr->portlck);
if(waserror()){
iunlock(ctlr);
qunlock(&ctlr->portlck);
nexterror();
}
ilock(ctlr);
s = opio->portsc[port-1];
if(s & (Psstatuschg | Pschange)){
opio->portsc[port-1] = s;
coherence();
ddprint("ehci %#p port %d status %#x\n", ctlr->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 & Pspresent) != 0 && (s & Pslinemask) == Pslow){
portlend(ctlr, port, "low");
s &= ~Pspresent; /* not for us this time */
}
iunlock(ctlr);
qunlock(&ctlr->portlck);
poperror();
/*
* We must return status bits as a
* get port status hub request would do.
*/
r = 0;
if(s & Pspresent)
r |= HPpresent|HPhigh;
if(s & Psenable)
r |= HPenable;
if(s & Pssuspend)
r |= HPsuspend;
if(s & Psreset)
r |= HPreset;
if(s & Psstatuschg)
r |= HPstatuschg;
if(s & 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->qh, io->usbid);
s = seprint(s,e," iot %ld", io->iotime);
s = seprint(s,e," tog %#x tok %#x err %s", io->toggle, io->tok, io->err);
return s;
}
/*
* iso->tdu is the next place to put data. When it gets full
* it is activated and tdu advanced.
*/
static long
putsamples(Ctlr *ctlr, Isoio *iso, uchar *b, long count)
{
long left, tot, n;
Sitd *stdu;
Itd *tdu;
for(tot = 0; isocanwrite(iso) && tot < count; tot += n){
n = count-tot;
left = iso->nleft;
if(iso->hs != 0){
tdu = iso->tdu;
if(n > tdu->mdata - left)
n = tdu->mdata - left;
iunlock(ctlr); /* We could page fault here */
memmove(tdu->data + left, b + tot, n);
ilock(ctlr);
if(iso->tdu != tdu)
continue;
iso->nleft += n;
if(iso->nleft == tdu->mdata){
itdinit(ctlr, iso, tdu);
iso->tdu = tdu->next;
iso->nleft = 0;
}
}else{
stdu = iso->stdu;
if(n > stdu->mdata - left)
n = stdu->mdata - left;
iunlock(ctlr); /* We could page fault here */
memmove(stdu->data + left, b + tot, n);
ilock(ctlr);
if(iso->stdu != stdu)
continue;
iso->nleft += n;
if(iso->nleft == stdu->mdata){
sitdinit(ctlr, iso, stdu);
iso->stdu = stdu->next;
iso->nleft = 0;
}
}
}
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, nw;
char *err;
/*
* 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->nreqs > 0;
}
static void
ehcipoll(void* a)
{
Hci *hp;
Ctlr *ctlr;
Poll *poll;
int i;
/*
* 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)
{
int saved, ntds, tmout;
long n, tot;
ulong load;
char *err;
char buf[128];
uchar *c;
Ctlr *ctlr;
Qh* qh;
Td *td, *ltd, *td0, *ntd;
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, we must save the next toggle value from the
* last completed Td (in case of a short packet, or
* fewer than the requested number of packets in the
* Td being transferred).
*/
if(td->csw & (Tdhalt|Tdactive))
saved++;
else{
if(!saved){
io->toggle = td->csw & Tddata1;
coherence();
}
if(err == nil && (n = td->ndata) > 0 && tot < count){
if((tot + n) > count)
n = count - tot;
if(c != nil && (td->csw & Tdtok) == Tdtokin){
if(td->buff != nil){
dmaflush(0, td->data, n);
}
memmove(c, td->data, n);
c += n;
}
tot += n;
}
}
ntd = td->next;
tdfree(ctlr, td);
}
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);
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->aux == nil)
panic("epread: not open");
pollcheck(ep->hp);
switch(ep->ttype){
case Tctl:
cio = ep->aux;
eqlock(cio);
if(waserror()){
qunlock(cio);
nexterror();
}
ddeprint("epread ctl ndata %d\n", cio->ndata);
if(cio->ndata < 0)
error("request expected");
else if(cio->ndata == 0){
cio->ndata = -1;
count = 0;
}else{
if(count > cio->ndata)
count = cio->ndata;
if(count > 0)
memmove(a, cio->data, count);
/* BUG for big transfers */
free(cio->data);
cio->data = nil;
cio->ndata = 0; /* signal EOF next time */
}
qunlock(cio);
poperror();
if(ehcidebug>1 || ep->debug){
seprintdata(buf, buf+sizeof(buf), a, count);
print("epread: %s\n", buf);
}
return count;
case Tbulk:
io = ep->aux;
if(ep->clrhalt)
clrhalt(ep);
return epio(ep, &io[OREAD], a, count, 1);
case Tintr:
io = ep->aux;
delta = TK2MS(MACHP(0)->ticks) - io[OREAD].iotime + 1;
if(delta < ep->pollival / 2)
tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta);
if(ep->clrhalt)
clrhalt(ep);
return epio(ep, &io[OREAD], a, count, 1);
case Tiso:
iso = ep->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;
/* set the address if unset and out of configuration state */
if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
if(cio->usbid == 0){
cio->usbid = (ep->nb&Epmax)<<7 | (ep->dev->nb&Devmax);
coherence();
qhsetaddr(cio->qh, cio->usbid);
}
/* adjust maxpkt if the user has learned a different one */
if(qhmaxpkt(cio->qh) != ep->maxpkt)
qhsetmaxpkt(cio->qh, ep->maxpkt);
c = a;
cio->tok = Tdtoksetup;
cio->toggle = Tddata0;
coherence();
if(epio(ep, cio, a, Rsetuplen, 0) < Rsetuplen)
error(Eio);
a = c + Rsetuplen;
count -= Rsetuplen;
cio->toggle = Tddata1;
if(c[Rtype] & Rd2h){
cio->tok = Tdtokin;
len = GET2(c+Rcount);
if(len <= 0)
error("bad length in d2h request");
if(len > Maxctllen)
error("d2h data too large to fit in ehci");
a = cio->data = smalloc(len+1);
}else{
cio->tok = Tdtokout;
len = count;
}
coherence();
if(len > 0)
if(waserror())
len = -1;
else{
len = epio(ep, cio, a, len, 0);
poperror();
}
if(c[Rtype] & Rd2h){
count = Rsetuplen;
cio->ndata = len;
cio->tok = Tdtokout;
}else{
if(len < 0)
count = -1;
else
count = Rsetuplen + len;
cio->tok = Tdtokin;
}
cio->toggle = Tddata1;
coherence();
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->hp);
ddeprint("ehci: epwrite ep%d.%d\n", ep->dev->nb, ep->nb);
if(ep->aux == nil)
panic("ehci: epwrite: not open");
switch(ep->ttype){
case Tctl:
cio = ep->aux;
return epctlio(ep, cio, a, count);
case Tbulk:
io = ep->aux;
if(ep->clrhalt)
clrhalt(ep);
return epio(ep, &io[OWRITE], a, count, 1);
case Tintr:
io = ep->aux;
delta = TK2MS(MACHP(0)->ticks) - io[OWRITE].iotime + 1;
if(delta < ep->pollival)
tsleep(&up->sleep, return0, 0, ep->pollival - delta);
if(ep->clrhalt)
clrhalt(ep);
return epio(ep, &io[OWRITE], a, count, 1);
case Tiso:
iso = ep->aux;
return episowrite(ep, iso, a, count);
}
return -1;
}
static void
isofsinit(Ep *ep, Isoio *iso)
{
long left;
Sitd *td, *ltd;
int i;
ulong frno;
Ctlr *ctlr;
/*
* Iso uses a virtual frame window of Nisoframes, and we must
* fill the actual ctlr frame array by placing ctlr->nframes/Nisoframes
* copies of the window in the frame array.
*/
assert(ctlr->nframes >= Nisoframes && Nisoframes >= iso->nframes);
assert(Nisoframes >= Nintrleafs);
n = ctlr->nframes / Nisoframes;
for(w = 0; w < n; w++){
frno = iso->td0frno;
woff = w * Nisoframes;
for(i = 0; i < iso->nframes; i++){
assert(woff+frno < ctlr->nframes);
assert(iso->tdps[frno] != nil);
ctlr->frames[woff+frno] = PADDR(iso->tdps[frno]) | (iso->hs? Litd: Lsitd);
coherence();
frno = TRUNC(frno+iso->ival, Nisoframes);
}
}
coherence();
iso->next = ctlr->iso;
ctlr->iso = iso;
coherence();
iso->state = Qdone;
iunlock(ctlr);
if(ehcidebug > 1 || iso->debug >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;
frno = iso->td0frno;
for(i = 0; i < iso->nframes; i++){
tp = iso->tdps[frno];
if(iso->hs){
Itd *td = iso->itdps[frno];
for(t = 0; t < nelem(td->csw); t++)
td->csw[t] &= ~(Itdioc|Itdactive);
}else{
Sitd *std = iso->sitdps[frno];
std->csw &= ~(Stdioc|Stdactive);
}
coherence();
for(lp = &ctlr->frames[frno]; !(*lp & Lterm); lp = &LPTR(*lp)[0])
if(LPTR(*lp) == tp)
break;
if(*lp & 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->frames.
*/
if(lp == &ctlr->frames[frno]){
n = ctlr->nframes / Nisoframes;
for(w = 1; w < n; w++){
woff = w * Nisoframes;
ctlr->frames[woff+frno] = *lp;
}
}
coherence();
frno = TRUNC(frno+iso->ival, Nisoframes);
}
iunlock(ctlr);
/*
* 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);
if(!waserror()){
tsleep(&up->sleep, return0, 0, 5);
poperror();
}
qlock(iso);
qunlock(iso);
diprint("cancelisoio iso %#p releasing iso\n", iso);
if(ep->aux == nil)
panic("ehci: epclose called with closed ep");
switch(ep->ttype){
case Tctl:
cio = ep->aux;
cancelio(ctlr, cio);
free(cio->data);
cio->data = nil;
break;
case Tintr:
case Tbulk:
io = ep->aux;
ep->toggle[OREAD] = ep->toggle[OWRITE] = 0;
if(ep->mode != OWRITE){
cancelio(ctlr, &io[OREAD]);
if(io[OREAD].toggle == Tddata1)
ep->toggle[OREAD] = 1;
}
if(ep->mode != OREAD){
cancelio(ctlr, &io[OWRITE]);
if(io[OWRITE].toggle == Tddata1)
ep->toggle[OWRITE] = 1;
}
coherence();
break;
case Tiso:
iso = ep->aux;
cancelisoio(ctlr, iso, ep->load);
break;
default:
panic("epclose: bad ttype");
}
free(ep->aux);
ep->aux = nil;
}
/*
* return smallest power of 2 >= n
*/
static int
flog2(int n)
{
int i;
for(i = 0; (1 << i) < 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;
ulong leafs[Nintrleafs];
Qh *qh;
Qh **tree;
Qtree *qt;
for (i = 0; i < ctlr->nframes; i++)
ctlr->frames[i] = Lterm;
opio->frbase = PADDR(ctlr->frames);
opio->frno = 0;
coherence();
qhalloc(ctlr, nil, nil, nil); /* init async list */
mkqhtree(ctlr); /* init sync list */
edfree(edalloc(ctlr)); /* try to get some ones pre-allocated */