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 */
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 */
ulong td0frno; /* first frame used in ctlr */
qhlinkqh(Qh *qh, Qh *next)
{
qh->next = next;
- qh->link = PADDR(next)|Lqh;
+ if(next == nil)
+ qh->link = Lterm;
+ else
+ qh->link = PADDR(next)|Lqh;
coherence();
return qh;
}
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);
}
return 0;
}
+static int
+isodelay(void *a)
+{
+ Isoio *iso;
+ int delay;
+
+ iso = a;
+ if(iso->state == Qclose || iso->err || iso->delay == 0)
+ return 1;
+
+ delay = 0;
+ if(iso->hs){
+ Itd *i;
+
+ for(i = iso->tdi; i->next != iso->tdu; i = i->next){
+ if(!itdactive(i))
+ continue;
+ delay += i->mdata;
+ if(delay > iso->delay)
+ break;
+ }
+ } else {
+ Sitd *i;
+
+ for(i = iso->stdi; i->next != iso->stdu; i = i->next){
+ if((i->csw & Stdactive) == 0)
+ continue;
+ delay += i->mdata;
+ if(delay > iso->delay)
+ break;
+ }
+ }
+
+ return delay <= iso->delay;
+}
+
+
static int
isohsinterrupt(Ctlr *ctlr, Isoio *iso)
{
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;
}
ctlr = hp->aux;
opio = ctlr->opio;
s = opio->portsc[port-1];
- qlock(&ctlr->portlck);
+ eqlock(&ctlr->portlck);
if(waserror()){
qunlock(&ctlr->portlck);
nexterror();
static int
portreset(Hci *hp, int port, int on)
{
- ulong s;
+ ulong *portscp;
Eopio *opio;
Ctlr *ctlr;
int i;
ctlr = hp->aux;
opio = ctlr->opio;
- qlock(&ctlr->portlck);
+ eqlock(&ctlr->portlck);
if(waserror()){
iunlock(ctlr);
qunlock(&ctlr->portlck);
nexterror();
}
- s = opio->portsc[port-1];
- dprint("ehci %#p port %d reset; sts %#lux\n", ctlr->capio, port, s);
+ portscp = &opio->portsc[port-1];
+ dprint("ehci %#p port %d reset; sts %#lux\n", ctlr->capio, port, *portscp);
ilock(ctlr);
- s &= ~(Psenable|Psreset);
- opio->portsc[port-1] = s | Psreset; /* initiate reset */
- coherence();
+ /* 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);
- for(i = 0; i < 50; i++){ /* was 10 */
- delay(10);
- if((opio->portsc[port-1] & Psreset) == 0)
- break;
- }
- if (opio->portsc[port-1] & Psreset)
- iprint("ehci %#p: port %d didn't reset after %d ms; sts %#lux\n",
- ctlr->capio, port, i * 10, opio->portsc[port-1]);
- opio->portsc[port-1] &= ~Psreset; /* force appearance of reset done */
- 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);
- if((opio->portsc[port-1] & Psenable) == 0)
+ for(i = 0; *portscp & Psreset && i < 10; i++)
+ delay(10);
+
+ if (*portscp & Psreset)
+ 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);
dprint("ehci %#p after port %d reset; sts %#lux\n",
- ctlr->capio, port, opio->portsc[port-1]);
+ ctlr->capio, port, *portscp);
qunlock(&ctlr->portlck);
poperror();
return 0;
ctlr = hp->aux;
opio = ctlr->opio;
- qlock(&ctlr->portlck);
+ eqlock(&ctlr->portlck);
if(waserror()){
iunlock(ctlr);
qunlock(&ctlr->portlck);
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);
b = a;
ctlr = ep->hp->aux;
- qlock(iso);
+ eqlock(iso);
if(waserror()){
qunlock(iso);
nexterror();
* it is activated and tdu advanced.
*/
static long
-putsamples(Isoio *iso, uchar *b, long count)
+putsamples(Ctlr *ctlr, Isoio *iso, uchar *b, long count)
{
- long tot, n;
+ 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){
- if(n > iso->tdu->mdata - iso->nleft)
- n = iso->tdu->mdata - iso->nleft;
- memmove(iso->tdu->data + iso->nleft, b + tot, n);
- coherence();
+ 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 == iso->tdu->mdata){
- itdinit(iso, iso->tdu);
+ if(iso->nleft == tdu->mdata){
+ itdinit(iso, tdu);
+ iso->tdu = tdu->next;
iso->nleft = 0;
- iso->tdu = iso->tdu->next;
}
}else{
- if(n > iso->stdu->mdata - iso->nleft)
- n = iso->stdu->mdata - iso->nleft;
- memmove(iso->stdu->data + iso->nleft, b + tot, n);
- coherence();
+ 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 == iso->stdu->mdata){
- sitdinit(iso, iso->stdu);
+ if(iso->nleft == stdu->mdata){
+ sitdinit(iso, stdu);
+ iso->stdu = stdu->next;
iso->nleft = 0;
- iso->stdu = iso->stdu->next;
}
}
}
int tot, nw;
char *err;
+ iso->delay = ep->sampledelay * ep->samplesz;
iso->debug = ep->debug;
diprint("ehci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
ctlr = ep->hp->aux;
- qlock(iso);
+ eqlock(iso);
if(waserror()){
qunlock(iso);
nexterror();
}
if(iso->state != Qrun)
panic("episowrite: iso not running");
- iunlock(ctlr); /* We could page fault here */
- nw = putsamples(iso, b+tot, count-tot);
+ nw = putsamples(ctlr, iso, b+tot, count-tot);
+ }
+ while(isodelay(iso) == 0){
+ iunlock(ctlr);
+ sleep(iso, isodelay, iso);
ilock(ctlr);
}
if(iso->state != Qclose)
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;
print("echi epio: user data: %s\n", buf);
}
if(mustlock){
- qlock(io);
+ eqlock(io);
if(waserror()){
qunlock(io);
nexterror();
}
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;
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.
+ * 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)){
- if(saved++ == 0) {
+ if(td->csw & (Tdhalt|Tdactive))
+ saved++;
+ else{
+ if(!saved){
io->toggle = td->csw & Tddata1;
coherence();
}
- }else{
- 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;
}
switch(ep->ttype){
case Tctl:
cio = ep->aux;
- qlock(cio);
+ eqlock(cio);
if(waserror()){
qunlock(cio);
nexterror();
cio, ep->dev->nb, ep->nb, count);
if(count < Rsetuplen)
error("short usb comand");
- qlock(cio);
+ eqlock(cio);
free(cio->data);
cio->data = nil;
cio->ndata = 0;
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
{
Ctlr *ctlr;
Eopio *opio;
+ static int ctlrno;
int i;
hp->highspeed = 1;
opio->cmd |= Case;
coherence();
ehcirun(ctlr, 1);
- opio->config = Callmine; /* reclaim all ports */
+
+ /* route all ports to us */
+ opio->config = Callmine;
coherence();
for (i = 0; i < hp->nports; i++)
iunlock(ctlr);
if(ehcidebug > 1)
dump(hp);
+ ctlrno++;
}
void