Subject: kern/26538: Source Selector for USB Audio (uaudio.c)
To: None <gnats-bugs@gnats.NetBSD.org>
From: None <kazuhito@ph.noda.tus.ac.jp>
List: netbsd-bugs
Date: 08/03/2004 16:19:58
>Number:         26538
>Category:       kern
>Synopsis:       Source Selector for USB Audio (uaudio.c)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Tue Aug 03 18:45:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:     Kazuhito HONDA
>Release:        NetBSD 2.0G
>Organization:
>Environment:
NetBSD kaoru 2.0G NetBSD 2.0G #250: Sat Jul 31 04:00:15 JST 2004  root@kaoru:/sys/arch/i386/compile/KAORU.2.0G.1 i386
>Description:
This is a suggestion for a improvement.

I have USB audio device, Sound Blaster Digital Music (SBDM).
This has a source selector for recording.
But NetBSD don't have its controller. 
So I added codes to uaudio.c in order to control it.
Because I roughly referred to other codes of audio devices only,
I can't recognize that the codes are strictly correct.
But I haven't had any problem.

For example, using SBDM, mixerctl -av wrote:
outputs.fea8-i10i11i12=1  [ 1 2 3 ]

This is a variable for selector control.
In this case, users can select one source from feature 10, 11 and 12.
Values of feature 10 11 and 12 are  1, 2 and 3 respectively.

The codes don't use the feature numbers for variable value,
because it may be difficult
and the feature numbers must be indirect 
and senseless for general users, too.

>How-To-Repeat:

>Fix:
--- uaudio.c.orig	2004-07-17 07:39:23.000000000 +0900
+++ uaudio.c	2004-08-04 02:48:15.000000000 +0900
@@ -97,6 +97,7 @@
 #define MIX_SIGNED_16	2
 #define MIX_UNSIGNED_16	3
 #define MIX_SIGNED_8	4
+#define MIX_SELECTOR	5
 #define MIX_SIZE(n) ((n) == MIX_SIGNED_16 || (n) == MIX_UNSIGNED_16 ? 2 : 1)
 #define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16)
 	int		minval, maxval;
@@ -579,7 +580,12 @@
 	sc->sc_ctls = nmc;
 
 	mc->delta = 0;
-	if (mc->type != MIX_ON_OFF) {
+	if (mc->type == MIX_ON_OFF) {
+		mc->minval = 0;
+		mc->maxval = 1;
+	} else if (mc->type == MIX_SELECTOR) {
+		;
+	} else {
 		/* Determine min and max values. */
 		mc->minval = uaudio_signext(mc->type,
 			uaudio_get(sc, GET_MIN, UT_READ_CLASS_INTERFACE,
@@ -597,9 +603,6 @@
 				 MIX_SIZE(mc->type));
 		if (res > 0)
 			mc->delta = (res * 255 + mc->mul/2) / mc->mul;
-	} else {
-		mc->minval = 0;
-		mc->maxval = 1;
 	}
 
 	sc->sc_ctls[sc->sc_nctls++] = *mc;
@@ -800,14 +803,34 @@
 uaudio_add_selector(struct uaudio_softc *sc, usb_descriptor_t *v,
 		    usb_descriptor_t **dps)
 {
-#ifdef UAUDIO_DEBUG
 	struct usb_audio_selector_unit *d =
 		(struct usb_audio_selector_unit *)v;
+	int i, srcId;
+	struct mixerctl mix;
+	char ctlname[MAX_AUDIO_DEV_LEN];
 
+#ifdef UAUDIO_DEBUG
 	DPRINTFN(2,("uaudio_add_selector: bUnitId=%d bNrInPins=%d\n",
 		    d->bUnitId, d->bNrInPins));
 #endif
-	printf("uaudio_add_selector: NOT IMPLEMENTED\n");
+	mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
+	mix.wValue[0] = MAKE(0, 0);
+	mix.class = UAC_OUTPUT;
+	mix.nchan = 1;
+	mix.type = MIX_SELECTOR;
+	mix.ctlunit = "";
+	mix.minval = 1;
+	mix.maxval = d->bNrInPins;
+	mix.mul = mix.maxval - mix.minval;
+	snprintf(mix.ctlname, MAX_AUDIO_DEV_LEN, "fea%d-", d->bUnitId);
+	for (i = 1; i <= d->bNrInPins; i++) {
+		srcId = d->baSourceId[i - 1];
+		snprintf(ctlname, MAX_AUDIO_DEV_LEN, "i%d", srcId);
+		if (strlen(mix.ctlname) + strlen(ctlname) > MAX_AUDIO_DEV_LEN - 1)
+			break;
+		strcat(mix.ctlname, ctlname);
+	}
+	uaudio_mixer_add_ctl(sc, &mix);
 }
 
 void
@@ -1386,7 +1409,7 @@
 {
 	struct uaudio_softc *sc = addr;
 	struct mixerctl *mc;
-	int n, nctls;
+	int n, nctls, i;
 
 	DPRINTFN(2,("uaudio_query_devinfo: index=%d\n", mi->index));
 	if (sc->sc_dying)
@@ -1438,6 +1461,14 @@
 		    sizeof(mi->un.e.member[1].label.name));
 		mi->un.e.member[1].ord = 1;
 		break;
+	case MIX_SELECTOR:
+		mi->type = AUDIO_MIXER_ENUM;
+		mi->un.e.num_mem = mc->maxval - mc->minval + 1;
+		for (i = 0; i <= mc->maxval - mc->minval; i++) {
+			snprintf(mi->un.e.member[i].label.name, sizeof(mi->un.e.member[i].label.name),"%d", i + mc->minval);
+			mi->un.e.member[i].ord = i + mc->minval;
+		}
+		break;
 	default:
 		mi->type = AUDIO_MIXER_VALUE;
 		strncpy(mi->un.v.units.name, mc->ctlunit, MAX_AUDIO_DEV_LEN);
@@ -1667,7 +1698,10 @@
 		     mc->type, val, mc->minval, mc->maxval));
 	if (mc->type == MIX_ON_OFF)
 		val = (val != 0);
-	else
+	else if (mc->type == MIX_SELECTOR) {
+		if (val < mc->minval || val > mc->maxval)
+			val = mc->minval;
+	} else
 		val = ((uaudio_signext(mc->type, val) - mc->minval) * 255
 			+ mc->mul/2) / mc->mul;
 	DPRINTFN(5, ("val'=%d\n", val));
@@ -1681,7 +1715,10 @@
 		    mc->type, val, mc->minval, mc->maxval));
 	if (mc->type == MIX_ON_OFF)
 		val = (val != 0);
-	else
+	else if (mc->type == MIX_SELECTOR) {
+		if (val < mc->minval || val > mc->maxval)
+			val = mc->minval;
+	} else
 		val = (val + mc->delta/2) * mc->mul / 255 + mc->minval;
 	DPRINTFN(5, ("val'=%d\n", val));
 	return (val);
@@ -1729,6 +1766,10 @@
 		if (cp->type != AUDIO_MIXER_ENUM)
 			return (EINVAL);
 		cp->un.ord = uaudio_ctl_get(sc, GET_CUR, 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);
 	} else {
 		if (cp->type != AUDIO_MIXER_VALUE)
 			return (EINVAL);
@@ -1769,6 +1810,10 @@
 		if (cp->type != AUDIO_MIXER_ENUM)
 			return (EINVAL);
 		uaudio_ctl_set(sc, SET_CUR, 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);
 	} else {
 		if (cp->type != AUDIO_MIXER_VALUE)
 			return (EINVAL);

>Release-Note:
>Audit-Trail:
>Unformatted: