OK, turing.

<- leave blank

Wed Nov 23 22:42:36 EST 2022

Return-Path: <9front-bounces@9front.inri.net>
Delivered-To: ori@eigenstate.org
Received: from 9front.inri.net (9front.inri.net [168.235.81.73])
	by mimir.eigenstate.org (OpenSMTPD) with ESMTP id 0cc1a689
	for <ori@eigenstate.org>;
	Fri, 18 Nov 2022 19:53:36 -0800 (PST)
MIME-Version: 1.0
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: 8bit
Received: from mail-pf1-f171.google.com ([209.85.210.171]) by 9front; Fri Nov 18
22:39:10 -0500 2022
Received: by mail-pf1-f171.google.com with SMTP id c203so6613216pfc.11
	for <9front@9front.org>; Fri, 18 Nov 2022 19:39:06 -0800 (PST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
	d=mforney.org; s=google;
	h=to:subject:date:from:references:in-reply-to:message-id:from:to:cc
	 :subject:date:message-id:reply-to;
	bh=OUZZS/cZMo/PPnx2unxTqvQ8ZOexwhUnduIkOuXqL6o=;
	b=mVMeSu1I2HsFndV52iKuCGOqbM5TPV1CofqvNQB0f/mTVMQAcKYd9AHlONWxyO94tG
	 8/2iP94qhY35jgMxcm19f2cJxig0S/5DDZEXuExrBlXnHypmdToX8TQoRlYCgSW/hC8n
	 8YDzwSu523oWVjZS+QmdS4I1RJfxkThXUR4WAXBKraaOz9KP+pQPCA2V+u1WvUsizt+T
	 FK2GZupcUxBJk7p3r/nrk9Src5tAOUY2uiWmD+m4aMgBYG4jNN5c4R5f2BY3Pas/LR/r
	 /r56p0wAjte3fRGtE34MKd7/0dTVxv55i/ZLkDe8ErcPR2tiSLS0Jkvy6YbWSN3XWmzU
	 IMxA==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
	d=1e100.net; s=20210112;
	h=to:subject:date:from:references:in-reply-to:message-id
	 :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;
	bh=OUZZS/cZMo/PPnx2unxTqvQ8ZOexwhUnduIkOuXqL6o=;
	b=rLYB3/KCuGQMpjnM268iNxIm2UL1Ln5f9Tm6L0qVLfSzQiJE8cn8/znAqvEbkJcfgL
	 bgjCFVA+LvoH37XpIrlhJ7mAbDVVSyAPubAaGwfKjzGfGjggwYr16kZhNSVf5LMQHEk5
	 6GcIWkJH7IVBYBaqHZCfY/QMwvAD31t7gZlKh2iPsDjw/Vw3e3yH2q86ouP9cjIXDpTi
	 usUOlOruN3u1o0VBpMKKomDq+0nIg11D6aUjnug53okfhmZLZDd6W6rZa+nk3kIR7E6r
	 3Zgsl5BM2sLF3z6UxoOFAqYYg9fb4KxfpF+wLaDDzYUDqmyiyzcZ0Qhru8nUm73zwzC0
	 D44g==
X-Gm-Message-State: ANoB5pm+FzNv5I3e8mUegxxrK+dvkrCAuVidPTSg8qm1ZbRxTZ24TzXk
	90g3TgCt0vMt1EMFdG6vLQ1oR6BFEaVOLYzsbeI=
X-Google-Smtp-Source:
AA0mqf439KtbSaQd3eWB4c1VeyF+iTt58NK4B2hvdkrZb+YCDGdm0Frq9ROzBbakWifg1BNw5j6KoQ==
X-Received: by 2002:aa7:9e5d:0:b0:54c:9e75:ab7 with SMTP id
z29-20020aa79e5d000000b0054c9e750ab7mr10726998pfq.82.1668829145982;
	Fri, 18 Nov 2022 19:39:05 -0800 (PST)
Return-Path: <mforney@mforney.org>
Received: from localhost ([2601:647:6400:20b0:cab2:9bff:fe88:d09c])
	by smtp.gmail.com with ESMTPSA id
	mg24-20020a17090b371800b002071ee97923sm6139805pjb.53.2022.11.18.19.39.04
	for <9front@9front.org>
	(version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256);
	Fri, 18 Nov 2022 19:39:05 -0800 (PST)
Message-Id:
<50c1a92978c503051edfe64339441a0b53dd3fb4.1668828454.git.mforney@mforney.org>
In-Reply-To: <cover.1668828454.git.mforney@mforney.org>
References: <cover.1668828454.git.mforney@mforney.org>
From: Michael Forney <mforney@mforney.org>
Date: Fri, 18 Nov 2022 20:35:33 +0000
To: 9front@9front.org
List-ID: <9front.9front.org>
List-Help: <http://lists.9front.org>
X-Glyph: ➈
X-Bullshit: property lifecycle realtime just-in-time frontend
Subject: [9front] [PATCH 5/5] nusb/audio: add support for USB audio 2.0
Reply-To: 9front@9front.org
Precedence: bulk

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