9front - general discussion about 9front
 help / color / mirror / Atom feed
* [9front] [PATCH 1/5] nusb: move audio-specific requests to nusb/audio
  2022-11-19  3:27 [9front] [PATCH 0/5] USB audio 2.0 Michael Forney
@ 2022-11-18  9:11 ` Michael Forney
  2022-11-18  9:11 ` [9front] [PATCH 2/5] nusb/audio: remove code for bi-directional endpoint Michael Forney
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Michael Forney @ 2022-11-18  9:11 UTC (permalink / raw)
  To: 9front

---
 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


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [9front] [PATCH 2/5] nusb/audio: remove code for bi-directional endpoint
  2022-11-19  3:27 [9front] [PATCH 0/5] USB audio 2.0 Michael Forney
  2022-11-18  9:11 ` [9front] [PATCH 1/5] nusb: move audio-specific requests to nusb/audio Michael Forney
@ 2022-11-18  9:11 ` Michael Forney
  2022-11-18  9:11 ` [9front] [PATCH 3/5] nusb/audio: only consider data endpoints when setting up stream Michael Forney
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Michael Forney @ 2022-11-18  9:11 UTC (permalink / raw)
  To: 9front

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


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [9front] [PATCH 3/5] nusb/audio: only consider data endpoints when setting up stream
  2022-11-19  3:27 [9front] [PATCH 0/5] USB audio 2.0 Michael Forney
  2022-11-18  9:11 ` [9front] [PATCH 1/5] nusb: move audio-specific requests to nusb/audio Michael Forney
  2022-11-18  9:11 ` [9front] [PATCH 2/5] nusb/audio: remove code for bi-directional endpoint Michael Forney
@ 2022-11-18  9:11 ` Michael Forney
  2022-11-18  9:21 ` [9front] [PATCH 4/5] nusb/audio: enumerate streams through control interface Michael Forney
  2022-11-18 20:35 ` [9front] [PATCH 5/5] nusb/audio: add support for USB audio 2.0 Michael Forney
  4 siblings, 0 replies; 6+ messages in thread
From: Michael Forney @ 2022-11-18  9:11 UTC (permalink / raw)
  To: 9front

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


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [9front] [PATCH 4/5] nusb/audio: enumerate streams through control interface
  2022-11-19  3:27 [9front] [PATCH 0/5] USB audio 2.0 Michael Forney
                   ` (2 preceding siblings ...)
  2022-11-18  9:11 ` [9front] [PATCH 3/5] nusb/audio: only consider data endpoints when setting up stream Michael Forney
@ 2022-11-18  9:21 ` Michael Forney
  2022-11-18 20:35 ` [9front] [PATCH 5/5] nusb/audio: add support for USB audio 2.0 Michael Forney
  4 siblings, 0 replies; 6+ messages in thread
From: Michael Forney @ 2022-11-18  9:21 UTC (permalink / raw)
  To: 9front

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


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [9front] [PATCH 5/5] nusb/audio: add support for USB audio 2.0
  2022-11-19  3:27 [9front] [PATCH 0/5] USB audio 2.0 Michael Forney
                   ` (3 preceding siblings ...)
  2022-11-18  9:21 ` [9front] [PATCH 4/5] nusb/audio: enumerate streams through control interface Michael Forney
@ 2022-11-18 20:35 ` Michael Forney
  4 siblings, 0 replies; 6+ messages in thread
From: Michael Forney @ 2022-11-18 20:35 UTC (permalink / raw)
  To: 9front

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


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [9front] [PATCH 0/5] USB audio 2.0
@ 2022-11-19  3:27 Michael Forney
  2022-11-18  9:11 ` [9front] [PATCH 1/5] nusb: move audio-specific requests to nusb/audio Michael Forney
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Michael Forney @ 2022-11-19  3:27 UTC (permalink / raw)
  To: 9front

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


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2022-11-19  3:56 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-19  3:27 [9front] [PATCH 0/5] USB audio 2.0 Michael Forney
2022-11-18  9:11 ` [9front] [PATCH 1/5] nusb: move audio-specific requests to nusb/audio Michael Forney
2022-11-18  9:11 ` [9front] [PATCH 2/5] nusb/audio: remove code for bi-directional endpoint Michael Forney
2022-11-18  9:11 ` [9front] [PATCH 3/5] nusb/audio: only consider data endpoints when setting up stream Michael Forney
2022-11-18  9:21 ` [9front] [PATCH 4/5] nusb/audio: enumerate streams through control interface Michael Forney
2022-11-18 20:35 ` [9front] [PATCH 5/5] nusb/audio: add support for USB audio 2.0 Michael Forney

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).