Module Name:    src
Committed By:   mlelstv
Date:           Sun Apr 16 19:26:20 UTC 2023

Modified Files:
        src/sys/dev/usb: uaudio.c uaudioreg.h

Log Message:
Handle packet scheduling for high/super speed.
More UAC2 handling for input/output/feature/selector units.
Setting sample rate for UAC2 now works, still no support for clock selectors
and multipliers.
Added sysctl to set debug level.
Minor fixes.


To generate a diff of this commit:
cvs rdiff -u -r1.178 -r1.179 src/sys/dev/usb/uaudio.c
cvs rdiff -u -r1.18 -r1.19 src/sys/dev/usb/uaudioreg.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/usb/uaudio.c
diff -u src/sys/dev/usb/uaudio.c:1.178 src/sys/dev/usb/uaudio.c:1.179
--- src/sys/dev/usb/uaudio.c:1.178	Mon Apr 10 15:14:50 2023
+++ src/sys/dev/usb/uaudio.c	Sun Apr 16 19:26:20 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: uaudio.c,v 1.178 2023/04/10 15:14:50 mlelstv Exp $	*/
+/*	$NetBSD: uaudio.c,v 1.179 2023/04/16 19:26:20 mlelstv Exp $	*/
 
 /*
  * Copyright (c) 1999, 2012 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.178 2023/04/10 15:14:50 mlelstv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.179 2023/04/16 19:26:20 mlelstv Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_usb.h"
@@ -59,6 +59,7 @@ __KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1
 #include <sys/bus.h>
 #include <sys/cpu.h>
 #include <sys/atomic.h>
+#include <sys/sysctl.h>
 
 #include <sys/audioio.h>
 #include <dev/audio/audio_if.h>
@@ -99,8 +100,12 @@ int	uaudiodebug = 0;
 #define DPRINTFN(n,x,y...)
 #endif
 
-#define UAUDIO_NCHANBUFS 6	/* number of outstanding request */
-#define UAUDIO_NFRAMES   10	/* ms of sound in each request */
+/* number of outstanding requests */
+#define UAUDIO_NCHANBUFS	6
+/* number of USB frames per request, also the number of ms */
+#define UAUDIO_NFRAMES		10
+/* number of microframes per requewst (high, super)  */
+#define UAUDIO_NFRAMES_HI	(UAUDIO_NFRAMES * USB_UFRAMES_PER_FRAME)
 
 
 #define MIX_MAX_CHAN 8
