usb: added buffer delay control
This commit is contained in:
parent
89ab1f286e
commit
676a876df6
6 changed files with 114 additions and 11 deletions
|
@ -88,6 +88,7 @@ enum
|
||||||
CMdebugep, /* debug n (set/clear debug for this ep) */
|
CMdebugep, /* debug n (set/clear debug for this ep) */
|
||||||
CMname, /* name str (show up as #u/name as well) */
|
CMname, /* name str (show up as #u/name as well) */
|
||||||
CMtmout, /* timeout n (activate timeouts for ep) */
|
CMtmout, /* timeout n (activate timeouts for ep) */
|
||||||
|
CMsampledelay, /* maximum delay introduced by buffering (iso) */
|
||||||
CMpreset, /* reset the port */
|
CMpreset, /* reset the port */
|
||||||
|
|
||||||
/* Hub feature selectors */
|
/* Hub feature selectors */
|
||||||
|
@ -132,6 +133,7 @@ static Cmdtab epctls[] =
|
||||||
{CMclrhalt, "clrhalt", 1},
|
{CMclrhalt, "clrhalt", 1},
|
||||||
{CMname, "name", 2},
|
{CMname, "name", 2},
|
||||||
{CMtmout, "timeout", 2},
|
{CMtmout, "timeout", 2},
|
||||||
|
{CMsampledelay, "sampledelay", 2},
|
||||||
{CMpreset, "reset", 1},
|
{CMpreset, "reset", 1},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1309,6 +1311,11 @@ epctl(Ep *ep, Chan *c, void *a, long n)
|
||||||
if(ep->tmout != 0 && ep->tmout < Xfertmout)
|
if(ep->tmout != 0 && ep->tmout < Xfertmout)
|
||||||
ep->tmout = Xfertmout;
|
ep->tmout = Xfertmout;
|
||||||
break;
|
break;
|
||||||
|
case CMsampledelay:
|
||||||
|
if(ep->ttype != Tiso)
|
||||||
|
error("ctl ignored for this endpoint type");
|
||||||
|
ep->sampledelay = strtoul(cb->f[1], nil, 0);
|
||||||
|
break;
|
||||||
case CMpreset:
|
case CMpreset:
|
||||||
deprint("usb epctl %s\n", cb->f[0]);
|
deprint("usb epctl %s\n", cb->f[0]);
|
||||||
if(ep->ttype != Tctl)
|
if(ep->ttype != Tctl)
|
||||||
|
|
|
@ -220,6 +220,7 @@ struct Isoio
|
||||||
ulong frno; /* next frame number avail for I/O */
|
ulong frno; /* next frame number avail for I/O */
|
||||||
ulong left; /* remainder after rounding Hz to samples/ms */
|
ulong left; /* remainder after rounding Hz to samples/ms */
|
||||||
int nerrs; /* consecutive errors on iso I/O */
|
int nerrs; /* consecutive errors on iso I/O */
|
||||||
|
int delay; /* maximum number of frames to buffer */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1101,6 +1102,17 @@ isocanwrite(void *a)
|
||||||
iso->navail > iso->nframes / 2;
|
iso->navail > iso->nframes / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
isodelay(void *a)
|
||||||
|
{
|
||||||
|
Isoio *iso;
|
||||||
|
|
||||||
|
iso = a;
|
||||||
|
if(iso->state == Qclose || iso->err != nil || iso->delay == 0)
|
||||||
|
return 1;
|
||||||
|
return (iso->nframes - iso->navail) <= iso->delay;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Service a completed/failed Td from the done queue.
|
* Service a completed/failed Td from the done queue.
|
||||||
* It may be of any transfer type.
|
* It may be of any transfer type.
|
||||||
|
@ -1780,6 +1792,7 @@ episowrite(Ep *ep, void *a, long count)
|
||||||
|
|
||||||
ctlr = ep->hp->aux;
|
ctlr = ep->hp->aux;
|
||||||
iso = ep->aux;
|
iso = ep->aux;
|
||||||
|
iso->delay = (ep->sampledelay*ep->samplesz + ep->maxpkt-1) / ep->maxpkt;
|
||||||
iso->debug = ep->debug;
|
iso->debug = ep->debug;
|
||||||
|
|
||||||
qlock(iso);
|
qlock(iso);
|
||||||
|
@ -1821,6 +1834,11 @@ episowrite(Ep *ep, void *a, long count)
|
||||||
nw = putsamples(ctlr, ep, iso, b+tot, count-tot);
|
nw = putsamples(ctlr, ep, iso, b+tot, count-tot);
|
||||||
ilock(ctlr);
|
ilock(ctlr);
|
||||||
}
|
}
|
||||||
|
while(isodelay(iso) == 0){
|
||||||
|
iunlock(ctlr);
|
||||||
|
sleep(iso, isodelay, iso);
|
||||||
|
ilock(ctlr);
|
||||||
|
}
|
||||||
if(iso->state != Qclose)
|
if(iso->state != Qclose)
|
||||||
iso->state = Qdone;
|
iso->state = Qdone;
|
||||||
iunlock(ctlr);
|
iunlock(ctlr);
|
||||||
|
|
|
@ -195,6 +195,7 @@ struct Isoio
|
||||||
int debug; /* debug flag from the endpoint */
|
int debug; /* debug flag from the endpoint */
|
||||||
Isoio* next; /* in list of active Isoios */
|
Isoio* next; /* in list of active Isoios */
|
||||||
Td* tdps[Nframes]; /* pointer to Td used for i-th frame or nil */
|
Td* tdps[Nframes]; /* pointer to Td used for i-th frame or nil */
|
||||||
|
int delay; /* maximum number of bytes to buffer */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Tdpool
|
struct Tdpool
|
||||||
|
@ -775,6 +776,29 @@ isocanwrite(void *a)
|
||||||
iso->tok == Tdtokout && iso->tdu->next != iso->tdi);
|
iso->tok == Tdtokout && iso->tdu->next != iso->tdi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
isodelay(void *a)
|
||||||
|
{
|
||||||
|
Isoio *iso;
|
||||||
|
int delay;
|
||||||
|
Td *tdi;
|
||||||
|
|
||||||
|
iso = a;
|
||||||
|
if(iso->state == Qclose || iso->err || iso->delay == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
delay = 0;
|
||||||
|
for(tdi = iso->tdi; tdi->next != iso->tdu; tdi = tdi->next){
|
||||||
|
if((tdi->csw & Tdactive) == 0)
|
||||||
|
continue;
|
||||||
|
delay += maxtdlen(tdi);
|
||||||
|
if(delay > iso->delay)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return delay <= iso->delay;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tdisoinit(Isoio *iso, Td *td, long count)
|
tdisoinit(Isoio *iso, Td *td, long count)
|
||||||
{
|
{
|
||||||
|
@ -812,7 +836,6 @@ isointerrupt(Ctlr *ctlr, Isoio* iso)
|
||||||
nframes = iso->nframes / 2; /* limit how many we look */
|
nframes = iso->nframes / 2; /* limit how many we look */
|
||||||
if(nframes > 64)
|
if(nframes > 64)
|
||||||
nframes = 64;
|
nframes = 64;
|
||||||
|
|
||||||
for(i = 0; i < nframes && (tdi->csw & Tdactive) == 0; i++){
|
for(i = 0; i < nframes && (tdi->csw & Tdactive) == 0; i++){
|
||||||
tdi->csw &= ~Tdioc;
|
tdi->csw &= ~Tdioc;
|
||||||
err = tdi->csw & Tderrors;
|
err = tdi->csw & Tderrors;
|
||||||
|
@ -839,7 +862,7 @@ isointerrupt(Ctlr *ctlr, Isoio* iso)
|
||||||
}
|
}
|
||||||
tdi = tdi->next;
|
tdi = tdi->next;
|
||||||
}
|
}
|
||||||
ddiprint("isointr: %d frames processed\n", nframes);
|
ddiprint("isointr: %d frames processed\n", i);
|
||||||
if(i == nframes)
|
if(i == nframes)
|
||||||
tdi->csw |= Tdioc;
|
tdi->csw |= Tdioc;
|
||||||
iso->tdi = tdi;
|
iso->tdi = tdi;
|
||||||
|
@ -848,7 +871,6 @@ isointerrupt(Ctlr *ctlr, Isoio* iso)
|
||||||
iso->tdi, iso->tdu);
|
iso->tdi, iso->tdu);
|
||||||
wakeup(iso);
|
wakeup(iso);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1005,6 +1027,7 @@ episowrite(Ep *ep, Isoio *iso, void *a, long count)
|
||||||
char *err;
|
char *err;
|
||||||
|
|
||||||
iso->debug = ep->debug;
|
iso->debug = ep->debug;
|
||||||
|
iso->delay = ep->sampledelay * ep->samplesz;
|
||||||
diprint("uhci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
|
diprint("uhci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
|
||||||
|
|
||||||
ctlr = ep->hp->aux;
|
ctlr = ep->hp->aux;
|
||||||
|
@ -1046,6 +1069,11 @@ episowrite(Ep *ep, Isoio *iso, void *a, long count)
|
||||||
nw = putsamples(iso, b+tot, count-tot);
|
nw = putsamples(iso, b+tot, count-tot);
|
||||||
ilock(ctlr);
|
ilock(ctlr);
|
||||||
}
|
}
|
||||||
|
while(isodelay(iso) == 0){
|
||||||
|
iunlock(ctlr);
|
||||||
|
sleep(iso, isodelay, iso);
|
||||||
|
ilock(ctlr);
|
||||||
|
}
|
||||||
if(iso->state != Qclose)
|
if(iso->state != Qclose)
|
||||||
iso->state = Qdone;
|
iso->state = Qdone;
|
||||||
iunlock(ctlr);
|
iunlock(ctlr);
|
||||||
|
@ -1678,7 +1706,7 @@ isoopen(Ep *ep)
|
||||||
}
|
}
|
||||||
ltd->next = iso->tdps[iso->td0frno];
|
ltd->next = iso->tdps[iso->td0frno];
|
||||||
iso->tdi = iso->tdps[iso->td0frno];
|
iso->tdi = iso->tdps[iso->td0frno];
|
||||||
iso->tdu = iso->tdi; /* read: right now; write: 1s ahead */
|
iso->tdu = iso->tdi;
|
||||||
ilock(ctlr);
|
ilock(ctlr);
|
||||||
frno = iso->td0frno;
|
frno = iso->td0frno;
|
||||||
for(i = 0; i < iso->nframes; i++){
|
for(i = 0; i < iso->nframes; i++){
|
||||||
|
|
|
@ -170,6 +170,7 @@ struct Ep
|
||||||
long samplesz; /* sample size (iso) */
|
long samplesz; /* sample size (iso) */
|
||||||
int ntds; /* nb. of Tds per µframe */
|
int ntds; /* nb. of Tds per µframe */
|
||||||
int tmout; /* 0 or timeout for transfers (ms) */
|
int tmout; /* 0 or timeout for transfers (ms) */
|
||||||
|
int sampledelay; /* maximum delay introduced by buffering (iso) */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -215,6 +215,7 @@ struct Isoio
|
||||||
ulong maxsize; /* ntds * ep->maxpkt */
|
ulong maxsize; /* ntds * ep->maxpkt */
|
||||||
long nleft; /* number of bytes left from last write */
|
long nleft; /* number of bytes left from last write */
|
||||||
int debug; /* debug flag from the endpoint */
|
int debug; /* debug flag from the endpoint */
|
||||||
|
int delay; /* max number of bytes to buffer */
|
||||||
int hs; /* is high speed? */
|
int hs; /* is high speed? */
|
||||||
Isoio* next; /* in list of active Isoios */
|
Isoio* next; /* in list of active Isoios */
|
||||||
ulong td0frno; /* first frame used in ctlr */
|
ulong td0frno; /* first frame used in ctlr */
|
||||||
|
@ -1290,6 +1291,43 @@ itdactive(Itd *td)
|
||||||
return 0;
|
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
|
static int
|
||||||
isohsinterrupt(Ctlr *ctlr, Isoio *iso)
|
isohsinterrupt(Ctlr *ctlr, Isoio *iso)
|
||||||
{
|
{
|
||||||
|
@ -1996,6 +2034,7 @@ episowrite(Ep *ep, Isoio *iso, void *a, long count)
|
||||||
int tot, nw;
|
int tot, nw;
|
||||||
char *err;
|
char *err;
|
||||||
|
|
||||||
|
iso->delay = ep->sampledelay * ep->samplesz;
|
||||||
iso->debug = ep->debug;
|
iso->debug = ep->debug;
|
||||||
diprint("ehci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
|
diprint("ehci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
|
||||||
|
|
||||||
|
@ -2039,6 +2078,11 @@ episowrite(Ep *ep, Isoio *iso, void *a, long count)
|
||||||
nw = putsamples(iso, b+tot, count-tot);
|
nw = putsamples(iso, b+tot, count-tot);
|
||||||
ilock(ctlr);
|
ilock(ctlr);
|
||||||
}
|
}
|
||||||
|
while(isodelay(iso) == 0){
|
||||||
|
iunlock(ctlr);
|
||||||
|
sleep(iso, isodelay, iso);
|
||||||
|
ilock(ctlr);
|
||||||
|
}
|
||||||
if(iso->state != Qclose)
|
if(iso->state != Qclose)
|
||||||
iso->state = Qdone;
|
iso->state = Qdone;
|
||||||
iunlock(ctlr);
|
iunlock(ctlr);
|
||||||
|
|
|
@ -14,6 +14,7 @@ struct Audio
|
||||||
int maxfreq;
|
int maxfreq;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int audiodelay = 882;
|
||||||
int audiofreq = 44100;
|
int audiofreq = 44100;
|
||||||
int audiochan = 2;
|
int audiochan = 2;
|
||||||
int audiores = 16;
|
int audiores = 16;
|
||||||
|
@ -85,15 +86,17 @@ Foundaltc:
|
||||||
b[1] = speed >> 8;
|
b[1] = speed >> 8;
|
||||||
b[2] = speed >> 16;
|
b[2] = speed >> 16;
|
||||||
if(usbcmd(d, Rh2d|Rclass|Rep, Rsetcur, 0x100, e->addr, b, 3) < 0)
|
if(usbcmd(d, Rh2d|Rclass|Rep, Rsetcur, 0x100, e->addr, b, 3) < 0)
|
||||||
fprint(2, "warning: set freq: %r");
|
fprint(2, "warning: set freq: %r\n");
|
||||||
|
|
||||||
if((d = openep(d, e->id)) == nil){
|
if((d = openep(d, e->id)) == nil){
|
||||||
werrstr("openep: %r");
|
werrstr("openep: %r");
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
devctl(d, "pollival %d", 1);
|
devctl(d, "pollival %d", a->interval);
|
||||||
devctl(d, "samplesz %d", audiochan*audiores/8);
|
devctl(d, "samplesz %d", audiochan*audiores/8);
|
||||||
|
devctl(d, "sampledelay %d", audiodelay);
|
||||||
devctl(d, "hz %d", speed);
|
devctl(d, "hz %d", speed);
|
||||||
|
devctl(d, "name audio");
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +105,7 @@ fsread(Req *r)
|
||||||
{
|
{
|
||||||
char *msg;
|
char *msg;
|
||||||
|
|
||||||
msg = smprint("master 100 100\nspeed %d\n", audiofreq);
|
msg = smprint("master 100 100\nspeed %d\ndelay %d\n", audiofreq, audiodelay);
|
||||||
readstr(r, msg);
|
readstr(r, msg);
|
||||||
respond(r, nil);
|
respond(r, nil);
|
||||||
free(msg);
|
free(msg);
|
||||||
|
@ -112,7 +115,7 @@ void
|
||||||
fswrite(Req *r)
|
fswrite(Req *r)
|
||||||
{
|
{
|
||||||
char msg[256], *f[4];
|
char msg[256], *f[4];
|
||||||
int nf;
|
int nf, speed;
|
||||||
|
|
||||||
snprint(msg, sizeof(msg), "%.*s", r->ifcall.count, r->ifcall.data);
|
snprint(msg, sizeof(msg), "%.*s", r->ifcall.count, r->ifcall.data);
|
||||||
nf = tokenize(msg, f, nelem(f));
|
nf = tokenize(msg, f, nelem(f));
|
||||||
|
@ -122,17 +125,20 @@ fswrite(Req *r)
|
||||||
}
|
}
|
||||||
if(strcmp(f[0], "speed") == 0){
|
if(strcmp(f[0], "speed") == 0){
|
||||||
Dev *d;
|
Dev *d;
|
||||||
int speed;
|
|
||||||
|
|
||||||
speed = atoi(f[1]);
|
speed = atoi(f[1]);
|
||||||
|
Setup:
|
||||||
if((d = setupep(audiodev, audioep, speed)) == nil){
|
if((d = setupep(audiodev, audioep, speed)) == nil){
|
||||||
responderror(r);
|
responderror(r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
devctl(d, "name audio");
|
|
||||||
closedev(d);
|
closedev(d);
|
||||||
|
|
||||||
audiofreq = speed;
|
audiofreq = speed;
|
||||||
|
} else if(strcmp(f[0], "delay") == 0){
|
||||||
|
audiodelay = atoi(f[1]);
|
||||||
|
speed = audiofreq;
|
||||||
|
goto Setup;
|
||||||
}
|
}
|
||||||
r->ofcall.count = r->ifcall.count;
|
r->ofcall.count = r->ifcall.count;
|
||||||
respond(r, nil);
|
respond(r, nil);
|
||||||
|
@ -185,7 +191,6 @@ Foundendp:
|
||||||
audioep = e;
|
audioep = e;
|
||||||
if((d = setupep(audiodev, audioep, audiofreq)) == nil)
|
if((d = setupep(audiodev, audioep, audiofreq)) == nil)
|
||||||
sysfatal("setupep: %r");
|
sysfatal("setupep: %r");
|
||||||
devctl(d, "name audio");
|
|
||||||
closedev(d);
|
closedev(d);
|
||||||
|
|
||||||
fs.tree = alloctree(user, "usb", DMDIR|0555, nil);
|
fs.tree = alloctree(user, "usb", DMDIR|0555, nil);
|
||||||
|
|
Loading…
Reference in a new issue