--- sys/src/cmd/nusb/audio/audio.c | 11 +++++++++++ sys/src/cmd/nusb/lib/usb.h | 9 --------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/sys/src/cmd/nusb/audio/audio.c b/sys/src/cmd/nusb/audio/audio.c index b6944a725d..4018352022 100644 --- a/sys/src/cmd/nusb/audio/audio.c +++ b/sys/src/cmd/nusb/audio/audio.c @@ -5,6 +5,17 @@ #include <9p.h> #include "usb.h" +enum { + Rgetcur = 0x81, + Rgetmin = 0x82, + Rgetmax = 0x83, + Rgetres = 0x84, + Rsetcur = 0x01, + Rsetmin = 0x02, + Rsetmax = 0x03, + Rsetres = 0x04, +}; + typedef struct Range Range; struct Range { diff --git a/sys/src/cmd/nusb/lib/usb.h b/sys/src/cmd/nusb/lib/usb.h index c34df70826..6fbb7c8d50 100644 --- a/sys/src/cmd/nusb/lib/usb.h +++ b/sys/src/cmd/nusb/lib/usb.h @@ -52,15 +52,6 @@ enum { Rsethubdepth = 12, Rgetporterrcnt = 13, - Rgetcur = 0x81, - Rgetmin = 0x82, - Rgetmax = 0x83, - Rgetres = 0x84, - Rsetcur = 0x01, - Rsetmin = 0x02, - Rsetmax = 0x03, - Rsetres = 0x04, - /* dev classes */ Clnone = 0, /* not in usb */ Claudio = 1, -- 2.37.3
Isochronous endpoints are never bi-directional. --- sys/src/cmd/nusb/audio/audio.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/sys/src/cmd/nusb/audio/audio.c b/sys/src/cmd/nusb/audio/audio.c index 4018352022..66e77da713 100644 --- a/sys/src/cmd/nusb/audio/audio.c +++ b/sys/src/cmd/nusb/audio/audio.c @@ -103,7 +103,7 @@ setupep(Dev *d, Ep *e, int speed) Range *f; for(;e != nil; e = e->next){ - if(e->dir!=dir && e->dir!=Eboth) + if(e->dir!=dir) continue; c = e->iface->aux; if(c == nil) @@ -177,7 +177,7 @@ Setup: return; } closedev(d); - if(audioepin != nil && audioepin != audioepout) + if(audioepin != nil) if(d = setupep(audiodev, audioepin, speed)) closedev(d); audiofreq = speed; @@ -243,14 +243,6 @@ main(int argc, char *argv[]) continue; audioepout = e; break; - case Eboth: - if(audioepin != nil && audioepout != nil) - continue; - if(audioepin == nil) - audioepin = e; - if(audioepout == nil) - audioepout = e; - break; } if((ed = setupep(audiodev, e, audiofreq)) == nil){ fprint(2, "setupep: %r\n"); -- 2.37.3
An async iso endpoint has a data endpoint and feedback endpoint, and both get mapped to the same slot in d->usb->ep. However, when setting up audio streams, we are only interested in the data endpoints. If the feedback endpoint appears first in the list, we end up trying to use feedback endpoints for audio data and ignore the data endpoints. --- sys/src/cmd/nusb/audio/audio.c | 48 +++++++++++++++++++--------------- sys/src/cmd/nusb/lib/usb.h | 5 ++++ 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/sys/src/cmd/nusb/audio/audio.c b/sys/src/cmd/nusb/audio/audio.c index 66e77da713..09b84b8c48 100644 --- a/sys/src/cmd/nusb/audio/audio.c +++ b/sys/src/cmd/nusb/audio/audio.c @@ -231,30 +231,36 @@ main(int argc, char *argv[]) parsedescr(d->usb->ddesc[i]); for(i = 0; i < nelem(d->usb->ep); i++){ e = d->usb->ep[i]; - if(e != nil && e->type == Eiso && e->iface->csp == CSP(Claudio, 2, 0)){ - switch(e->dir){ - case Ein: - if(audioepin != nil) - continue; - audioepin = e; - break; - case Eout: - if(audioepout != nil) - continue; - audioepout = e; + if(e == nil || e->type != Eiso || e->iface->csp != CSP(Claudio, 2, 0)) + continue; + for(; e != nil; e = e->next){ + if((e->attrib>>4 & 3) == Edata) break; - } - if((ed = setupep(audiodev, e, audiofreq)) == nil){ - fprint(2, "setupep: %r\n"); - - if(e == audioepin) - audioepin = nil; - if(e == audioepout) - audioepout = nil; + } + if(e == nil) + continue; + switch(e->dir){ + case Ein: + if(audioepin != nil) continue; - } - closedev(ed); + audioepin = e; + break; + case Eout: + if(audioepout != nil) + continue; + audioepout = e; + break; + } + if((ed = setupep(audiodev, e, audiofreq)) == nil){ + fprint(2, "setupep: %r\n"); + + if(e == audioepin) + audioepin = nil; + if(e == audioepout) + audioepout = nil; + continue; } + closedev(ed); } if(audioepout == nil) sysfatal("no endpoints found"); diff --git a/sys/src/cmd/nusb/lib/usb.h b/sys/src/cmd/nusb/lib/usb.h index 6fbb7c8d50..83b6039289 100644 --- a/sys/src/cmd/nusb/lib/usb.h +++ b/sys/src/cmd/nusb/lib/usb.h @@ -100,6 +100,11 @@ enum { Ebulk = 2, Eintr = 3, + /* endpoint isousage */ + Edata = 0, + Efeedback = 1, + Eimplicit = 2, + /* config attrib */ Cbuspowered = 1<<7, Cselfpowered = 1<<6, -- 2.37.3
USB audio 2.0 splits the sample rate control from the AudioStreaming alternate settings to a clock entity connected to input/output terminals associated with the AudioStreaming interfaces. So, in order to pair endpoints with their corresponding clock, we must have a better picture of the audio function topology. The AudioControl interface tells us which streams are available, so we can go through each one, determining the possible stream parameters and locating the associated endpoint. This will give us the necessary context to locate the corresponding terminal (and soon, clock). When we go to setup input/output endpoints, we now only have to consider those that we have selected earlier. --- sys/src/cmd/nusb/audio/audio.c | 193 ++++++++++++++++++++++++--------- 1 file changed, 139 insertions(+), 54 deletions(-) diff --git a/sys/src/cmd/nusb/audio/audio.c b/sys/src/cmd/nusb/audio/audio.c index 09b84b8c48..2cd75f6361 100644 --- a/sys/src/cmd/nusb/audio/audio.c +++ b/sys/src/cmd/nusb/audio/audio.c @@ -19,16 +19,21 @@ enum { typedef struct Range Range; struct Range { - Range *next; - int min; - int max; + uint min; + uint max; }; typedef struct Aconf Aconf; struct Aconf { + Ep *ep; + int bits; + int bps; /* subslot size (bytes per sample) */ + int format; + int channels; + int controls; Range *freq; - int caps; + int nfreq; }; int audiodelay = 1764; /* 40 ms */ @@ -39,62 +44,131 @@ int audiores = 16; char user[] = "audio"; Dev *audiodev = nil; - +Iface *audiocontrol = nil; Ep *audioepin = nil; Ep *audioepout = nil; -void -parsedescr(Desc *dd) +Iface* +findiface(Conf *conf, int class, int subclass, int id) { - Aconf *c; - Range *f; - uchar *b; int i; + Iface *iface; - if(dd == nil || dd->iface == nil) - return; - if(Subclass(dd->iface->csp) != 2) - return; + for(i = 0; i < nelem(conf->iface); i++){ + iface = conf->iface[i]; + if(iface == nil || Class(iface->csp) != class || Subclass(iface->csp) != subclass) + continue; + if(id == -1 || iface->id == id) + return iface; + } + return nil; +} + +Desc* +findacheader(Usbdev *u, Iface *ac) +{ + Desc *dd; + uchar *b; + int i; - c = dd->iface->aux; - if(c == nil){ - c = mallocz(sizeof(*c), 1); - dd->iface->aux = c; + for(i = 0; i < nelem(u->ddesc); i++){ + dd = u->ddesc[i]; + if(dd == nil || dd->iface != ac || dd->data.bDescriptorType != 0x24) + continue; + if(dd->data.bLength < 8 || dd->data.bbytes[0] != 1) + continue; + b = dd->data.bbytes; + if(dd->data.bLength == 8+b[5]) + return dd; } + return nil; +} + +void +parseasdesc(Desc *dd, Aconf *c) +{ + uchar *b; + Range *f; - b = (uchar*)&dd->data; - switch(b[1]<<8 | b[2]){ + b = dd->data.bbytes; + switch(dd->data.bDescriptorType<<8 | b[0]){ case 0x2501: /* CS_ENDPOINT, EP_GENERAL */ - c->caps |= b[3]; + if(dd->data.bLength != 7) + return; + c->controls = b[1]; break; - case 0x2402: /* CS_INTERFACE, FORMAT_TYPE */ - if(b[4] != audiochan) - break; - if(b[6] != audiores) - break; + case 0x2401: /* CS_INTERFACE, AS_GENERAL */ + if(dd->data.bLength != 7) + return; + c->format = GET2(&b[3]); + break; - if(b[7] == 0){ - f = mallocz(sizeof(*f), 1); - f->min = b[8] | b[9]<<8 | b[10]<<16; - f->max = b[11] | b[12]<<8 | b[13]<<16; - - f->next = c->freq; - c->freq = f; - } else { - for(i=0; i<b[7]; i++){ - f = mallocz(sizeof(*f), 1); - f->min = b[8+3*i] | b[9+3*i]<<8 | b[10+3*i]<<16; + case 0x2402: /* CS_INTERFACE, FORMAT_TYPE */ + if(dd->data.bLength < 8 || b[1] != 1) + return; + c->channels = b[2]; + c->bps = b[3]; + c->bits = b[4]; + if(b[5] == 0){ /* continuous frequency range */ + c->nfreq = 1; + c->freq = emallocz(sizeof(*f), 1); + c->freq->min = b[6] | b[7]<<8 | b[8]<<16; + c->freq->max = b[9] | b[10]<<8 | b[11]<<16; + }else{ /* discrete sampling frequencies */ + c->nfreq = b[5]; + c->freq = emallocz(c->nfreq * sizeof(*f), 1); + b += 6; + for(f = c->freq; f < c->freq+c->nfreq; f++, b += 3){ + f->min = b[0] | b[1]<<8 | b[2]<<16; f->max = f->min; - - f->next = c->freq; - c->freq = f; } } break; } } +void +parsestream(Dev *d, int id) +{ + Iface *as; + Desc *dd; + Ep *e; + Aconf *c; + int i; + + /* find AS interface */ + as = findiface(d->usb->conf[0], Claudio, 2, id); + + /* enumerate through alt. settings */ + for(; as != nil; as = as->next){ + c = emallocz(sizeof(*c), 1); + as->aux = c; + + /* find AS endpoint */ + for(i = 0; i < nelem(as->ep); i++){ + e = as->ep[i]; + if(e != nil && e->type == Eiso && (e->attrib>>4 & 3) == Edata){ + c->ep = e; + break; + } + } + if(c->ep == nil){ + free(c); + as->aux = nil; + continue; + } + + /* parse AS descriptors */ + for(i = 0; i < nelem(d->usb->ddesc); i++){ + dd = d->usb->ddesc[i]; + if(dd == nil || dd->iface != as) + continue; + parseasdesc(dd, c); + } + } +} + Dev* setupep(Dev *d, Ep *e, int speed) { @@ -103,12 +177,12 @@ setupep(Dev *d, Ep *e, int speed) Range *f; for(;e != nil; e = e->next){ - if(e->dir!=dir) - continue; c = e->iface->aux; - if(c == nil) + if(c == nil || e != c->ep || e->dir != dir) continue; - for(f = c->freq; f != nil; f = f->next) + if(c->format != 1 || c->bits != audiores || 8*c->bps != audiores || c->channels != audiochan) + continue; + for(f = c->freq; f != c->freq+c->nfreq; f++) if(speed >= f->min && speed <= f->max) goto Foundaltc; } @@ -119,7 +193,7 @@ Foundaltc: if(setalt(d, e->iface) < 0) return nil; - if(c->caps & 1){ + if(c->controls & 1){ uchar b[4]; b[0] = speed; @@ -207,6 +281,10 @@ main(int argc, char *argv[]) { char buf[32]; Dev *d, *ed; + Desc *dd; + Conf *conf; + Iface *ac; + Aconf *c; Ep *e; int i; @@ -226,15 +304,22 @@ main(int argc, char *argv[]) sysfatal("getdev: %r"); audiodev = d; - /* parse descriptors, mark valid altc */ - for(i = 0; i < nelem(d->usb->ddesc); i++) - parsedescr(d->usb->ddesc[i]); + conf = d->usb->conf[0]; + ac = findiface(conf, Claudio, 1, -1); + if(ac == nil) + sysfatal("no audio control interface"); + audiocontrol = ac; + + dd = findacheader(d->usb, ac); + if(dd == nil) + sysfatal("no audio control header"); + for(i = 6; i < dd->data.bLength-2; i++) + parsestream(d, dd->data.bbytes[i]); + for(i = 0; i < nelem(d->usb->ep); i++){ - e = d->usb->ep[i]; - if(e == nil || e->type != Eiso || e->iface->csp != CSP(Claudio, 2, 0)) - continue; - for(; e != nil; e = e->next){ - if((e->attrib>>4 & 3) == Edata) + for(e = d->usb->ep[i]; e != nil; e = e->next){ + c = e->iface->aux; + if(c != nil && c->ep == e) break; } if(e == nil) @@ -263,7 +348,7 @@ main(int argc, char *argv[]) closedev(ed); } if(audioepout == nil) - sysfatal("no endpoints found"); + sysfatal("no output stream found"); fs.tree = alloctree(user, "usb", DMDIR|0555, nil); createfile(fs.tree->root, "volume", user, 0666, nil); -- 2.37.3
USB audio 2.0 mandates the use of an interface association descriptor to group together the interfaces that comprise an audio function, and the AudioControl interface no longer lists the streams. So for 2.0 devices, find and use the IAD to enumerate the streams. Additionally, we now need to locate the terminal associated with the stream, and the clock for that terminal. This replaces the frequency fields in the type I format type descriptor, and the sampling rate is now set with a request directed at the clock entity in the control interface, rather than the endpoint. --- sys/src/cmd/nusb/audio/audio.c | 258 +++++++++++++++++++++++++++++---- 1 file changed, 228 insertions(+), 30 deletions(-) diff --git a/sys/src/cmd/nusb/audio/audio.c b/sys/src/cmd/nusb/audio/audio.c index 2cd75f6361..195bd6bd5b 100644 --- a/sys/src/cmd/nusb/audio/audio.c +++ b/sys/src/cmd/nusb/audio/audio.c @@ -6,14 +6,17 @@ #include "usb.h" enum { - Rgetcur = 0x81, - Rgetmin = 0x82, - Rgetmax = 0x83, - Rgetres = 0x84, + Paudio1 = 0x00, + Paudio2 = 0x20, + + Csamfreq = 0x01, + + /* audio 1 */ Rsetcur = 0x01, - Rsetmin = 0x02, - Rsetmax = 0x03, - Rsetres = 0x04, + + /* audio 2 */ + Rcur = 0x01, + Rrange = 0x02, }; typedef struct Range Range; @@ -31,9 +34,15 @@ struct Aconf int bps; /* subslot size (bytes per sample) */ int format; int channels; - int controls; + int terminal; Range *freq; int nfreq; + + /* audio 1 */ + int controls; + + /* audio 2 */ + int clock; }; int audiodelay = 1764; /* 40 ms */ @@ -64,6 +73,24 @@ findiface(Conf *conf, int class, int subclass, int id) return nil; } +Desc* +findiad(Usbdev *u, int id, int csp) +{ + int i; + Desc *dd; + uchar *b; + + for(i = 0; i < nelem(u->ddesc); i++){ + dd = u->ddesc[i]; + if(dd == nil || dd->data.bDescriptorType != 11 || dd->data.bLength != 8) + continue; + b = dd->data.bbytes; + if(b[0] == id && b[0]+b[1] <= Niface && csp == CSP(b[2], b[3], b[4])) + return dd; + } + return nil; +} + Desc* findacheader(Usbdev *u, Iface *ac) { @@ -78,14 +105,70 @@ findacheader(Usbdev *u, Iface *ac) if(dd->data.bLength < 8 || dd->data.bbytes[0] != 1) continue; b = dd->data.bbytes; - if(dd->data.bLength == 8+b[5]) + switch(Proto(ac->csp)){ + case Paudio1: + if(dd->data.bLength == 8+b[5]) + return dd; + break; + case Paudio2: + if(dd->data.bLength == 9) + return dd; + break; + } + } + return nil; +} + +Desc* +findterminal(Usbdev *u, Iface *ac, int id) +{ + Desc *dd; + uchar *b; + int i; + + for(i = 0; i < nelem(u->ddesc); i++){ + dd = u->ddesc[i]; + if(dd == nil || dd->iface != ac) + continue; + if(dd->data.bDescriptorType != 0x24 || dd->data.bLength < 4) + continue; + b = dd->data.bbytes; + if(b[1] != id) + continue; + /* check descriptor length according to type and proto */ + switch(b[0]<<16 | dd->data.bLength<<8 | Proto(ac->csp)){ + case 0x020C00|Paudio1: + case 0x030900|Paudio1: + case 0x021100|Paudio2: + case 0x030c00|Paudio2: + return dd; + } + } + return nil; +} + +Desc* +findclocksource(Usbdev *u, Iface *ac, int id) +{ + Desc *dd; + uchar *b; + int i; + + for(i = 0; i < nelem(u->ddesc); i++){ + dd = u->ddesc[i]; + if(dd == nil || dd->iface != ac) + continue; + if(dd->data.bDescriptorType != 0x24 || dd->data.bLength != 8) + continue; + b = dd->data.bbytes; + if(b[0] == 0x0A && b[1] == id) return dd; } return nil; } void -parseasdesc(Desc *dd, Aconf *c) +parseasdesc1(Desc *dd, Aconf *c) { uchar *b; Range *f; @@ -101,6 +184,7 @@ parseasdesc(Desc *dd, Aconf *c) case 0x2401: /* CS_INTERFACE, AS_GENERAL */ if(dd->data.bLength != 7) return; + c->terminal = b[1]; c->format = GET2(&b[3]); break; @@ -129,12 +213,82 @@ parseasdesc(Desc *dd, Aconf *c) } void -parsestream(Dev *d, int id) +parseasdesc2(Desc *dd, Aconf *c) +{ + uchar *b; + + b = dd->data.bbytes; + switch(dd->data.bDescriptorType<<8 | b[0]){ + case 0x2401: /* CS_INTERFACE, AS_GENERAL */ + if(dd->data.bLength != 16 || b[3] != 1) + return; + c->terminal = b[1]; + c->format = GET4(&b[4]); + c->channels = b[8]; + break; + + case 0x2402: /* CS_INTERFACE, FORMAT_TYPE */ + if(dd->data.bLength != 6 || b[1] != 1) + return; + c->bps = b[2]; + c->bits = b[3]; + break; + } +} + +int +setclock(Dev *d, Iface *ac, Aconf *c, int speed) +{ + uchar b[4]; + int index; + + switch(Proto(ac->csp)){ + case Paudio1: + if((c->controls & 1) == 0) + break; + b[0] = speed; + b[1] = speed >> 8; + b[2] = speed >> 16; + index = c->ep->id & Epmax; + if(c->ep->dir == Ein) + index |= 0x80; + return usbcmd(d, Rh2d|Rclass|Rep, Rsetcur, Csamfreq<<8, index, b, 3); + case Paudio2: + PUT4(b, speed); + index = c->clock<<8 | ac->id; + return usbcmd(d, Rh2d|Rclass|Riface, Rcur, Csamfreq<<8, index, b, 4); + } + return 0; +} + +int +getclockrange(Dev *d, Iface *ac, Aconf *c) +{ + uchar b[2 + 32*12]; + int i, n, rc; + + rc = usbcmd(d, Rd2h|Rclass|Riface, Rrange, Csamfreq<<8, c->clock<<8 | ac->id, b, sizeof(b)); + if(rc < 0) + return -1; + if(rc < 2 || rc < 2 + (n = GET2(b))*12){ + werrstr("invalid response"); + return -1; + } + c->freq = emallocz(n, sizeof(Range)); + c->nfreq = n; + for(i = 0; i < n; i++) + c->freq[i] = (Range){GET4(&b[2 + i*12]), GET4(&b[6 + i*12])}; + return 0; +} + +void +parsestream(Dev *d, Iface *ac, int id) { Iface *as; Desc *dd; Ep *e; Aconf *c; + uchar *b; int i; /* find AS interface */ @@ -154,6 +308,7 @@ parsestream(Dev *d, int id) } } if(c->ep == nil){ + Skip: free(c); as->aux = nil; continue; @@ -164,13 +319,48 @@ parsestream(Dev *d, int id) dd = d->usb->ddesc[i]; if(dd == nil || dd->iface != as) continue; - parseasdesc(dd, c); + switch(Proto(ac->csp)){ + case Paudio1: + parseasdesc1(dd, c); + break; + case Paudio2: + parseasdesc2(dd, c); + break; + } + } + + if(Proto(ac->csp) == Paudio1) + continue; + + dd = findterminal(d->usb, ac, c->terminal); + if(dd == nil) + goto Skip; + b = dd->data.bbytes; + switch(b[0]){ + case 0x02: /* INPUT_TERMINAL */ + c->clock = b[5]; + break; + case 0x03: /* OUTPUT_TERMINAL */ + c->clock = b[6]; + break; + } + + dd = findclocksource(d->usb, ac, c->clock); + if(dd == nil) + goto Skip; + b = dd->data.bbytes; + /* check that clock has rw frequency control */ + if((b[3] & 3) != 3) + goto Skip; + if(getclockrange(d, ac, c) != 0){ + fprint(2, "getclockrange %d: %r", c->clock); + goto Skip; } } } Dev* -setupep(Dev *d, Ep *e, int speed) +setupep(Dev *d, Iface *ac, Ep *e, int speed) { int dir = e->dir; Aconf *c; @@ -192,15 +382,9 @@ setupep(Dev *d, Ep *e, int speed) Foundaltc: if(setalt(d, e->iface) < 0) return nil; - - if(c->controls & 1){ - uchar b[4]; - - b[0] = speed; - b[1] = speed >> 8; - b[2] = speed >> 16; - if(usbcmd(d, Rh2d|Rclass|Rep, Rsetcur, 0x100, (e->dir==Ein?0x80:0)|(e->id&Epmax), b, 3) < 0) - fprint(2, "warning: set freq: %r\n"); + if(setclock(d, ac, c, speed) < 0){ + werrstr("setclock: %r"); + return nil; } if((d = openep(d, e)) == nil){ @@ -246,13 +430,13 @@ fswrite(Req *r) speed = atoi(f[1]); Setup: - if((d = setupep(audiodev, audioepout, speed)) == nil){ + if((d = setupep(audiodev, audiocontrol, audioepout, speed)) == nil){ responderror(r); return; } closedev(d); if(audioepin != nil) - if(d = setupep(audiodev, audioepin, speed)) + if(d = setupep(audiodev, audiocontrol, audioepin, speed)) closedev(d); audiofreq = speed; } else if(strcmp(f[0], "delay") == 0){ @@ -286,6 +470,7 @@ main(int argc, char *argv[]) Iface *ac; Aconf *c; Ep *e; + uchar *b; int i; ARGBEGIN { @@ -310,11 +495,24 @@ main(int argc, char *argv[]) sysfatal("no audio control interface"); audiocontrol = ac; - dd = findacheader(d->usb, ac); - if(dd == nil) - sysfatal("no audio control header"); - for(i = 6; i < dd->data.bLength-2; i++) - parsestream(d, dd->data.bbytes[i]); + switch(Proto(ac->csp)){ + case Paudio1: + dd = findacheader(d->usb, ac); + if(dd == nil) + sysfatal("no audio control header"); + b = dd->data.bbytes; + for(i = 6; i < dd->data.bLength-2; i++) + parsestream(d, ac, b[i]); + break; + case Paudio2: + dd = findiad(d->usb, ac->id, CSP(Claudio, 0, Paudio2)); + if(dd == nil) + sysfatal("no audio function"); + b = dd->data.bbytes; + for(i = b[0]+1; i < b[0]+b[1]; i++) + parsestream(d, ac, i); + break; + } for(i = 0; i < nelem(d->usb->ep); i++){ for(e = d->usb->ep[i]; e != nil; e = e->next){ @@ -336,7 +534,7 @@ main(int argc, char *argv[]) audioepout = e; break; } - if((ed = setupep(audiodev, e, audiofreq)) == nil){ + if((ed = setupep(d, ac, e, audiofreq)) == nil){ fprint(2, "setupep: %r\n"); if(e == audioepin) -- 2.37.3
Looking for people with USB audio devices to test this series. I have two devices (one 1.0 and one 2.0), and both seem to work, but the more testing the better. Note that since 2.0 devices use an IAD, nusbrc auto-detection doesn't work for them; you'll need to either add your device IDs to nusbrc, or start nusb/audio manually. The patches are also available in the "audio" branch of https://git.sr.ht/~mcf/plan9front if that's more convenient for you. Michael Forney (5): nusb: move audio-specific requests to nusb/audio nusb/audio: remove code for bi-directional endpoint nusb/audio: only consider data endpoints when setting up stream nusb/audio: enumerate streams through control interface nusb/audio: add support for USB audio 2.0 sys/src/cmd/nusb/audio/audio.c | 470 ++++++++++++++++++++++++++------- sys/src/cmd/nusb/lib/usb.h | 14 +- 2 files changed, 386 insertions(+), 98 deletions(-) -- 2.37.3