Qfree,
Enabledelay = 100, /* waiting for a port to enable */
- Abortdelay = 5, /* delay after cancelling Tds (ms) */
+ Abortdelay = 10, /* delay after cancelling Tds (ms) */
Incr = 64, /* for pools of Tds, Qhs, etc. */
Align = 128, /* in bytes for all those descriptors */
qlock(&ctlr->portlck);
ctlr->opio->cmd |= Ciasync; /* ask for intr. on async advance */
coherence();
- for(i = 0; i < 3 && qhadvanced(ctlr) == 0; i++)
- if(!waserror()){
- tsleep(ctlr, qhadvanced, ctlr, Abortdelay);
- poperror();
- }
+ for(i = 0; i < 3 && qhadvanced(ctlr) == 0; i++){
+ while(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");
static void
qhfree(Ctlr *ctlr, Qh *qh)
{
- Td *td, *ltd;
+ Td *td;
Qh *q;
- if(qh == nil)
- return;
ilock(ctlr);
if(qh->sched < 0){
for(q = ctlr->qhs; q != nil; q = q->next)
coherence();
}else
unschedq(ctlr, qh);
+ qh->state = Qfree; /* paranoia */
iunlock(ctlr);
qhcoherency(ctlr);
- for(td = qh->tds; td != nil; td = ltd){
- ltd = td->next;
+ while((td = qh->tds) != nil){
+ qh->tds = td->next;
tdfree(td);
}
Itd *tdi;
tdi = iso->tdi;
- assert(tdi != nil);
- if(itdactive(tdi)) /* not all tds are done */
+ if(tdi == nil || itdactive(tdi)) /* not all tds are done */
return 0;
ctlr->nisointr++;
ddiprint("isohsintr: iso %#p: tdi %#p tdu %#p\n", iso, tdi, iso->tdu);
Sitd *stdi;
stdi = iso->stdi;
- assert(stdi != nil);
- if((stdi->csw & Stdactive) != 0) /* nothing new done */
+ if(stdi == nil || (stdi->csw & Stdactive) != 0) /* nothing new done */
return 0;
ctlr->nisointr++;
ddiprint("isofsintr: iso %#p: tdi %#p tdu %#p\n", iso, stdi, iso->stdu);
panic("qhinterrupt: qh state");
td = qh->tds;
if(td == nil)
- panic("qhinterrupt: no tds");
+ return 0;
if((td->csw & Tdactive) == 0)
ddqprint("qhinterrupt port %#p qh %#p\n", ctlr->capio, qh);
for(; td != nil; td = td->next){
}
static int
-ehciintr(Hci *hp)
+ctlrinterrupt(Ctlr *ctlr)
{
- Ctlr *ctlr;
Eopio *opio;
Isoio *iso;
ulong sts;
Qh *qh;
int i, some;
- ctlr = hp->aux;
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.
*/
- ilock(ctlr);
- ctlr->nintr++;
sts = opio->sts & Sintrs;
- if(sts == 0){ /* not ours; shared intr. */
- iunlock(ctlr);
+ if(sts == 0) /* not ours; shared intr. */
return 0;
- }
opio->sts = sts;
coherence();
+ ctlr->nintr++;
if((sts & Sherr) != 0)
- print("ehci: port %#p fatal host system error\n", ctlr->capio);
+ iprint("ehci: port %#p fatal host system error\n", ctlr->capio);
if((sts & Shalted) != 0)
- print("ehci: port %#p: halted\n", ctlr->capio);
+ iprint("ehci: port %#p: halted\n", ctlr->capio);
if((sts & Sasync) != 0){
dprint("ehci: doorbell\n");
wakeup(ctlr);
if((sts & (Serrintr|Sintr)) != 0){
ctlr->ntdintr++;
if(ehcidebug > 1){
- print("ehci port %#p frames %#p nintr %d ntdintr %d",
+ iprint("ehci port %#p frames %#p nintr %d ntdintr %d",
ctlr->capio, ctlr->frames,
ctlr->nintr, ctlr->ntdintr);
- print(" nqhintr %d nisointr %d\n",
+ iprint(" nqhintr %d nisointr %d\n",
ctlr->nqhintr, ctlr->nisointr);
- print("\tcmd %#lux sts %#lux intr %#lux frno %uld",
+ iprint("\tcmd %#lux sts %#lux intr %#lux frno %uld",
opio->cmd, opio->sts, opio->intr, opio->frno);
}
qh = ctlr->qhs;
i = 0;
do{
- if (qh == nil)
- panic("ehciintr: nil qh");
+ if(qh == nil)
+ break;
if(qh->state == Qrun)
some += qhinterrupt(ctlr, qh);
qh = qh->next;
}while(qh != ctlr->qhs && i++ < 100);
if(i > 100)
- print("echi: interrupt: qh loop?\n");
+ iprint("echi: interrupt: qh loop?\n");
}
-// if (some == 0)
-// panic("ehciintr: no work");
+ return some;
+}
+
+static int
+ehciintr(Hci *hp)
+{
+ Ctlr *ctlr;
+ int some;
+
+ ctlr = hp->aux;
+ ilock(ctlr);
+ some = ctlrinterrupt(ctlr);
iunlock(ctlr);
return some;
}
if (opio->sts & Shalted)
iprint("ehci %#p: halted yet trying to reset port\n",
ctlr->capio);
- *portscp = (*portscp & ~Psenable) | Psreset; /* initiate reset */
- coherence();
+ *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 */
+
+ delay(10);
for(i = 0; *portscp & Psreset && i < 10; i++)
delay(10);
+
if (*portscp & Psreset)
- iprint("ehci %#p: port %d didn't reset within %d ms; sts %#lux\n",
- ctlr->capio, port, i * 10, *portscp);
- *portscp &= ~Psreset; /* force appearance of reset done */
- coherence();
- delay(10); /* ehci spec: enable within 2 ms. */
+ iprint("ehci %#p: port %d didn't reset; sts %#lux\n",
+ ctlr->capio, port, *portscp);
+ delay(10); /* ehci spec: enable within 2 ms. */
if((*portscp & Psenable) == 0)
portlend(ctlr, port, "full");
iunlock(ctlr); /* We could page fault here */
memmove(b+tot, tdu->data, nr);
ilock(ctlr);
+ if(iso->tdu != tdu)
+ continue;
if(nr < tdu->ndata)
memmove(tdu->data, tdu->data+nr, tdu->ndata - nr);
tdu->ndata -= nr;
iunlock(ctlr); /* We could page fault here */
memmove(b+tot, stdu->data, nr);
ilock(ctlr);
+ if(iso->stdu != stdu)
+ continue;
if(nr < stdu->ndata)
memmove(stdu->data, stdu->data+nr,
stdu->ndata - nr);
if(count <= Align - sizeof(Td)){
td->data = td->sbuff;
td->buff = nil;
- }else
- td->data = td->buff = smalloc(Tdmaxpkt);
+ } else if(count <= 0x4000){
+ td->buff = td->data = smalloc(count);
+ } else {
+ td->buff = smalloc(count + 0x1000);
+ td->data = (uchar*)ROUND((uintptr)td->buff, 0x1000);
+ }
pa = PADDR(td->data);
for(i = 0; i < nelem(td->buffer); i++){
td->buffer[i] = pa;
- if(i > 0)
- td->buffer[i] &= ~0xFFF;
+ pa &= ~0xFFF;
pa += 0x1000;
}
td->ndata = count;
{
Td *td;
- qh->state = Qdone;
- coherence();
if(qh->sched >= 0 && (qh->eps0 & Qhspeedmask) != Qhhigh)
qh->eps0 |= Qhint; /* inactivate on next pass */
+ qh->csw = (qh->csw & ~Tdactive) | Tdhalt;
coherence();
for(td = qh->tds; td != nil; td = td->next){
- if(td->csw & Tdactive)
+ if(td->csw & Tdactive){
td->ndata = 0;
- td->csw |= Tdhalt;
- coherence();
+ td->csw |= Tdhalt;
+ coherence();
+ }
}
}
hp = a;
ctlr = hp->aux;
poll = &ctlr->poll;
+ while(waserror())
+ ;
for(;;){
if(ctlr->nreqs == 0){
if(0)ddprint("ehcipoll %#p sleep\n", ctlr->capio);
ilock(ctlr);
/* Are we missing interrupts? */
if(qh->state == Qrun){
- iunlock(ctlr);
- ehciintr(hp);
- ilock(ctlr);
+ ctlrinterrupt(ctlr);
if(qh->state == Qdone){
dqprint("ehci %#p: polling required\n", ctlr->capio);
ctlr->poll.must = 1;
}else if(qh->state != Qdone && qh->state != Qclose)
panic("ehci: epio: queue state %d", qh->state);
if(timedout){
- aborttds(io->qh);
- io->err = "request timed out";
+ aborttds(qh);
+ qh->state = Qdone;
+ if(io->err == nil)
+ io->err = "request timed out";
iunlock(ctlr);
- if(!waserror()){
- tsleep(&up->sleep, return0, 0, Abortdelay);
- poperror();
- }
+ while(waserror())
+ ;
+ tsleep(&up->sleep, return0, 0, Abortdelay);
+ poperror();
ilock(ctlr);
}
if(qh->state != Qclose)
qh->state = Qidle;
- coherence();
qhlinktd(qh, nil);
ctlr->load -= load;
ctlr->nreqs--;
Qh* qh;
Td *td, *ltd, *td0, *ntd;
- qh = io->qh;
ctlr = ep->hp->aux;
io->debug = ep->debug;
tmout = ep->tmout;
}
io->err = nil;
ilock(ctlr);
- if(qh->state == Qclose){ /* Tds released by cancelio */
+ qh = io->qh;
+ if(qh == nil || qh->state == Qclose){ /* Tds released by cancelio */
iunlock(ctlr);
error(io->err ? io->err : Eio);
}
dumptd(td0, "epio: got: ");
qhdump(qh);
}
+ err = io->err;
tot = 0;
c = a;
io->toggle = td->csw & Tddata1;
coherence();
}
- tot += td->ndata;
- if(c != nil && (td->csw & Tdtok) == Tdtokin && td->ndata > 0){
- memmove(c, td->data, td->ndata);
- c += td->ndata;
+ if(err == nil && (n = td->ndata) > 0 && tot < count){
+ if((tot + n) > count)
+ n = count - tot;
+ if(c != nil && (td->csw & Tdtok) == Tdtokin){
+ memmove(c, td->data, n);
+ c += n;
+ }
+ tot += n;
}
}
ntd = td->next;
tdfree(td);
}
- err = io->err;
if(mustlock){
qunlock(io);
poperror();
return 0; /* that's our convention */
if(err != nil)
error(err);
- if(tot < 0)
- error(Eio);
return tot;
}
td = itdalloc();
td->data = iso->data + i * 8 * iso->maxsize;
pa = PADDR(td->data) & ~0xFFF;
- for(p = 0; p < 8; p++)
- td->buffer[i] = pa + p * 0x1000;
- td->buffer[0] = PADDR(iso->data) & ~0xFFF |
- ep->nb << Itdepshift | ep->dev->nb << Itddevshift;
+ for(p = 0; p < nelem(td->buffer); p++){
+ td->buffer[p] = pa;
+ pa += 0x1000;
+ }
+ td->buffer[0] |= ep->nb << Itdepshift | ep->dev->nb << Itddevshift;
if(ep->mode == OREAD)
td->buffer[1] |= Itdin;
else
ilock(ctlr);
qh = io->qh;
- if(io == nil || io->qh == nil || io->qh->state == Qclose){
+ if(qh == nil || qh->state == Qclose){
iunlock(ctlr);
return;
}
aborttds(qh);
qh->state = Qclose;
iunlock(ctlr);
- if(!waserror()){
- tsleep(&up->sleep, return0, 0, Abortdelay);
- poperror();
- }
+ while(waserror())
+ ;
+ tsleep(&up->sleep, return0, 0, Abortdelay);
+ poperror();
wakeup(io);
qlock(io);
/* wait for epio if running */
+ if(io->qh == qh)
+ io->qh = nil;
qunlock(io);
qhfree(ctlr, qh);
- io->qh = nil;
}
static void
opio->cmd |= Case;
coherence();
ehcirun(ctlr, 1);
- /*
- * route all ports by default to only one ehci (the first).
- * it's not obvious how multiple ehcis could work and on some
- * machines, setting Callmine on all ehcis makes the machine seize up.
- */
- opio->config = (ctlrno == 0 ? Callmine : 0);
+
+ /* route all ports to us */
+ opio->config = Callmine;
coherence();
for (i = 0; i < hp->nports; i++)