@@ -133,7 +138,6 @@ struct mixerctl {
 	u_int		nranges;
 	u_int		delta;
 	u_int		mul;
-	u_int		high;
 	uint8_t		class;
 	char		ctlname[MAX_AUDIO_DEV_LEN];
 	const char	*ctlunit;
@@ -147,6 +151,7 @@ struct as_info {
 	uint8_t		attributes; /* Copy of bmAttributes of
 				     * usb_audio_streaming_endpoint_descriptor
 				     */
+	uint8_t		terminal;	/* connected Terminal ID */
 	struct usbd_interface *	ifaceh;
 	const usb_interface_descriptor_t *idesc;
 	const usb_endpoint_descriptor_audio_t *edesc;
@@ -177,12 +182,14 @@ struct chan {
 	int	altidx;		/* currently used altidx */
 
 	int	curchanbuf;
+	u_int	nframes;	/* UAUDIO_NFRAMES or UAUDIO_NFRAMES_HI */
+	u_int	nchanbufs;	/* 1..UAUDIO_NCHANBUFS */
 	struct chanbuf {
 		struct chan	*chan;
 		struct usbd_xfer *xfer;
 		u_char		*buffer;
-		uint16_t	sizes[UAUDIO_NFRAMES];
-		uint16_t	offsets[UAUDIO_NFRAMES];
+		uint16_t	sizes[UAUDIO_NFRAMES_HI];
+		uint16_t	offsets[UAUDIO_NFRAMES_HI];
 		uint16_t	size;
 	} chanbufs[UAUDIO_NCHANBUFS];
 
@@ -223,9 +230,13 @@ struct uaudio_softc {
 	int		sc_nratectls;	/* V2 sample rates */
 	int		sc_ratectls[AUFMT_MAX_FREQUENCIES];
 	int		sc_ratemode[AUFMT_MAX_FREQUENCIES];
+	int		sc_playclock;
+	int		sc_recclock;
 	struct audio_format *sc_formats;
 	int		sc_nformats;
+	uint8_t		sc_clock[256];	/* map terminals to clocks */
 	u_int		sc_channel_config;
+	u_int		sc_usb_frames_per_second;
 	char		sc_dying;
 	struct audio_device sc_adev;
 };
@@ -244,7 +255,7 @@ struct io_terminal {
 		const union usb_audio_output_terminal *ot;
 		const struct usb_audio_mixer_unit *mu;
 		const struct usb_audio_selector_unit *su;
-		const struct usb_audio_feature_unit *fu;
+		const union usb_audio_feature_unit *fu;
 		const struct usb_audio_processing_unit *pu;
 		const struct usb_audio_extension_unit *eu;
 		const struct usb_audio_clksrc_unit *cu;
@@ -254,6 +265,7 @@ struct io_terminal {
 	struct terminal_list **inputs; /* list of source input terminals */
 	struct terminal_list *output; /* list of destination output terminals */
 	int direct;		/* directly connected to an output terminal */
+	uint8_t clock;
 };
 
 #define UAC_OUTPUT	0
@@ -286,7 +298,7 @@ Static const usb_interface_descriptor_t 
 
 Static void	uaudio_mixer_add_ctl(struct uaudio_softc *, struct mixerctl *);
 Static char	*uaudio_id_name
-	(struct uaudio_softc *, const struct io_terminal *, int);
+	(struct uaudio_softc *, const struct io_terminal *, uint8_t);
 #ifdef UAUDIO_DEBUG
 Static void	uaudio_dump_cluster
 	(struct uaudio_softc *, const union usb_audio_cluster *);
@@ -307,7 +319,7 @@ Static const char *uaudio_get_terminal_n
 Static int	uaudio_determine_class
 	(const struct io_terminal *, struct mixerctl *);
 Static const char *uaudio_feature_name
-	(const struct io_terminal *, struct mixerctl *);
+	(const struct io_terminal *, uint8_t, int);
 Static void	uaudio_add_feature
 	(struct uaudio_softc *, const struct io_terminal *, int);
 Static void	uaudio_add_processing_updown
@@ -348,6 +360,7 @@ Static void	uaudio_set
 Static void	uaudio_ctl_set
 	(struct uaudio_softc *, int, struct mixerctl *, int, int);
 
+Static usbd_status uaudio_speed(struct uaudio_softc *, int, int, uint8_t *, int);
 Static usbd_status uaudio_set_speed(struct uaudio_softc *, int, int, u_int);
 
 Static usbd_status uaudio_chan_open(struct uaudio_softc *, struct chan *);
@@ -357,7 +370,7 @@ Static usbd_status uaudio_chan_alloc_buf
 	(struct uaudio_softc *, struct chan *);
 Static void	uaudio_chan_free_buffers(struct uaudio_softc *, struct chan *);
 Static void	uaudio_chan_init
-	(struct chan *, int, const struct audio_params *, int);
+	(struct chan *, int, const struct audio_params *, int, bool);
 Static void	uaudio_chan_set_param(struct chan *, u_char *, u_char *, int);
 Static void	uaudio_chan_ptransfer(struct chan *);
 Static void	uaudio_chan_pintr
@@ -414,7 +427,6 @@ static void uaudio_childdet(device_t, de
 static int uaudio_activate(device_t, enum devact);
 
 
-
 CFATTACH_DECL2_NEW(uaudio, sizeof(struct uaudio_softc),
     uaudio_match, uaudio_attach, uaudio_detach, uaudio_activate, NULL,
     uaudio_childdet);
@@ -510,6 +522,25 @@ uaudio_attach(device_t parent, device_t 
 	sc->sc_playchan.altidx = -1;
 	sc->sc_recchan.altidx = -1;
 
+	switch (sc->sc_udev->ud_speed) {
+	case USB_SPEED_LOW:
+	case USB_SPEED_FULL:
+		sc->sc_usb_frames_per_second = USB_FRAMES_PER_SECOND;
+		sc->sc_playchan.nframes =
+		    sc->sc_recchan.nframes = UAUDIO_NFRAMES;
+		break;
+	default: /* HIGH, SUPER, SUPER_PLUS, more ? */
+		sc->sc_usb_frames_per_second = USB_FRAMES_PER_SECOND * USB_UFRAMES_PER_FRAME;
+		sc->sc_playchan.nframes =
+		    sc->sc_recchan.nframes = UAUDIO_NFRAMES_HI;
+		break;
+	}
+	sc->sc_playchan.nchanbufs =
+	    sc->sc_recchan.nchanbufs = UAUDIO_NCHANBUFS;
+
+	DPRINTF("usb fps %u, max channel frames %u, max channel buffers %u\n",
+	    sc->sc_usb_frames_per_second, sc->sc_playchan.nframes, sc->sc_playchan.nchanbufs);
+
 	if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_FRAC)
 		sc->sc_altflags |= UA_NOFRAC;
 
@@ -785,11 +816,8 @@ uaudio_mixer_add_ctl(struct uaudio_softc
 
 	if (mc->mul == 0)
 		mc->mul = 1;
-	mc->delta = res * 255 / mc->mul;
-	if (mc->delta > 0 && mc->delta < 255)
-		mc->high = 255 / mc->delta * mc->delta;
-	else
-		mc->high = 255;
+
+	mc->delta = (res * 255 + mc->mul - 1) / mc->mul;
 
 #ifdef UAUDIO_DEBUG
 	if (uaudiodebug > 2) {
@@ -811,11 +839,12 @@ uaudio_mixer_add_ctl(struct uaudio_softc
 
 Static char *
 uaudio_id_name(struct uaudio_softc *sc,
-    const struct io_terminal *iot, int id)
+    const struct io_terminal *iot, uint8_t id)
 {
 	static char tbuf[32];
 
-	snprintf(tbuf, sizeof(tbuf), "i%d", id);
+	snprintf(tbuf, sizeof(tbuf), "i%u", id);
+
 	return tbuf;
 }
 
@@ -879,6 +908,7 @@ uaudio_get_cluster(struct uaudio_softc *
 {
 	union usb_audio_cluster r;
 	const uaudio_cs_descriptor_t *dp;
+	u_int pins;
 	int i;
 
 	for (i = 0; i < 25; i++) { /* avoid infinite loops */
@@ -891,37 +921,78 @@ uaudio_get_cluster(struct uaudio_softc *
 			switch (sc->sc_version) {
 			case UAUDIO_VERSION1:
 				r.v1.bNrChannels = iot[id].d.it->v1.bNrChannels;
-				USETW(r.v1.wChannelConfig, UGETW(iot[id].d.it->v1.wChannelConfig));
+				USETW(r.v1.wChannelConfig,
+				    UGETW(iot[id].d.it->v1.wChannelConfig));
 				r.v1.iChannelNames = iot[id].d.it->v1.iChannelNames;
 				break;
 			case UAUDIO_VERSION2:
 				r.v2.bNrChannels = iot[id].d.it->v2.bNrChannels;
-				USETDW(r.v2.bmChannelConfig, UGETW(iot[id].d.it->v2.bmChannelConfig));
+				USETDW(r.v2.bmChannelConfig,
+				    UGETW(iot[id].d.it->v2.bmChannelConfig));
 				r.v2.iChannelNames = iot[id].d.it->v2.iChannelNames;
 				break;
 			}
 			return r;
 		case UDESCSUB_AC_OUTPUT:
+			/* XXX This is not really right */
 			id = iot[id].d.ot->v1.bSourceId;
 			break;
 		case UDESCSUB_AC_MIXER:
-			r = *(const union usb_audio_cluster *)
-				&iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins];
+			switch (sc->sc_version) {
+			case UAUDIO_VERSION1:
+				pins = iot[id].d.mu->bNrInPins;
+				r.v1 = *(const struct usb_audio_v1_cluster *)
+				    &iot[id].d.mu->baSourceId[pins];
+				break;
+			case UAUDIO_VERSION2:
+				pins = iot[id].d.mu->bNrInPins;
+				r.v2 = *(const struct usb_audio_v2_cluster *)
+				    &iot[id].d.mu->baSourceId[pins];
+				break;
+			}
 			return r;
 		case UDESCSUB_AC_SELECTOR:
 			/* XXX This is not really right */
 			id = iot[id].d.su->baSourceId[0];
 			break;
 		case UDESCSUB_AC_FEATURE:
-			id = iot[id].d.fu->bSourceId;
+			/* XXX This is not really right */
+			switch (sc->sc_version) {
+			case UAUDIO_VERSION1:
+				id = iot[id].d.fu->v1.bSourceId;
+				break;
+			case UAUDIO_VERSION2:
+				id = iot[id].d.fu->v2.bSourceId;
+				break;
+			}
 			break;
 		case UDESCSUB_AC_PROCESSING:
-			r = *(const union usb_audio_cluster *)
-				&iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins];
+			switch (sc->sc_version) {
+			case UAUDIO_VERSION1:
+				pins = iot[id].d.pu->bNrInPins;
+				r.v1 = *(const struct usb_audio_v1_cluster *)
+				    &iot[id].d.pu->baSourceId[pins];
+				break;
+			case UAUDIO_VERSION2:
+				pins = iot[id].d.pu->bNrInPins;
+				r.v2 = *(const struct usb_audio_v2_cluster *)
+				    &iot[id].d.pu->baSourceId[pins];
+				break;
+			}
 			return r;
 		case UDESCSUB_AC_EXTENSION:
-			r = *(const union usb_audio_cluster *)
-				&iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins];
+			switch (sc->sc_version) {
+			case UAUDIO_VERSION1:
+				pins = iot[id].d.eu->bNrInPins;
+				r.v1 = *(const struct usb_audio_v1_cluster *)
+				    &iot[id].d.eu->baSourceId[pins];
+				break;
+			case UAUDIO_VERSION2:
+				pins = iot[id].d.eu->bNrInPins;
+				r.v2 = *(const struct usb_audio_v2_cluster *)
+				    &iot[id].d.eu->baSourceId[pins];
+				break;
+			}
 			return r;
 		default:
 			goto bad;
@@ -951,9 +1022,10 @@ uaudio_add_input(struct uaudio_softc *sc
 			    d->v1.iChannelNames, d->v1.iTerminal);
 #endif
 		/* If USB input terminal, record wChannelConfig */
-		if ((UGETW(d->v1.wTerminalType) & 0xff00) != 0x0100)
+		if ((UGETW(d->v1.wTerminalType) & 0xff00) != UAT_UNDEFINED)
 			return;
 		sc->sc_channel_config = UGETW(d->v1.wChannelConfig);
+		sc->sc_clock[id] = 0;
 		break;
 	case UAUDIO_VERSION2:
 #ifdef UAUDIO_DEBUG
@@ -965,9 +1037,10 @@ uaudio_add_input(struct uaudio_softc *sc
 			    d->v2.iChannelNames, d->v2.bCSourceId, d->v2.iTerminal);
 #endif
 		/* If USB input terminal, record wChannelConfig */
-		if ((UGETW(d->v2.wTerminalType) & 0xff00) != 0x0100)
+		if ((UGETW(d->v2.wTerminalType) & 0xff00) != UAT_UNDEFINED)
 			return;
 		sc->sc_channel_config = UGETDW(d->v2.bmChannelConfig);
+		sc->sc_clock[id] = d->v2.bCSourceId;
 		break;
 	}
 }
@@ -986,12 +1059,14 @@ uaudio_add_output(struct uaudio_softc *s
 			    "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n",
 			    d->v1.bTerminalId, UGETW(d->v1.wTerminalType), d->v1.bAssocTerminal,
 			    d->v1.bSourceId, d->v1.iTerminal);
+		sc->sc_clock[id] = 0;
 		break;
 	case UAUDIO_VERSION2:
 		DPRINTFN(2,"bTerminalId=%d wTerminalType=0x%04x "
 			    "bAssocTerminal=%d bSourceId=%d bCSourceId=%d, iTerminal=%d\n",
 			    d->v2.bTerminalId, UGETW(d->v2.wTerminalType), d->v2.bAssocTerminal,
 			    d->v2.bSourceId, d->v2.bCSourceId, d->v2.iTerminal);
+		sc->sc_clock[id] = d->v2.bCSourceId;
 		break;
 	}
 #endif
@@ -1002,15 +1077,20 @@ uaudio_add_mixer(struct uaudio_softc *sc
 {
 	const struct usb_audio_mixer_unit *d;
 	const union usb_audio_mixer_unit_1 *d1;
-	int c, chs, ichs, ochs, i, o, bno, p, mo, mc, k;
+	int c, chs, ichs, ochs, nchs, i, o, bno, p, k;
+	size_t bm_size;
 	const uByte *bm;
 	struct mixerctl mix;
 
 	d = iot[id].d.mu;
+	d1 = (const union usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins];
 	DPRINTFN(2,"bUnitId=%d bNrInPins=%d\n",
 		    d->bUnitId, d->bNrInPins);
 
-	d1 = (const union usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins];
+	mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
+	uaudio_determine_class(&iot[id], &mix);
+	mix.type = MIX_SIGNED_16;
+	mix.ctlunit = AudioNvolume;
 
 	/* Compute the number of input channels */
 	/* and the number of output channels */
@@ -1028,60 +1108,55 @@ uaudio_add_mixer(struct uaudio_softc *sc
 			ichs += uaudio_get_cluster(sc, d->baSourceId[i], iot).v2.bNrChannels;
 		ochs = d1->v2.bNrChannels;
 		DPRINTFN(2,"ichs=%d ochs=%d\n", ichs, ochs);
-		bm = d1->v2.bmControls;
+		bm = d1->v2.bmMixerControls;
+		bm_size = ichs * ochs / 8 + ((ichs * ochs % 8) ? 1 : 0);
+		/* bmControls */
+		if ((bm[bm_size] & UA_MIX_CLUSTER_MASK) != UA_MIX_CLUSTER_RW)
+			return;
 		break;
 	default:
 		return;
 	}
-	mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
-	uaudio_determine_class(&iot[id], &mix);
-	mix.type = MIX_SIGNED_16;
-	mix.ctlunit = AudioNvolume;
-#define _BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1)
+
 	for (p = i = 0; i < d->bNrInPins; i++) {
 		switch (sc->sc_version) {
 		case UAUDIO_VERSION1:
-			chs = uaudio_get_cluster(sc, d->baSourceId[i], iot).v1.bNrChannels;
+			chs = uaudio_get_cluster(sc, d->baSourceId[i], iot)
+			    .v1.bNrChannels;
 			break;
 		case UAUDIO_VERSION2:
-			chs = uaudio_get_cluster(sc, d->baSourceId[i], iot).v2.bNrChannels;
+			chs = uaudio_get_cluster(sc, d->baSourceId[i], iot)
+			    .v2.bNrChannels;
 			break;
 		default:
-			chs = 0;
-			break;
+			continue;
 		}
-		mc = 0;
-		for (c = 0; c < chs; c++) {
-			mo = 0;
+
+#define _BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1)
+
+		nchs = chs < MIX_MAX_CHAN ? chs : MIX_MAX_CHAN;
+
+		k = 0;
+		for (c = 0; c < nchs; c++) {
 			for (o = 0; o < ochs; o++) {
 				bno = (p + c) * ochs + o;
 				if (_BIT(bno))
-					mo++;
+					mix.wValue[k++] =
+						MAKE(p+c+1, o+1);
 			}
-			if (mo == 1)
-				mc++;
-		}
-		if (mc == chs && chs <= MIX_MAX_CHAN) {
-			k = 0;
-			for (c = 0; c < chs; c++)
-				for (o = 0; o < ochs; o++) {
-					bno = (p + c) * ochs + o;
-					if (_BIT(bno))
-						mix.wValue[k++] =
-							MAKE(p+c+1, o+1);
-				}
-			snprintf(mix.ctlname, sizeof(mix.ctlname), "mix%d-%s",
-			    d->bUnitId, uaudio_id_name(sc, iot,
-			    d->baSourceId[i]));
-			mix.nchan = chs;
-			uaudio_mixer_add_ctl(sc, &mix);
-		} else {
-			/* XXX */
 		}
+		mix.nchan = nchs;
+
+		snprintf(mix.ctlname, sizeof(mix.ctlname),
+		    "mix%d-%s", d->bUnitId,
+		    uaudio_id_name(sc, iot, d->baSourceId[i])
+		);
+		uaudio_mixer_add_ctl(sc, &mix);
+
 #undef _BIT
+
 		p += chs;
 	}
-
 }
 
 Static void
@@ -1095,7 +1170,10 @@ uaudio_add_selector(struct uaudio_softc 
 	DPRINTFN(2,"bUnitId=%d bNrInPins=%d\n",
 		    d->bUnitId, d->bNrInPins);
 	mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
-	mix.wValue[0] = MAKE(0, 0);
+	if (sc->sc_version == UAUDIO_VERSION2)
+		mix.wValue[0] = MAKE(V2_CUR_SELECTOR, 0);
+	else
+		mix.wValue[0] = MAKE(0, 0);
 	uaudio_determine_class(&iot[id], &mix);
 	mix.nchan = 1;
 	mix.type = MIX_SELECTOR;
@@ -1106,8 +1184,9 @@ uaudio_add_selector(struct uaudio_softc 
 	mix.mul = mix.range0.maxval - mix.range0.minval;
 	wp = snprintf(mix.ctlname, MAX_AUDIO_DEV_LEN, "sel%d-", d->bUnitId);
 	for (i = 1; i <= d->bNrInPins; i++) {
-		wp += snprintf(mix.ctlname + wp, MAX_AUDIO_DEV_LEN - wp,
-			       "i%d", d->baSourceId[i - 1]);
+		wp += strlcpy(mix.ctlname + wp,
+		    uaudio_id_name(sc, iot, d->baSourceId[i-1]),
+		    MAX_AUDIO_DEV_LEN - wp);
 		if (wp > MAX_AUDIO_DEV_LEN - 1)
 			break;
 	}
@@ -1242,13 +1321,13 @@ uaudio_determine_class(const struct io_t
 }
 
 Static const char *
-uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix)
+uaudio_feature_name(const struct io_terminal *iot,
+    uint8_t class, int terminal_type)
 {
-	int terminal_type;
 
-	terminal_type = uaudio_determine_class(iot, mix);
-	if (mix->class == UAC_RECORD && terminal_type == 0)
+	if (class == UAC_RECORD && terminal_type == 0)
 		return AudioNmixerout;
+
 	DPRINTF("terminal_type=%s\n", uaudio_get_terminal_name(terminal_type));
 	switch (terminal_type) {
 	case UAT_STREAM:
@@ -1342,123 +1421,229 @@ uaudio_feature_name(const struct io_term
 	return AudioNmaster;
 }
 
+static void
+uaudio_add_feature_mixer(struct uaudio_softc *sc, const struct io_terminal *iot,
+    int unit, int ctl, struct mixerctl *mc)
+{
+	const char *mixername, *attr = NULL;
+	int terminal_type;
+
+	mc->wIndex = MAKE(unit, sc->sc_ac_iface);
+	terminal_type = uaudio_determine_class(iot, mc);
+	mixername = uaudio_feature_name(iot, mc->class, terminal_type);
+	switch (ctl) {
+	case MUTE_CONTROL:
+		mc->type = MIX_ON_OFF;
+		mc->ctlunit = "";
+		attr = AudioNmute;
+		break;
+	case VOLUME_CONTROL:
+		mc->type = MIX_SIGNED_16;
+		mc->ctlunit = AudioNvolume;
+		attr = NULL;
+		break;
+	case BASS_CONTROL:
+		mc->type = MIX_SIGNED_8;
+		mc->ctlunit = AudioNbass;
+		attr = AudioNbass;
+		break;
+	case MID_CONTROL:
+		mc->type = MIX_SIGNED_8;
+		mc->ctlunit = AudioNmid;
+		attr = AudioNmid;
+		break;
+	case TREBLE_CONTROL:
+		mc->type = MIX_SIGNED_8;
+		mc->ctlunit = AudioNtreble;
+		attr = AudioNtreble;
+		break;
+	case GRAPHIC_EQUALIZER_CONTROL:
+		return; /* XXX don't add anything */
+		break;
+	case AGC_CONTROL:
+		mc->type = MIX_ON_OFF;
+		mc->ctlunit = "";
+		attr = AudioNagc;
+		break;
+	case DELAY_CONTROL:
+		mc->type = MIX_UNSIGNED_16;
+		mc->ctlunit = "4 ms";
+		attr = AudioNdelay;
+		break;
+	case BASS_BOOST_CONTROL:
+		mc->type = MIX_ON_OFF;
+		mc->ctlunit = "";
+		attr = AudioNbassboost;
+		break;
+	case LOUDNESS_CONTROL:
+		mc->type = MIX_ON_OFF;
+		mc->ctlunit = "";
+		attr = AudioNloudness;
+		break;
+	case GAIN_CONTROL:
+		mc->type = MIX_SIGNED_16;
+		mc->ctlunit = "gain";
+		attr = "gain";;
+		break;
+	case GAINPAD_CONTROL:
+		mc->type = MIX_SIGNED_16;
+		mc->ctlunit = "gainpad";
+		attr = "gainpad";;
+		break;
+	case PHASEINV_CONTROL:
+		mc->type = MIX_ON_OFF;
+		mc->ctlunit = "";
+		attr = "phaseinv";;
+		break;
+	case UNDERFLOW_CONTROL:
+		mc->type = MIX_ON_OFF;
+		mc->ctlunit = "";
+		attr = "underflow";;
+		break;
+	case OVERFLOW_CONTROL:
+		mc->type = MIX_ON_OFF;
+		mc->ctlunit = "";
+		attr = "overflow";;
+		break;
+	default:
+		return; /* XXX don't add anything */
+		break;
+	}
+
+	if (attr != NULL) {
+		snprintf(mc->ctlname, sizeof(mc->ctlname),
+		    "%s.%s", mixername, attr);
+	} else {
+		snprintf(mc->ctlname, sizeof(mc->ctlname),
+		    "%s", mixername);
+	}
+
+	uaudio_mixer_add_ctl(sc, mc);
+}
+
 Static void
 uaudio_add_feature(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
 {
-	const struct usb_audio_feature_unit *d;
+	const union usb_audio_feature_unit *d;
 	const uByte *ctls;
+	const uDWord *ctls2;
 	int ctlsize;
 	int nchan;
 	u_int fumask, mmask, cmask;
 	struct mixerctl mix;
 	int chan, ctl, i, unit;
-	const char *mixername;
 
-#define GET(i) (ctls[(i)*ctlsize] | \
-		(ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0))
 	d = iot[id].d.fu;
-	ctls = d->bmaControls;
-	ctlsize = d->bControlSize;
-	if (ctlsize == 0) {
-		DPRINTF("ignoring feature %d with controlSize of zero\n", id);
-		return;
-	}
-	nchan = (d->bLength - 7) / ctlsize;
-	mmask = GET(0);
-	/* Figure out what we can control */
-	for (cmask = 0, chan = 1; chan < nchan; chan++) {
-		DPRINTFN(9,"chan=%d mask=%x\n",
-			    chan, GET(chan));
-		cmask |= GET(chan);
-	}
-
-	DPRINTFN(1,"bUnitId=%d, "
-		    "%d channels, mmask=0x%04x, cmask=0x%04x\n",
-		    d->bUnitId, nchan, mmask, cmask);
-
-	if (nchan > MIX_MAX_CHAN)
-		nchan = MIX_MAX_CHAN;
-	unit = d->bUnitId;
-	mix.wIndex = MAKE(unit, sc->sc_ac_iface);
-	for (ctl = MUTE_CONTROL; ctl < LOUDNESS_CONTROL; ctl++) {
-		fumask = FU_MASK(ctl);
-		DPRINTFN(4,"ctl=%d fumask=0x%04x\n",
-			    ctl, fumask);
-		if (mmask & fumask) {
-			mix.nchan = 1;
-			mix.wValue[0] = MAKE(ctl, 0);
-		} else if (cmask & fumask) {
-			mix.nchan = nchan - 1;
-			for (i = 1; i < nchan; i++) {
-				if (GET(i) & fumask)
-					mix.wValue[i-1] = MAKE(ctl, i);
-				else
-					mix.wValue[i-1] = -1;
+
+	switch (sc->sc_version) {
+	case UAUDIO_VERSION1:
+
+#define GETV1(i) (ctls[(i)*ctlsize] | \
+		(ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0))
+
+		ctls = d->v1.bmaControls;
+		ctlsize = d->v1.bControlSize;
+		if (ctlsize == 0) {
+			DPRINTF("ignoring feature %d with controlSize of zero\n", id);
+			return;
+		}
+
+		/* offsetof bmaControls + sizeof iFeature == 7 */
+		nchan = (d->v1.bLength - 7) / ctlsize;
+		mmask = GETV1(0);
+		/* Figure out what we can control */
+		for (cmask = 0, chan = 1; chan < nchan; chan++) {
+			DPRINTFN(9,"chan=%d mask=%x\n",
+				    chan, GETV1(chan));
+			cmask |= GETV1(chan);
+		}
+
+		DPRINTFN(1,"bUnitId=%d, "
+			    "%d channels, mmask=0x%04x, cmask=0x%04x\n",
+			    d->v1.bUnitId, nchan, mmask, cmask);
+
+		if (nchan > MIX_MAX_CHAN)
+			nchan = MIX_MAX_CHAN;
+		unit = d->v1.bUnitId;
+
+		for (ctl = MUTE_CONTROL; ctl <= LOUDNESS_CONTROL; ctl++) {
+			fumask = FU_MASK(ctl);
+			DPRINTFN(4,"ctl=%d fumask=0x%04x\n",
+				    ctl, fumask);
+			if (mmask & fumask) {
+				mix.nchan = 1;
+				mix.wValue[0] = MAKE(ctl, 0);
+			} else if (cmask & fumask) {
+				mix.nchan = nchan - 1;
+				for (i = 1; i < nchan; i++) {
+					if (GETV1(i) & fumask)
+						mix.wValue[i-1] = MAKE(ctl, i);
+					else
+						mix.wValue[i-1] = -1;
+				}
+			} else {
+				continue;
 			}
-		} else {
-			continue;
+
+			uaudio_add_feature_mixer(sc, &iot[id], unit, ctl, &mix);
 		}
-#undef GET
-		mixername = uaudio_feature_name(&iot[id], &mix);
-		switch (ctl) {
-		case MUTE_CONTROL:
-			mix.type = MIX_ON_OFF;
-			mix.ctlunit = "";
-			snprintf(mix.ctlname, sizeof(mix.ctlname),
-				 "%s.%s", mixername, AudioNmute);
-			break;
-		case VOLUME_CONTROL:
-			mix.type = MIX_SIGNED_16;
-			mix.ctlunit = AudioNvolume;
-			strlcpy(mix.ctlname, mixername, sizeof(mix.ctlname));
-			break;
-		case BASS_CONTROL:
-			mix.type = MIX_SIGNED_8;
-			mix.ctlunit = AudioNbass;
-			snprintf(mix.ctlname, sizeof(mix.ctlname),
-				 "%s.%s", mixername, AudioNbass);
-			break;
-		case MID_CONTROL:
-			mix.type = MIX_SIGNED_8;
-			mix.ctlunit = AudioNmid;
-			snprintf(mix.ctlname, sizeof(mix.ctlname),
-				 "%s.%s", mixername, AudioNmid);
-			break;
-		case TREBLE_CONTROL:
-			mix.type = MIX_SIGNED_8;
-			mix.ctlunit = AudioNtreble;
-			snprintf(mix.ctlname, sizeof(mix.ctlname),
-				 "%s.%s", mixername, AudioNtreble);
-			break;
-		case GRAPHIC_EQUALIZER_CONTROL:
-			continue; /* XXX don't add anything */
-			break;
-		case AGC_CONTROL:
-			mix.type = MIX_ON_OFF;
-			mix.ctlunit = "";
-			snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s",
-				 mixername, AudioNagc);
-			break;
-		case DELAY_CONTROL:
-			mix.type = MIX_UNSIGNED_16;
-			mix.ctlunit = "4 ms";
-			snprintf(mix.ctlname, sizeof(mix.ctlname),
-				 "%s.%s", mixername, AudioNdelay);
-			break;
-		case BASS_BOOST_CONTROL:
-			mix.type = MIX_ON_OFF;
-			mix.ctlunit = "";
-			snprintf(mix.ctlname, sizeof(mix.ctlname),
-				 "%s.%s", mixername, AudioNbassboost);
-			break;
-		case LOUDNESS_CONTROL:
-			mix.type = MIX_ON_OFF;
-			mix.ctlunit = "";
-			snprintf(mix.ctlname, sizeof(mix.ctlname),
-				 "%s.%s", mixername, AudioNloudness);
-			break;
+#undef GETV1
+		break;
+
+	case UAUDIO_VERSION2:
+
+#define GETV2(i) UGETDW(ctls2[(i)])
+
+		ctls2 = d->v2.bmaControls;
+
+		/* offsetof bmaControls + sizeof iFeature == 6 */
+		nchan = (d->v2.bLength - 6) / 4;
+		if (nchan <= 0) {
+			DPRINTF("ignoring feature %d with no controls\n", id);
+			return;
 		}
-		uaudio_mixer_add_ctl(sc, &mix);
+
+		mmask = GETV2(0);
+		/* Figure out what we can control */
+		for (cmask = 0, chan = 1; chan < nchan; chan++) {
+			DPRINTFN(9,"chan=%d mask=%x\n",
+				    chan, GETV2(chan));
+			cmask |= GETV2(chan);
+		}
+
+		DPRINTFN(1,"bUnitId=%d, "
+			    "%d channels, mmask=0x%04x, cmask=0x%04x\n",
+			    d->v2.bUnitId, nchan, mmask, cmask);
+
+		if (nchan > MIX_MAX_CHAN)
+			nchan = MIX_MAX_CHAN;
+		unit = d->v2.bUnitId;
+
+		for (ctl = MUTE_CONTROL; ctl <= OVERFLOW_CONTROL; ctl++) {
+			fumask = V2_FU_MASK(ctl);
+			DPRINTFN(4,"ctl=%d fumask=0x%08x\n",
+				    ctl, fumask);
+
+			if (mmask & fumask) {
+				mix.nchan = 1;
+				mix.wValue[0] = MAKE(ctl, 0);
+			} else if (cmask & fumask) {
+				mix.nchan = nchan-1;
+				for (i = 1; i < nchan; ++i) {
+					if (GETV2(i) & fumask)
+						mix.wValue[i-1] = MAKE(ctl, i);
+					else
+						mix.wValue[i-1] = -1;
+				}
+			} else {
+				continue;
+			}
+
+			uaudio_add_feature_mixer(sc, &iot[id], unit, ctl, &mix);
+		}
+
+#undef GETV2
+		break;
 	}
 }
 
@@ -1750,7 +1935,17 @@ uaudio_io_terminaltype(struct uaudio_sof
 		it->inputs_size = 1;
 		return uaudio_merge_terminal_list(it);
 	case UDESCSUB_AC_FEATURE:
-		src_id = it->d.fu->bSourceId;
+		switch (sc->sc_version) {
+		case UAUDIO_VERSION1:
+			src_id = it->d.fu->v1.bSourceId;
+			break;
+		case UAUDIO_VERSION2:
+			src_id = it->d.fu->v2.bSourceId;
+			break;
+		default:
+			/* cannot happen */
+			return NULL;
+		}
 		it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
 		if (it->inputs == NULL) {
 			aprint_error("uaudio_io_terminaltype: no memory\n");
@@ -1891,7 +2086,7 @@ uaudio_process_as(struct uaudio_softc *s
 	const usb_endpoint_descriptor_audio_t *ed;
 	const usb_endpoint_descriptor_audio_t *epdesc1;
 	const struct usb_audio_streaming_endpoint_descriptor *sed;
-	int format, chan __unused, prec, bps, enc;
+	int format, chan __unused, prec, bps, enc, terminal;
 	int dir, type, sync, epcount;
 	struct as_info ai;
 	const char *format_str __unused;
@@ -1950,7 +2145,9 @@ uaudio_process_as(struct uaudio_softc *s
 					 "bInterval=%d bRefresh=%d bSynchAddress=%d\n",
 					 ed->bLength, ed->bDescriptorType, ed->bEndpointAddress,
 					 ed->bmAttributes, UGETW(ed->wMaxPacketSize),
-					 ed->bInterval, ed->bRefresh, ed->bSynchAddress);
+					 ed->bInterval,
+					 ed->bLength > 7 ? ed->bRefresh : 0,
+					 ed->bLength > 8 ? ed->bSynchAddress : 0);
 				if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
 					return USBD_INVAL;
 				break;
@@ -1963,9 +2160,10 @@ uaudio_process_as(struct uaudio_softc *s
 					 epdesc1->bLength, epdesc1->bDescriptorType,
 					 epdesc1->bEndpointAddress, epdesc1->bmAttributes,
 					 UGETW(epdesc1->wMaxPacketSize), epdesc1->bInterval,
-					 epdesc1->bRefresh, epdesc1->bSynchAddress);
+					 epdesc1->bLength > 7 ? epdesc1->bRefresh : 0,
+					 epdesc1->bLength > 8 ? epdesc1->bSynchAddress : 0);
 #if 0
-				if (epdesc1->bSynchAddress != 0) {
+				if (epdesc1->bLength > 8 && epdesc1->bSynchAddress != 0) {
 					aprint_error_dev(sc->sc_dev,
 					    "invalid endpoint: bSynchAddress=0\n");
 					return USBD_INVAL;
@@ -1978,7 +2176,7 @@ uaudio_process_as(struct uaudio_softc *s
 					return USBD_INVAL;
 				}
 #if 0
-				if (epdesc1->bEndpointAddress != ed->bSynchAddress) {
+				if (ed->bLength > 8 && epdesc1->bEndpointAddress != ed->bSynchAddress) {
 					aprint_error_dev(sc->sc_dev,
 					    "invalid endpoint addresses: "
 					    "ep[0]->bSynchAddress=%#x "
@@ -2099,6 +2297,7 @@ leave:
 	enc = AUDIO_ENCODING_NONE;
 	switch (sc->sc_version) {
 	case UAUDIO_VERSION1:
+		terminal = 0;
 		switch (format) {
 		case UA_FMT_PCM:
 			if (prec == 8) {
@@ -2135,6 +2334,7 @@ leave:
 		}
 		break;
 	case UAUDIO_VERSION2:
+		terminal = asid->v2.bTerminalLink;
 		if (format & UA_V2_FMT_PCM) {
 			if (prec == 8) {
 				sc->sc_altflags |= HAS_8;
@@ -2171,11 +2371,11 @@ leave:
 		return USBD_NORMAL_COMPLETION;
 	}
 #ifdef UAUDIO_DEBUG
+	aprint_debug_dev(sc->sc_dev, "%s: %dch, %d/%dbit, %s,",
+	       dir == UE_DIR_IN ? "recording" : "playback",
+	       chan, prec, bps * 8, format_str);
 	switch (sc->sc_version) {
 	case UAUDIO_VERSION1:
-		aprint_debug_dev(sc->sc_dev, "%s: %dch, %d/%dbit, %s,",
-		       dir == UE_DIR_IN ? "recording" : "playback",
-		       chan, prec, asf1d->v1.bSubFrameSize * 8, format_str);
 		if (asf1d->v1.bSamFreqType == UA_SAMP_CONTINUOUS) {
 			aprint_debug(" %d-%dHz\n", UA_SAMP_LO(&asf1d->v1),
 			    UA_SAMP_HI(&asf1d->v1));
@@ -2187,7 +2387,7 @@ leave:
 			aprint_debug("Hz\n");
 		}
 		break;
-	/* XXX V2 */
+	/* UAUDIO_VERSION2 has no frequency information in the format */
 	}
 #endif
 	ai.alt = id->bAlternateSetting;
@@ -2201,6 +2401,7 @@ leave:
 	ai.nchan = chan;
 	ai.aformat = NULL;
 	ai.ifaceh = NULL;
+	ai.terminal = terminal;
 	uaudio_add_alt(sc, &ai);
 #ifdef UAUDIO_DEBUG
 	if (ai.attributes & UA_SED_FREQ_CONTROL)
@@ -2390,6 +2591,11 @@ uaudio_build_formats(struct uaudio_softc
 			break;
 		}
 
+		DPRINTF("alt[%d] = %d/%d %dch %u[%u,%u,...] alt %u\n", i,
+		    auf->validbits, auf->precision, auf->channels, auf->frequency_type,
+		    auf->frequency[0], auf->frequency[1],
+		    as->idesc->bAlternateSetting);
+
 		sc->sc_alts[i].aformat = auf;
 	}
 }
@@ -2557,10 +2763,24 @@ uaudio_identify_ac(struct uaudio_softc *
 			printf("\n");
 			break;
 		case UDESCSUB_AC_FEATURE:
-			printf("AC_FEATURE src=%d\n", iot[i].d.fu->bSourceId);
+			switch (sc->sc_version) {
+			case UAUDIO_VERSION1:
+				printf("AC_FEATURE src=%d\n", iot[i].d.fu->v1.bSourceId);
+				break;
+			case UAUDIO_VERSION2:
+				printf("AC_FEATURE src=%d\n", iot[i].d.fu->v2.bSourceId);
+				break;
+			}
 			break;
 		case UDESCSUB_AC_EFFECT:
-			printf("AC_EFFECT src=%d\n", iot[i].d.fu->bSourceId);
+			switch (sc->sc_version) {
+			case UAUDIO_VERSION1:
+				printf("AC_EFFECT src=%d\n", iot[i].d.fu->v1.bSourceId);
+				break;
+			case UAUDIO_VERSION2:
+				printf("AC_EFFECT src=%d\n", iot[i].d.fu->v2.bSourceId);
+				break;
+			}
 			break;
 		case UDESCSUB_AC_PROCESSING:
 			printf("AC_PROCESSING src=");
@@ -2687,13 +2907,15 @@ uaudio_identify_ac(struct uaudio_softc *
 				switch (dp->bDescriptorSubtype) {
 				case UDESCSUB_AC_INPUT:
 					if (UGETW(iot[i].d.it->v2.wTerminalType) != UAT_STREAM &&
-					    wi == MAKE(iot[i].d.it->v2.bCSourceId, sc->sc_ac_iface))
+					    wi == MAKE(iot[i].d.it->v2.bCSourceId, sc->sc_ac_iface)) {
 						sc->sc_ratemode[j] |= AUMODE_RECORD;
+					}
 					break;
 				case UDESCSUB_AC_OUTPUT:
 					if (UGETW(iot[i].d.it->v2.wTerminalType) != UAT_STREAM &&
-					    wi == MAKE(iot[i].d.ot->v2.bCSourceId, sc->sc_ac_iface))
+					    wi == MAKE(iot[i].d.ot->v2.bCSourceId, sc->sc_ac_iface)) {
 						sc->sc_ratemode[j] |= AUMODE_PLAY;
+					}
 					break;
 				}
 			}
@@ -2905,13 +3127,14 @@ uaudio_round_blocksize(void *addr, int b
 
 	/* chan.bytes_per_frame can be 0. */
 	if (mode == AUMODE_PLAY || sc->sc_recchan.bytes_per_frame <= 0) {
-		b = param->sample_rate * UAUDIO_NFRAMES * UAUDIO_NCHANBUFS;
+		b = param->sample_rate * sc->sc_recchan.nframes
+		    * sc->sc_recchan.nchanbufs;
 
 		/*
 		 * This does not make accurate value in the case
-		 * of b % USB_FRAMES_PER_SECOND != 0
+		 * of b % usb_frames_per_second != 0
 		 */
-		b /= USB_FRAMES_PER_SECOND;
+		b /= sc->sc_usb_frames_per_second;
 
 		b *= param->precision / 8 * param->channels;
 	} else {
@@ -2920,7 +3143,7 @@ uaudio_round_blocksize(void *addr, int b
 		 * See uaudio_set_format() and uaudio_chan_init()
 		 */
 		b = sc->sc_recchan.bytes_per_frame
-		    * UAUDIO_NFRAMES * UAUDIO_NCHANBUFS;
+		    * sc->sc_recchan.nframes * sc->sc_recchan.nchanbufs;
 	}
 
 	if (b <= 0)
@@ -3073,7 +3296,7 @@ uaudio_set(struct uaudio_softc *sc, int 
 	err = usbd_do_request(sc->sc_udev, &req, data);
 #ifdef UAUDIO_DEBUG
 	if (err)
-		DPRINTF("err=%d\n", err);
+		DPRINTF("err=%s\n", usbd_errstr(err));
 #endif
 }
 
@@ -3122,11 +3345,15 @@ uaudio_value2bsd(struct mixerctl *mc, in
 	if (mc->type == MIX_ON_OFF) {
 		val = (val != 0);
 	} else if (mc->type == MIX_SELECTOR) {
-		if (val < mc->ranges[0].minval || val > mc->ranges[0].maxval)
+		if (val < mc->ranges[0].minval)
 			val = mc->ranges[0].minval;
+		if (val > mc->ranges[0].maxval)
+			val = mc->ranges[0].maxval;
+	} else if (mc->mul > 0) {
+		val = ((uaudio_signext(mc->type, val) - mc->ranges[0].minval)
+		    * 255 + mc->mul - 1) / mc->mul;
 	} else
-		val = (uaudio_signext(mc->type, val) - mc->ranges[0].minval)
-		    * 255 / mc->mul;
+		val = 0;
 	DPRINTFN_CLEAN(5, "val'=%d\n", val);
 	return val;
 }
@@ -3134,36 +3361,38 @@ uaudio_value2bsd(struct mixerctl *mc, in
 Static int
 uaudio_bsd2value(struct mixerctl *mc, int val)
 {
+	int i;
+
 	DPRINTFN(5,"type=%03x val=%d min=%d max=%d ",
 		    mc->type, val, mc->ranges[0].minval, mc->ranges[0].maxval);
 	if (mc->type == MIX_ON_OFF) {
 		val = (val != 0);
 	} else if (mc->type == MIX_SELECTOR) {
-		if (val < mc->ranges[0].minval || val > mc->ranges[0].maxval)
+		if (val < mc->ranges[0].minval)
 			val = mc->ranges[0].minval;
+		if (val > mc->ranges[0].maxval)
+			val = mc->ranges[0].maxval;
 	} else {
 		if (val < 0)
 			val = 0;
-		else if (val > mc->high)
-			val = mc->high;
+		else if (val > 255)
+			val = 255;
 
-		val = val * mc->mul / mc->high + mc->ranges[0].minval;
+		val = val * (mc->mul + 1) / 256 + mc->ranges[0].minval;
 
-		if (mc->nranges > 0) {
-			int i;
+		for (i=0; i<mc->nranges; ++i) {
+			struct range *r = &mc->ranges[i];
 
-			for (i=0; i<mc->nranges; ++i) {
-				struct range *r = &mc->ranges[i];
-
-				if (val > r->maxval)
-					continue;
-				if (val < r->minval)
-					val = r->minval;
-				val = (val - r->minval + r->resval/2)
-				    / r->resval * r->resval
-				    + r->minval;
-				break;
-			}
+			if (r->resval == 0)
+				continue;
+			if (val > r->maxval)
+				continue;
+			if (val < r->minval)
+				val = r->minval;
+			val = (val - r->minval + r->resval/2)
+			    / r->resval * r->resval
+			    + r->minval;
+			break;
 		}
 	}
 	DPRINTFN_CLEAN(5, "val'=%d\n", val);
@@ -3174,10 +3403,10 @@ Static const char *
 uaudio_clockname(u_int attr)
 {
 	static const char *names[] = {
-		"ext",
-		"fixed",
-		"var",
-		"prog"
+		"clkext",
+		"clkfixed",
+		"clkvar",
+		"clkprog"
 	};
 
 	return names[attr & 3];
@@ -3191,7 +3420,12 @@ uaudio_makename(struct uaudio_softc *sc,
 
 	tmp = kmem_alloc(USB_MAX_ENCODED_STRING_LEN, KM_SLEEP);
 	err = usbd_get_string0(sc->sc_udev, idx, tmp, true);
-	count = snprintf(buf, len, "%s%d", err ? defname : tmp, id);
+
+	if (id != 0 || err)
+		count = snprintf(buf, len, "%s%d", err ? defname : tmp, id);
+	else
+		count = snprintf(buf, len, "%s", err ? defname : tmp);
+
 	kmem_free(tmp, USB_MAX_ENCODED_STRING_LEN);
 
 	return count;
@@ -3204,7 +3438,7 @@ uaudio_ctl_get(struct uaudio_softc *sc, 
 {
 	int val;
 
-	DPRINTFN(5,"which=%d chan=%d\n", which, chan);
+	DPRINTFN(5,"which=%d chan=%d ctl=%s type=%d\n", which, chan, mc->ctlname, mc->type);
 	mutex_exit(&sc->sc_lock);
 	val = uaudio_get(sc, which, UT_READ_CLASS_INTERFACE, mc->wValue[chan],
 			 mc->wIndex, MIX_SIZE(mc->type));
@@ -3217,6 +3451,7 @@ uaudio_ctl_set(struct uaudio_softc *sc, 
 	       int chan, int val)
 {
 
+	DPRINTFN(5,"which=%d chan=%d ctl=%s type=%d\n", which, chan, mc->ctlname, mc->type);
 	val = uaudio_bsd2value(mc, val);
 	mutex_exit(&sc->sc_lock);
 	uaudio_set(sc, which, UT_WRITE_CLASS_INTERFACE, mc->wValue[chan],
@@ -3230,12 +3465,15 @@ uaudio_mixer_get_port(void *addr, mixer_
 	struct uaudio_softc *sc;
 	struct mixerctl *mc;
 	int i, n, vals[MIX_MAX_CHAN], val;
+	int req;
 
 	DPRINTFN(2, "index=%d\n", cp->dev);
 	sc = addr;
 	if (sc->sc_dying)
 		return EIO;
 
+	req = sc->sc_version == UAUDIO_VERSION2 ? V2_CUR : GET_CUR;
+
 	n = cp->dev - UAC_NCLASSES;
 	if (n < 0 || n >= sc->sc_nctls)
 		return ENXIO;
@@ -3244,11 +3482,11 @@ uaudio_mixer_get_port(void *addr, mixer_
 	if (mc->type == MIX_ON_OFF) {
 		if (cp->type != AUDIO_MIXER_ENUM)
 			return EINVAL;
-		cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0);
+		cp->un.ord = uaudio_ctl_get(sc, req, mc, 0);
 	} else if (mc->type == MIX_SELECTOR) {
 		if (cp->type != AUDIO_MIXER_ENUM)
 			return EINVAL;
-		cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0);
+		cp->un.ord = uaudio_ctl_get(sc, req, mc, 0);
 	} else {
 		if (cp->type != AUDIO_MIXER_VALUE)
 			return EINVAL;
@@ -3256,7 +3494,7 @@ uaudio_mixer_get_port(void *addr, mixer_
 		    cp->un.value.num_channels != mc->nchan)
 			return EINVAL;
 		for (i = 0; i < mc->nchan; i++)
-			vals[i] = uaudio_ctl_get(sc, GET_CUR, mc, i);
+			vals[i] = uaudio_ctl_get(sc, req, mc, i);
 		if (cp->un.value.num_channels == 1 && mc->nchan != 1) {
 			for (val = 0, i = 0; i < mc->nchan; i++)
 				val += vals[i];
@@ -3275,12 +3513,15 @@ uaudio_mixer_set_port(void *addr, mixer_
 	struct uaudio_softc *sc;
 	struct mixerctl *mc;
 	int i, n, vals[MIX_MAX_CHAN];
+	int req;
 
 	DPRINTFN(2, "index = %d\n", cp->dev);
 	sc = addr;
 	if (sc->sc_dying)
 		return EIO;
 
+	req = sc->sc_version == UAUDIO_VERSION2 ? V2_CUR : SET_CUR;
+
 	n = cp->dev - UAC_NCLASSES;
 	if (n < 0 || n >= sc->sc_nctls)
 		return ENXIO;
@@ -3289,11 +3530,11 @@ uaudio_mixer_set_port(void *addr, mixer_
 	if (mc->type == MIX_ON_OFF) {
 		if (cp->type != AUDIO_MIXER_ENUM)
 			return EINVAL;
-		uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord);
+		uaudio_ctl_set(sc, req, mc, 0, cp->un.ord);
 	} else if (mc->type == MIX_SELECTOR) {
 		if (cp->type != AUDIO_MIXER_ENUM)
 			return EINVAL;
-		uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord);
+		uaudio_ctl_set(sc, req, mc, 0, cp->un.ord);
 	} else {
 		if (cp->type != AUDIO_MIXER_VALUE)
 			return EINVAL;
@@ -3306,7 +3547,7 @@ uaudio_mixer_set_port(void *addr, mixer_
 		else
 			return EINVAL;
 		for (i = 0; i < mc->nchan; i++)
-			uaudio_ctl_set(sc, SET_CUR, mc, i, vals[i]);
+			uaudio_ctl_set(sc, req, mc, i, vals[i]);
 	}
 	return 0;
 }
@@ -3358,7 +3599,7 @@ uaudio_trigger_input(void *addr, void *s
 	 * Start as half as many channels for recording as for playback.
 	 * This stops playback from stuttering in full-duplex operation.
 	 */
-	for (i = 0; i < UAUDIO_NCHANBUFS / 2; i++) {
+	for (i = 0; i < ch->nchanbufs / 2; i++) {
 		uaudio_chan_rtransfer(ch);
 	}
 
@@ -3409,7 +3650,7 @@ uaudio_trigger_output(void *addr, void *
 	ch->intr = intr;
 	ch->arg = arg;
 
-	for (i = 0; i < UAUDIO_NCHANBUFS; i++)
+	for (i = 0; i < ch->nchanbufs; i++)
 		uaudio_chan_ptransfer(ch);
 
 	mutex_enter(&sc->sc_intr_lock);
@@ -3423,14 +3664,14 @@ uaudio_chan_open(struct uaudio_softc *sc
 {
 	struct as_info *as;
 	usb_device_descriptor_t *ddesc;
-	int endpt, ifnum;
+	int endpt, clkid;
 	usbd_status err;
 
 	as = &sc->sc_alts[ch->altidx];
 	endpt = as->edesc->bEndpointAddress;
-	ifnum = as->idesc->bInterfaceNumber;
-	DPRINTF("endpt=0x%02x, speed=%d, alt=%d\n",
-		 endpt, ch->sample_rate, as->alt);
+	clkid = sc->sc_clock[as->terminal];
+	DPRINTF("endpt=0x%02x, clkid=%d, speed=%d, alt=%d\n",
+		 endpt, clkid, ch->sample_rate, as->alt);
 
 	/* Set alternate interface corresponding to the mode. */
 	err = usbd_set_interface(as->ifaceh, as->alt);
@@ -3443,7 +3684,7 @@ uaudio_chan_open(struct uaudio_softc *sc
 	ddesc = usbd_get_device_descriptor(sc->sc_udev);
 	if ((UGETW(ddesc->idVendor) != USB_VENDOR_ROLAND) &&
 	    (UGETW(ddesc->idProduct) != USB_PRODUCT_ROLAND_SD90)) {
-		err = uaudio_set_speed(sc, ifnum, endpt, ch->sample_rate);
+		err = uaudio_set_speed(sc, endpt, clkid, ch->sample_rate);
 		if (err) {
 			DPRINTF("set_speed failed err=%s\n", usbd_errstr(err));
 		}
@@ -3507,11 +3748,11 @@ uaudio_chan_alloc_buffers(struct uaudio_
 {
 	int i, size;
 
-	size = (ch->bytes_per_frame + ch->sample_size) * UAUDIO_NFRAMES;
-	for (i = 0; i < UAUDIO_NCHANBUFS; i++) {
+	size = (ch->bytes_per_frame + ch->sample_size) * ch->nframes;
+	for (i = 0; i < ch->nchanbufs; i++) {
 		struct usbd_xfer *xfer;
 
-		int err = usbd_create_xfer(ch->pipe, size, 0, UAUDIO_NFRAMES,
+		int err = usbd_create_xfer(ch->pipe, size, 0, ch->nframes,
 		    &xfer);
 		if (err)
 			goto bad;
@@ -3535,34 +3776,35 @@ uaudio_chan_free_buffers(struct uaudio_s
 {
 	int i;
 
-	for (i = 0; i < UAUDIO_NCHANBUFS; i++)
+	for (i = 0; i < ch->nchanbufs; i++)
 		usbd_destroy_xfer(ch->chanbufs[i].xfer);
 }
 
 Static void
 uaudio_chan_ptransfer(struct chan *ch)
 {
+	struct uaudio_softc *sc = ch->sc;
 	struct chanbuf *cb;
 	int i, n, size, residue, total;
 
-	if (ch->sc->sc_dying)
+	if (sc->sc_dying)
 		return;
 
 	/* Pick the next channel buffer. */
 	cb = &ch->chanbufs[ch->curchanbuf];
-	if (++ch->curchanbuf >= UAUDIO_NCHANBUFS)
+	if (++ch->curchanbuf >= ch->nchanbufs)
 		ch->curchanbuf = 0;
 
 	/* Compute the size of each frame in the next transfer. */
 	residue = ch->residue;
 	total = 0;
-	for (i = 0; i < UAUDIO_NFRAMES; i++) {
+	for (i = 0; i < ch->nframes; i++) {
 		size = ch->bytes_per_frame;
 		residue += ch->fraction;
-		if (residue >= USB_FRAMES_PER_SECOND) {
-			if ((ch->sc->sc_altflags & UA_NOFRAC) == 0)
+		if (residue >= sc->sc_usb_frames_per_second) {
+			if ((sc->sc_altflags & UA_NOFRAC) == 0)
 				size += ch->sample_size;
-			residue -= USB_FRAMES_PER_SECOND;
+			residue -= sc->sc_usb_frames_per_second;
 		}
 		cb->sizes[i] = size;
 		total += size;
@@ -3588,7 +3830,7 @@ uaudio_chan_ptransfer(struct chan *ch)
 #ifdef UAUDIO_DEBUG
 	if (uaudiodebug > 8) {
 		DPRINTF("buffer=%p, residue=0.%03d\n", cb->buffer, ch->residue);
-		for (i = 0; i < UAUDIO_NFRAMES; i++) {
+		for (i = 0; i < ch->nframes; i++) {
 			DPRINTF("   [%d] length %d\n", i, cb->sizes[i]);
 		}
 	}
@@ -3596,30 +3838,32 @@ uaudio_chan_ptransfer(struct chan *ch)
 
 	//DPRINTFN(5, "ptransfer xfer=%p\n", cb->xfer);
 	/* Fill the request */
-	usbd_setup_isoc_xfer(cb->xfer, cb, cb->sizes, UAUDIO_NFRAMES, 0,
+	usbd_setup_isoc_xfer(cb->xfer, cb, cb->sizes, ch->nframes, 0,
 	    uaudio_chan_pintr);
 
 	usbd_status err = usbd_transfer(cb->xfer);
 	if (err != USBD_IN_PROGRESS && err != USBD_NORMAL_COMPLETION)
-		device_printf(ch->sc->sc_dev, "ptransfer error %d\n", err);
+		device_printf(sc->sc_dev, "ptransfer error %d\n", err);
 }
 
 Static void
 uaudio_chan_pintr(struct usbd_xfer *xfer, void *priv,
 		  usbd_status status)
 {
+	struct uaudio_softc *sc;
 	struct chanbuf *cb;
 	struct chan *ch;
 	uint32_t count;
 
 	cb = priv;
 	ch = cb->chan;
+	sc = ch->sc;
 	/* Return if we are aborting. */
 	if (status == USBD_CANCELLED)
 		return;
 
 	if (status != USBD_NORMAL_COMPLETION)
-		device_printf(ch->sc->sc_dev, "pintr error: %s\n",
+		device_printf(sc->sc_dev, "pintr error: %s\n",
 		              usbd_errstr(status));
 
 	usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
@@ -3627,13 +3871,13 @@ uaudio_chan_pintr(struct usbd_xfer *xfer
 		    count, ch->transferred);
 #ifdef DIAGNOSTIC
 	if (count != cb->size) {
-		device_printf(ch->sc->sc_dev,
+		device_printf(sc->sc_dev,
 		    "uaudio_chan_pintr: count(%d) != size(%d), status(%d)\n",
 		    count, cb->size, status);
 	}
 #endif
 
-	mutex_enter(&ch->sc->sc_intr_lock);
+	mutex_enter(&sc->sc_intr_lock);
 	ch->transferred += cb->size;
 	/* Call back to upper layer */
 	while (ch->transferred >= ch->blksize) {
@@ -3641,7 +3885,7 @@ uaudio_chan_pintr(struct usbd_xfer *xfer
 		DPRINTFN(5, "call %p(%p)\n", ch->intr, ch->arg);
 		ch->intr(ch->arg);
 	}
-	mutex_exit(&ch->sc->sc_intr_lock);
+	mutex_exit(&sc->sc_intr_lock);
 
 	/* start next transfer */
 	uaudio_chan_ptransfer(ch);
@@ -3650,22 +3894,31 @@ uaudio_chan_pintr(struct usbd_xfer *xfer
 Static void
 uaudio_chan_rtransfer(struct chan *ch)
 {
+	struct uaudio_softc *sc = ch->sc;
 	struct chanbuf *cb;
 	int i, size, residue, total;
 
-	if (ch->sc->sc_dying)
+	if (sc->sc_dying)
 		return;
 
 	/* Pick the next channel buffer. */
 	cb = &ch->chanbufs[ch->curchanbuf];
-	if (++ch->curchanbuf >= UAUDIO_NCHANBUFS)
+	if (++ch->curchanbuf >= ch->nchanbufs)
 		ch->curchanbuf = 0;
 
 	/* Compute the size of each frame in the next transfer. */
 	residue = ch->residue;
 	total = 0;
-	for (i = 0; i < UAUDIO_NFRAMES; i++) {
+	for (i = 0; i < ch->nframes; i++) {
 		size = ch->bytes_per_frame;
+#if 0
+		residue += ch->fraction;
+		if (residue >= sc->sc_usb_frames_per_second) {
+			if ((sc->sc_altflags & UA_NOFRAC) == 0)
+				size += ch->sample_size;
+			residue -= sc->sc_usb_frames_per_second;
+		}
+#endif
 		cb->sizes[i] = size;
 		cb->offsets[i] = total;
 		total += size;
@@ -3676,7 +3929,7 @@ uaudio_chan_rtransfer(struct chan *ch)
 #ifdef UAUDIO_DEBUG
 	if (uaudiodebug > 8) {
 		DPRINTF("buffer=%p, residue=0.%03d\n", cb->buffer, ch->residue);
-		for (i = 0; i < UAUDIO_NFRAMES; i++) {
+		for (i = 0; i < ch->nframes; i++) {
 			DPRINTF("   [%d] length %d\n", i, cb->sizes[i]);
 		}
 	}
@@ -3684,18 +3937,19 @@ uaudio_chan_rtransfer(struct chan *ch)
 
 	DPRINTFN(5, "transfer xfer=%p\n", cb->xfer);
 	/* Fill the request */
-	usbd_setup_isoc_xfer(cb->xfer, cb, cb->sizes, UAUDIO_NFRAMES, 0,
+	usbd_setup_isoc_xfer(cb->xfer, cb, cb->sizes, ch->nframes, 0,
 	    uaudio_chan_rintr);
 
 	usbd_status err = usbd_transfer(cb->xfer);
 	if (err != USBD_IN_PROGRESS && err != USBD_NORMAL_COMPLETION)
-		device_printf(ch->sc->sc_dev, "rtransfer error %d\n", err);
+		device_printf(sc->sc_dev, "rtransfer error %d\n", err);
 }
 
 Static void
 uaudio_chan_rintr(struct usbd_xfer *xfer, void *priv,
 		  usbd_status status)
 {
+	struct uaudio_softc *sc;
 	struct chanbuf *cb;
 	struct chan *ch;
 	uint32_t count;
@@ -3703,12 +3957,13 @@ uaudio_chan_rintr(struct usbd_xfer *xfer
 
 	cb = priv;
 	ch = cb->chan;
+	sc = ch->sc;
 	/* Return if we are aborting. */
 	if (status == USBD_CANCELLED)
 		return;
 
 	if (status != USBD_NORMAL_COMPLETION && status != USBD_SHORT_XFER)
-		device_printf(ch->sc->sc_dev, "rintr error: %s\n",
+		device_printf(sc->sc_dev, "rintr error: %s\n",
 		              usbd_errstr(status));
 
 	usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
@@ -3717,7 +3972,7 @@ uaudio_chan_rintr(struct usbd_xfer *xfer
 	/* count < cb->size is normal for asynchronous source */
 #ifdef DIAGNOSTIC
 	if (count > cb->size) {
-		device_printf(ch->sc->sc_dev,
+		device_printf(sc->sc_dev,
 		    "uaudio_chan_rintr: count(%d) > size(%d) status(%d)\n",
 		    count, cb->size, status);
 	}
@@ -3727,7 +3982,7 @@ uaudio_chan_rintr(struct usbd_xfer *xfer
 	 * Transfer data from channel buffer to upper layer buffer, taking
 	 * care of wrapping the upper layer buffer.
 	 */
-	for (i = 0; i < UAUDIO_NFRAMES; i++) {
+	for (i = 0; i < ch->nframes; i++) {
 		frsize = cb->sizes[i];
 		n = uimin(frsize, ch->end - ch->cur);
 		memcpy(ch->cur, cb->buffer + cb->offsets[i], n);
@@ -3742,37 +3997,54 @@ uaudio_chan_rintr(struct usbd_xfer *xfer
 	}
 
 	/* Call back to upper layer */
-	mutex_enter(&ch->sc->sc_intr_lock);
+	mutex_enter(&sc->sc_intr_lock);
 	ch->transferred += count;
 	while (ch->transferred >= ch->blksize) {
 		ch->transferred -= ch->blksize;
 		DPRINTFN(5, "call %p(%p)\n", ch->intr, ch->arg);
 		ch->intr(ch->arg);
 	}
-	mutex_exit(&ch->sc->sc_intr_lock);
+	mutex_exit(&sc->sc_intr_lock);
 
 	/* start next transfer */
 	uaudio_chan_rtransfer(ch);
 }
 
 Static void
-uaudio_chan_init(struct chan *ch, int altidx, const struct audio_params *param,
-    int maxpktsize)
+uaudio_chan_init(struct chan *ch, int altidx,
+    const struct audio_params *param, int maxpktsize, bool isrecord)
 {
+	struct uaudio_softc *sc = ch->sc;
 	int samples_per_frame, sample_size;
 
+	DPRINTFN(5, "altidx=%d, %d/%d %dch %dHz ufps %u max %d\n",
+		altidx, param->validbits, param->precision, param->channels,
+		param->sample_rate, sc->sc_usb_frames_per_second, maxpktsize);
+
 	ch->altidx = altidx;
 	sample_size = param->precision * param->channels / 8;
-	samples_per_frame = param->sample_rate / USB_FRAMES_PER_SECOND;
+
+	if (isrecord) {
+		if (maxpktsize >= sample_size)
+			samples_per_frame = maxpktsize / sample_size;
+		else
+			samples_per_frame = param->sample_rate / sc->sc_usb_frames_per_second
+			    + param->channels;
+		ch->fraction = 0;
+	} else {
+		samples_per_frame = param->sample_rate / sc->sc_usb_frames_per_second;
+		ch->fraction = param->sample_rate % sc->sc_usb_frames_per_second;
+	}
+
 	ch->sample_size = sample_size;
 	ch->sample_rate = param->sample_rate;
-	if (maxpktsize == 0) {
-		ch->fraction = param->sample_rate % USB_FRAMES_PER_SECOND;
+	ch->bytes_per_frame = samples_per_frame * sample_size;
+
+	if (maxpktsize > 0 && ch->bytes_per_frame > maxpktsize) {
+		samples_per_frame = maxpktsize / sample_size;
 		ch->bytes_per_frame = samples_per_frame * sample_size;
-	} else {
-		ch->fraction = 0;
-		ch->bytes_per_frame = maxpktsize;
 	}
+
 	ch->residue = 0;
 }
 
@@ -3817,14 +4089,15 @@ uaudio_set_format(void *addr, int setmod
 		paltidx = audio_indexof_format(sc->sc_formats, sc->sc_nformats,
 		    AUMODE_PLAY, play);
 		/* Transfer should have halted */
-		uaudio_chan_init(&sc->sc_playchan, paltidx, play, 0);
+		uaudio_chan_init(&sc->sc_playchan, paltidx, play,
+		    UGETW(sc->sc_alts[paltidx].edesc->wMaxPacketSize), false);
 	}
 	if ((setmode & AUMODE_RECORD)) {
 		raltidx = audio_indexof_format(sc->sc_formats, sc->sc_nformats,
 		    AUMODE_RECORD, rec);
 		/* Transfer should have halted */
 		uaudio_chan_init(&sc->sc_recchan, raltidx, rec,
-		    UGETW(sc->sc_alts[raltidx].edesc->wMaxPacketSize));
+		    UGETW(sc->sc_alts[raltidx].edesc->wMaxPacketSize), true);
 	}
 
 	if ((setmode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1) {
@@ -3847,43 +4120,81 @@ uaudio_set_format(void *addr, int setmod
 }
 
 Static usbd_status
-uaudio_set_speed(struct uaudio_softc *sc, int ifnum, int endpt, u_int speed)
+uaudio_speed(struct uaudio_softc *sc, int endpt, int clkid,
+    uint8_t *data, int set)
 {
 	usb_device_request_t req;
-	usbd_status err;
-	uint8_t data[4];
-
-	DPRINTFN(5, "endpt=%d speed=%u\n", endpt, speed);
 
 	switch (sc->sc_version) {
 	case UAUDIO_VERSION1:
-		req.bmRequestType = UT_WRITE_CLASS_ENDPOINT;
-		req.bRequest = SET_CUR;
+		req.bmRequestType = set ?
+			UT_WRITE_CLASS_ENDPOINT
+			: UT_READ_CLASS_ENDPOINT;
+		req.bRequest = set ?
+			SET_CUR
+			: GET_CUR;
 		USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0);
 		USETW(req.wIndex, endpt);
 		USETW(req.wLength, 3);
-		data[0] = speed;
-		data[1] = speed >> 8;
-		data[2] = speed >> 16;
 		break;
 	case UAUDIO_VERSION2:
-		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+		req.bmRequestType = set ?
+			UT_WRITE_CLASS_INTERFACE
+			: UT_READ_CLASS_INTERFACE;
 		req.bRequest = V2_CUR;
 		USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0);
-		USETW(req.wIndex, ifnum);
+		USETW2(req.wIndex, clkid, sc->sc_ac_iface);
 		USETW(req.wLength, 4);
-		data[0] = speed;
-		data[1] = speed >> 8;
-		data[2] = speed >> 16;
-		data[3] = speed >> 24;
 		break;
 	}
 
-	err = usbd_do_request(sc->sc_udev, &req, data);
+	return usbd_do_request(sc->sc_udev, &req, data);
+}
 
-	return err;
+Static usbd_status
+uaudio_set_speed(struct uaudio_softc *sc, int endpt, int clkid, u_int speed)
+{
+	uint8_t data[4];
+
+	DPRINTFN(5, "endpt=%d clkid=%u speed=%u\n", endpt, clkid, speed);
+
+	data[0] = speed;
+	data[1] = speed >> 8;
+	data[2] = speed >> 16;
+	data[3] = speed >> 24;
+
+	return uaudio_speed(sc, endpt, clkid, data, 1);
 }
 
+#ifdef UAUDIO_DEBUG
+SYSCTL_SETUP(sysctl_hw_uaudio_setup, "sysctl hw.uaudio setup")
+{
+        int err;
+        const struct sysctlnode *rnode;
+        const struct sysctlnode *cnode;
+ 
+        err = sysctl_createv(clog, 0, NULL, &rnode,
+            CTLFLAG_PERMANENT, CTLTYPE_NODE, "uaudio",
+            SYSCTL_DESCR("uaudio global controls"),
+            NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
+ 
+        if (err)
+                goto fail;
+ 
+        /* control debugging printfs */
+        err = sysctl_createv(clog, 0, &rnode, &cnode,
+            CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
+            "debug", SYSCTL_DESCR("Enable debugging output"),
+            NULL, 0, &uaudiodebug, sizeof(uaudiodebug), CTL_CREATE, CTL_EOL);
+        if (err)
+                goto fail;
+ 
+        return;
+fail:
+        aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
+} 
+#endif
+
 #ifdef _MODULE
 
 MODULE(MODULE_CLASS_DRIVER, uaudio, NULL);

Index: src/sys/dev/usb/uaudioreg.h
diff -u src/sys/dev/usb/uaudioreg.h:1.18 src/sys/dev/usb/uaudioreg.h:1.19
--- src/sys/dev/usb/uaudioreg.h:1.18	Mon Apr 10 15:14:50 2023
+++ src/sys/dev/usb/uaudioreg.h	Sun Apr 16 19:26:20 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: uaudioreg.h,v 1.18 2023/04/10 15:14:50 mlelstv Exp $	*/
+/*	$NetBSD: uaudioreg.h,v 1.19 2023/04/16 19:26:20 mlelstv Exp $	*/
 
 /*
  * Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -66,6 +66,9 @@ typedef struct {
 	 * allowed to extend the endpoint descriptor.
 	 * Who knows what goes on in the minds of the people in the USB
 	 * standardization?  :-(
+	 *
+	 * UAC2 no longer uses these extra fields. Check bLength to
+	 * find out if these exist.
 	 */
 	uByte		bRefresh;
 	uByte		bSynchAddress;
@@ -305,9 +308,20 @@ struct usb_audio_mixer_v2_unit_1 {
 	uByte		bNrChannels;
 	uDWord		bmChannelConfig;
 	uByte		iChannelNames;
-	uByte		bmControls[255]; /* [bNrChannels] */
+	uByte		bmMixerControls[255]; /* [bNrChannels] */
+	/*uByte		bmControls;*/
 	/*uByte		iMixer;*/
 } UPACKED;
+#define UA_MIX_CLUSTER_MASK	0x03
+#define UA_MIX_CLUSTER_RO	0x01
+#define UA_MIX_CLUSTER_RW	0x03
+/* UAC2 */
+#define UA_MIX_UNDERFLOW_MASK	0x0c
+#define UA_MIX_UNDERFLOW_RO	0x04
+#define UA_MIX_UNDERFLOW_RW	0x0c
+#define UA_MIX_OVERFLOW_MASK	0x30
+#define UA_MIX_OVERFLOW_RO	0x10
+#define UA_MIX_OVERFLOW_RW	0x30
 
 union usb_audio_mixer_unit_1 {
 	struct usb_audio_mixer_v1_unit_1 v1;
@@ -326,7 +340,7 @@ struct usb_audio_selector_unit {
 } UPACKED;
 
 /* UDESCSUB_AC_FEATURE */
-struct usb_audio_feature_unit {
+struct usb_audio_feature_v1_unit {
 	uByte		bLength;
 	uByte		bDescriptorType;
 	uByte		bDescriptorSubtype;
@@ -336,6 +350,20 @@ struct usb_audio_feature_unit {
 	uByte		bmaControls[255]; /* size for more than enough */
 	/* uByte	iFeature; */
 } UPACKED;
+struct usb_audio_feature_v2_unit {
+	uByte		bLength;
+	uByte		bDescriptorType;
+	uByte		bDescriptorSubtype;
+	uByte		bUnitId;
+	uByte		bSourceId;
+	uDWord		bmaControls[255]; /* size for more than enough */
+	/* uByte	iFeature; */
+} UPACKED;
+
+union usb_audio_feature_unit {
+	struct usb_audio_feature_v1_unit v1;
+	struct usb_audio_feature_v2_unit v2;
+};
 
 /* UDESCSUB_AC_PROCESSING */
 struct usb_audio_processing_unit {
@@ -496,11 +524,13 @@ struct usb_audio_clkmult_unit {
 #define SET_MEM 0x05
 #define GET_MEM 0x85
 #define GET_STAT 0xff
-#define V2_CUR        0x01
-#define V2_RANGES     0x02
+
+#define V2_CUR		0x01
+#define V2_RANGES	0x02
 
 #define V2_CUR_CLKFREQ	0x01
 #define V2_CUR_CLKSEL	0x01
+#define V2_CUR_SELECTOR 0x01
 
 
 #define MUTE_CONTROL	0x01
@@ -516,8 +546,12 @@ struct usb_audio_clkmult_unit {
 #define GAIN_CONTROL	0x0b
 #define GAINPAD_CONTROL	0x0c
 #define PHASEINV_CONTROL 0x0d
+/* V2 */
+#define UNDERFLOW_CONTROL 0x0e
+#define OVERFLOW_CONTROL 0x0f
 
 #define FU_MASK(u) (1 << ((u)-1))
+#define V2_FU_MASK(u) (3 << ((u)-1)*2)
 
 #define MASTER_CHAN	0
 

Reply via email to