Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 08 Nov 2005 05:04:31 +0900 (JST)
From:      Kazuhito HONDA <kazuhito@ph.noda.tus.ac.jp>
To:        julian@elischer.org
Cc:        freebsd-multimedia@freebsd.org, Alexander@Leidinger.net, skywizard@MyBSD.org.my
Subject:   Re: uaudio and Digigram UAX220
Message-ID:  <20051108.050431.343192595.kazuhito@ph.noda.tus.ac.jp>
In-Reply-To: <436F8EFE.9090704@elischer.org>
References:  <43691146.7030404@elischer.org> <20051103034216.379caa05.skywizard@MyBSD.org.my> <436F8EFE.9090704@elischer.org>

next in thread | previous in thread | raw e-mail | index | archive | help
----Next_Part(Tue_Nov__8_05:04:31_2005_613)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Sorry, I forgot to attach a patch for uaudio.h.
I'll attach a new patch including 
changes of uaudio.h and fixes of some typos.

From: Julian Elischer <julian@elischer.org>
Subject: Re: uaudio and Digigram UAX220
Date: Mon, 07 Nov 2005 09:29:34 -0800

>  Does it apply to 6?

`patched_6_patch' was made on FreeBSD 6.0 which a sound patch was applied.
(http://people.freebsd.org/~ariff/snd_RELENG_6_0_20051030_058.diff)
I checked that this patch was available on patched FreeBSD 6-stable
(http://people.freebsd.org/~ariff/snd_RELENG_6_20051030_058.diff).

`normal_6_patch' was made from FreeBSD 6 without the sound patch.
I don't test it, but I'm sure that it is available.

sincerely yours,
Kazuhito HONDA

----Next_Part(Tue_Nov__8_05:04:31_2005_613)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="patched_6_patch"

--- uaudio.c.old	Wed Nov  2 15:31:54 2005
+++ uaudio.c	Tue Nov  8 03:18:06 2005
@@ -239,6 +239,7 @@ struct uaudio_softc {
 #define HAS_MULAW	0x10
 #define UA_NOFRAC	0x20		/* don't do sample rate adjustment */
 #define HAS_24		0x40
+#define HAS_32		0x80
 	int		sc_mode;	/* play/record capability */
 	struct mixerctl *sc_ctls;	/* mixer controls */
 	int		sc_nctls;	/* # of mixer controls */
@@ -2050,7 +2051,7 @@ uaudio_process_as(struct uaudio_softc *s
 	format = UGETW(asid->wFormatTag);
 	chan = asf1d->bNrChannels;
 	prec = asf1d->bBitResolution;
-	if (prec != 8 && prec != 16 && prec != 24) {
+	if (prec != 8 && prec != 16 && prec != 24 && prec != 32) {
 		printf("%s: ignored setting with precision %d\n",
 		       USBDEVNAME(sc->sc_dev), prec);
 		return USBD_NORMAL_COMPLETION;
@@ -2063,6 +2064,8 @@ uaudio_process_as(struct uaudio_softc *s
 			sc->sc_altflags |= HAS_16;
 		} else if (prec == 24) {
 			sc->sc_altflags |= HAS_24;
+		} else if (prec == 32) {
+			sc->sc_altflags |= HAS_32;
 		}
 		enc = AUDIO_ENCODING_SLINEAR_LE;
 		format_str = "pcm";
@@ -3742,7 +3745,7 @@ uaudio_init_params(struct uaudio_softc *
 	if ((sc->sc_playchan.pipe != NULL) || (sc->sc_recchan.pipe != NULL))
 		return (-1);
 
-	switch(ch->format & 0x0000FFFF) {
+	switch(ch->format & 0x000FFFFF) {
 	case AFMT_U8:
 		enc = AUDIO_ENCODING_ULINEAR_LE;
 		ch->precision = 8;
@@ -3775,6 +3778,38 @@ uaudio_init_params(struct uaudio_softc *
 		enc = AUDIO_ENCODING_ULINEAR_BE;
 		ch->precision = 16;
 		break;
+	case AFMT_S24_LE:
+		enc = AUDIO_ENCODING_SLINEAR_LE;
+		ch->precision = 24;
+		break;
+	case AFMT_S24_BE:
+		enc = AUDIO_ENCODING_SLINEAR_BE;
+		ch->precision = 24;
+		break;
+	case AFMT_U24_LE:
+		enc = AUDIO_ENCODING_ULINEAR_LE;
+		ch->precision = 24;
+		break;
+	case AFMT_U24_BE:
+		enc = AUDIO_ENCODING_ULINEAR_BE;
+		ch->precision = 24;
+		break;
+	case AFMT_S32_LE:
+		enc = AUDIO_ENCODING_SLINEAR_LE;
+		ch->precision = 32;
+		break;
+	case AFMT_S32_BE:
+		enc = AUDIO_ENCODING_SLINEAR_BE;
+		ch->precision = 32;
+		break;
+	case AFMT_U32_LE:
+		enc = AUDIO_ENCODING_ULINEAR_LE;
+		ch->precision = 32;
+		break;
+	case AFMT_U32_BE:
+		enc = AUDIO_ENCODING_ULINEAR_BE;
+		ch->precision = 32;
+		break;
 	default:
 		enc = 0;
 		ch->precision = 16;
@@ -3857,83 +3892,135 @@ uaudio_init_params(struct uaudio_softc *
 	return (0);
 }
 
-void
-uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt)
+struct uaudio_conversion {
+	uint8_t uaudio_fmt;
+	uint8_t uaudio_prec;
+	uint32_t freebsd_fmt;
+};
+
+const struct uaudio_conversion const accepted_conversion[] = {
+	{AUDIO_ENCODING_ULINEAR_LE, 8, AFMT_U8},
+	{AUDIO_ENCODING_ULINEAR_LE, 16, AFMT_U16_LE},
+	{AUDIO_ENCODING_ULINEAR_LE, 24, AFMT_U24_LE},
+	{AUDIO_ENCODING_ULINEAR_LE, 32, AFMT_U32_LE},
+	{AUDIO_ENCODING_ULINEAR_BE, 16, AFMT_U16_BE},
+	{AUDIO_ENCODING_ULINEAR_BE, 24, AFMT_U24_BE},
+	{AUDIO_ENCODING_ULINEAR_BE, 32, AFMT_U32_BE},
+	{AUDIO_ENCODING_SLINEAR_LE, 8, AFMT_S8},
+	{AUDIO_ENCODING_SLINEAR_LE, 16, AFMT_S16_LE},
+	{AUDIO_ENCODING_SLINEAR_LE, 24, AFMT_S24_LE},
+	{AUDIO_ENCODING_SLINEAR_LE, 24, AFMT_S32_LE},
+	{AUDIO_ENCODING_SLINEAR_BE, 16, AFMT_S16_BE},
+	{AUDIO_ENCODING_SLINEAR_BE, 24, AFMT_S24_BE},
+	{AUDIO_ENCODING_SLINEAR_BE, 24, AFMT_S32_BE},
+	{AUDIO_ENCODING_ALAW, 8, AFMT_A_LAW},
+	{AUDIO_ENCODING_ULAW, 8, AFMT_MU_LAW},
+	{0,0,0}
+};
+
+unsigned
+uaudio_query_formats(device_t dev, int reqdir, unsigned maxfmt, struct pcmchan_caps *cap)
 {
-	int i, pn=0, rn=0;
-	int prec, dir;
-	u_int32_t fmt;
 	struct uaudio_softc *sc;
-
-	const struct usb_audio_streaming_type1_descriptor *a1d;
+	const struct usb_audio_streaming_type1_descriptor *asf1d;
+	const struct uaudio_conversion *iterator;
+	unsigned fmtcount, foundcount;
+	u_int32_t fmt;
+	uint8_t format, numchan, subframesize, prec, dir, iscontinuous;
+	int freq, freq_min, freq_max;
+	char *numchannel_descr;
+	char freq_descr[64];
+	int i,r;
 
 	sc = device_get_softc(dev);
+	if (sc == NULL)
+		return 0;
+
+	cap->minspeed = cap->maxspeed = 0;
+	foundcount = fmtcount = 0;
 
 	for (i = 0; i < sc->sc_nalts; i++) {
-		fmt = 0;
-		a1d = sc->sc_alts[i].asf1desc;
-		prec = a1d->bBitResolution;	/* precision */
+		dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
 
-		switch (sc->sc_alts[i].encoding) {
-		case AUDIO_ENCODING_ULINEAR_LE:
-			if (prec == 8) {
-				fmt = AFMT_U8;
-			} else if (prec == 16) {
-				fmt = AFMT_U16_LE;
-			}
-			break;
-		case AUDIO_ENCODING_SLINEAR_LE:
-			if (prec == 8) {
-				fmt = AFMT_S8;
-			} else if (prec == 16) {
-				fmt = AFMT_S16_LE;
-			}
-			break;
-		case AUDIO_ENCODING_ULINEAR_BE:
-			if (prec == 16) {
-				fmt = AFMT_U16_BE;
-			}
-			break;
-		case AUDIO_ENCODING_SLINEAR_BE:
-			if (prec == 16) {
-				fmt = AFMT_S16_BE;
-			}
-			break;
-		case AUDIO_ENCODING_ALAW:
-			if (prec == 8) {
-				fmt = AFMT_A_LAW;
-			}
-			break;
-		case AUDIO_ENCODING_ULAW:
-			if (prec == 8) {
-				fmt = AFMT_MU_LAW;
-			}
-			break;
-		}
+		if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY))
+			continue;
 
-		if (fmt != 0) {
-			if (a1d->bNrChannels == 2) {	/* stereo/mono */
-				fmt |= AFMT_STEREO;
-			} else if (a1d->bNrChannels != 1) {
-				fmt = 0;
-			}
+		asf1d = sc->sc_alts[i].asf1desc;
+		format = sc->sc_alts[i].encoding;
+
+		numchan = asf1d->bNrChannels;
+		subframesize = asf1d->bSubFrameSize;
+		prec = asf1d->bBitResolution;	/* precision */
+		iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS;
+
+		if (iscontinuous)
+			snprintf(freq_descr, sizeof(freq_descr), "continous min %d max %d", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+		else
+			snprintf(freq_descr, sizeof(freq_descr), "fixed frequency (%d listed formats)", asf1d->bSamFreqType);
+
+		if (numchan == 1)
+			numchannel_descr = " (mono)";
+		else if (numchan == 2)
+			numchannel_descr = " (stereo)";
+		else
+			numchannel_descr = "";
+
+		if (bootverbose) {
+			device_printf(dev, "uaudio_query_formats: found a native %s channel%s %s %dbit %dbytes/subframe X %d channels = %d bytes per sample\n",
+					(dir==UE_DIR_OUT)?"playback":"record",
+					numchannel_descr, freq_descr,
+					prec, subframesize, numchan, subframesize*numchan);
 		}
+		/*
+		 * Now start rejecting the ones that don't map to FreeBSD
+		 */
 
-		if (fmt != 0) {
-			dir= UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
-			if (dir == UE_DIR_OUT) {
-				pfmt[pn++] = fmt;
-			} else if (dir == UE_DIR_IN) {
-				rfmt[rn++] = fmt;
+		if (numchan != 1 && numchan != 2)
+			continue;
+
+		for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++)
+			if (iterator->uaudio_fmt == format && iterator->uaudio_prec == prec)
+				break;
+
+		if (iterator->uaudio_fmt == 0)
+			continue;
+
+		fmt = iterator->freebsd_fmt;
+
+		if (numchan == 2)
+			fmt |= AFMT_STEREO;
+
+		foundcount++;
+
+		if (fmtcount >= maxfmt)
+			continue;
+
+		cap->fmtlist[fmtcount++] = fmt;
+
+		if (iscontinuous) {
+			freq_min = UA_SAMP_LO(asf1d);
+			freq_max = UA_SAMP_HI(asf1d);
+
+			if (cap->minspeed == 0 || freq_min < cap->minspeed)
+				cap->minspeed = freq_min;
+			if (cap->maxspeed == 0)
+				cap->maxspeed = cap->minspeed;
+			if (freq_max > cap->maxspeed)
+				cap->maxspeed = freq_max;
+		} else {
+			for (r = 0; r < asf1d->bSamFreqType; r++) {
+				freq = UA_GETSAMP(asf1d, r);
+				if (cap->minspeed == 0 || freq < cap->minspeed)
+					cap->minspeed = freq;
+				if (cap->maxspeed == 0)
+					cap->maxspeed = cap->minspeed;
+				if (freq > cap->maxspeed)
+					cap->maxspeed = freq;
 			}
 		}
-
-		if ((pn > 8*2) || (rn > 8*2))
-			break;
 	}
-	pfmt[pn] = 0;
-	rfmt[rn] = 0;
-	return;
+	cap->fmtlist[fmtcount] = 0;
+	return foundcount;
 }
 
 void
@@ -3982,25 +4069,81 @@ uaudio_chan_set_param_blocksize(device_t
 	return;
 }
 
-void
-uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int dir)
+int
+uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir)
 {
+	const struct uaudio_conversion *iterator;
 	struct uaudio_softc *sc;
 	struct chan *ch;
+	int i, r, score, hiscore, bestspeed;
 
 	sc = device_get_softc(dev);
 #ifndef NO_RECORDING
-	if (dir == PCMDIR_PLAY)
+	if (reqdir == PCMDIR_PLAY)
 		ch = &sc->sc_playchan;
 	else
 		ch = &sc->sc_recchan;
 #else
 	ch = &sc->sc_playchan;
 #endif
+	/*
+	 * We are successful if we find an endpoint that matches our selected format and it
+	 * supports the requested speed.
+	 */
+	hiscore = 0;
+	bestspeed = 1;
+	for (i = 0; i < sc->sc_nalts; i++) {
+		int dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
+		int format = sc->sc_alts[i].encoding;
+		const struct usb_audio_streaming_type1_descriptor *asf1d = sc->sc_alts[i].asf1desc;
+		int iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS;
 
-	ch->sample_rate = speed;
+		if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY))
+			continue;
 
-	return;
+		for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++)
+			if (iterator->uaudio_fmt != format || iterator->freebsd_fmt != (ch->format&0xfffffff))
+				continue;
+			if (iscontinuous) {
+				if (speed >= UA_SAMP_LO(asf1d) && speed <= UA_SAMP_HI(asf1d)) {
+					ch->sample_rate = speed;
+					return speed;
+				} else if (speed < UA_SAMP_LO(asf1d)) {
+					score = 0xfff * speed / UA_SAMP_LO(asf1d);
+					if (score > hiscore) {
+						bestspeed = UA_SAMP_LO(asf1d);
+						hiscore = score;
+					}
+				} else if (speed < UA_SAMP_HI(asf1d)) {
+					score = 0xfff * UA_SAMP_HI(asf1d) / speed;
+					if (score > hiscore) {
+						bestspeed = UA_SAMP_HI(asf1d);
+						hiscore = score;
+					}
+				}
+				continue;
+			}
+			for (r = 0; r < asf1d->bSamFreqType; r++) {
+				if (speed == UA_GETSAMP(asf1d, r)) {
+					ch->sample_rate = speed;
+					return speed;
+				}
+				if (speed > UA_GETSAMP(asf1d, r))
+					score = 0xfff * UA_GETSAMP(asf1d, r) / speed;
+				else
+					score = 0xfff * speed / UA_GETSAMP(asf1d, r);
+				if (score > hiscore) { 
+					bestspeed = UA_GETSAMP(asf1d, r);
+					hiscore = score;
+				}
+			}
+	}
+	if (bestspeed != 1) {
+		ch->sample_rate = bestspeed;
+		return bestspeed;
+	}
+
+	return 0;
 }
 
 int
--- uaudio.h.old	Thu Apr 28 02:16:27 2005
+++ uaudio.h	Tue Nov  8 03:18:51 2005
@@ -41,7 +41,7 @@ int	uaudio_halt_in_dma(device_t dev);
 #endif
 void	uaudio_chan_set_param(device_t, u_char *, u_char *);
 void	uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir);
-void	uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int dir);
+int	uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir);
 void	uaudio_chan_set_param_format(device_t dev, u_int32_t format,int dir);
 int	uaudio_chan_getptr(device_t dev, int);
 void	uaudio_mixer_set(device_t dev, unsigned type, unsigned left,
@@ -49,5 +49,5 @@ void	uaudio_mixer_set(device_t dev, unsi
 u_int32_t uaudio_mixer_setrecsrc(device_t dev, u_int32_t src);
 u_int32_t uaudio_query_mix_info(device_t dev);
 u_int32_t uaudio_query_recsrc_info(device_t dev);
-void	uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt);
+unsigned uaudio_query_formats(device_t dev, int dir, unsigned maxfmt, struct pcmchan_caps *fmt);
 void	uaudio_sndstat_register(device_t dev);
--- uaudio_pcm.c.old	Wed Nov  2 15:31:54 2005
+++ uaudio_pcm.c	Tue Nov  8 03:18:17 2005
@@ -49,16 +49,13 @@ struct ua_info {
 	device_t sc_dev;
 	u_int32_t bufsz;
 	struct ua_chinfo pch, rch;
+#define FORMAT_NUM	32
+	u_int32_t ua_playfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */
+	u_int32_t ua_recfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */
+	struct pcmchan_caps ua_playcaps;
+	struct pcmchan_caps ua_reccaps;
 };
 
-static u_int32_t ua_playfmt[8*2+1]; /* 8 format * (stereo or mono) + endptr */
-
-static struct pcmchan_caps ua_playcaps = {8000, 48000, ua_playfmt, 0};
-
-static u_int32_t ua_recfmt[8*2+1]; /* 8 format * (stereo or mono) + endptr */
-
-static struct pcmchan_caps ua_reccaps = {8000, 48000, ua_recfmt, 0};
-
 #define UAUDIO_DEFAULT_BUFSZ		16*1024
 
 /************************************************************/
@@ -76,23 +73,9 @@ ua_chan_init(kobj_t obj, void *devinfo, 
 	ch->dir = dir;
 
 	pa_dev = device_get_parent(sc->sc_dev);
-     	/* Create ua_playfmt[] & ua_recfmt[] */
-	uaudio_query_formats(pa_dev, (u_int32_t *)&ua_playfmt, (u_int32_t *)&ua_recfmt);
-	if (dir == PCMDIR_PLAY) {
-		if (ua_playfmt[0] == 0) {
-			printf("play channel supported format list invalid\n");
-			return NULL;
-		}
-	} else {
-		if (ua_recfmt[0] == 0) {
-			printf("record channel supported format list invalid\n");
-			return NULL;
-		}
-
-	}
 
 	ch->buf = malloc(sc->bufsz, M_DEVBUF, M_NOWAIT);
-        if (ch->buf == NULL)
+	if (ch->buf == NULL)
 		return NULL;
 	if (sndbuf_setup(b, ch->buf, sc->bufsz) != 0) {
 		free(ch->buf, M_DEVBUF);
@@ -133,6 +116,9 @@ ua_chan_setformat(kobj_t obj, void *data
 
 	struct ua_chinfo *ch = data;
 
+	/*
+	 * At this point, no need to query as we shouldn't select an unsorted format
+	 */
 	ua = ch->parent;
 	pa_dev = device_get_parent(ua->sc_dev);
 	uaudio_chan_set_param_format(pa_dev, format, ch->dir);
@@ -144,15 +130,15 @@ ua_chan_setformat(kobj_t obj, void *data
 static int
 ua_chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
 {
+	struct ua_chinfo *ch;
 	device_t pa_dev;
-	struct ua_info *ua;
+	int bestspeed;
 
-	struct ua_chinfo *ch = data;
-	ch->spd = speed;
+	ch = data;
+	pa_dev = device_get_parent(ch->parent->sc_dev);
 
-	ua = ch->parent;
-	pa_dev = device_get_parent(ua->sc_dev);
-	uaudio_chan_set_param_speed(pa_dev, speed, ch->dir);
+	if ((bestspeed = uaudio_chan_set_param_speed(pa_dev, speed, ch->dir)))
+		ch->spd = bestspeed;
 
 	return ch->spd;
 }
@@ -224,9 +210,10 @@ ua_chan_getptr(kobj_t obj, void *data)
 static struct pcmchan_caps *
 ua_chan_getcaps(kobj_t obj, void *data)
 {
-	struct ua_chinfo *ch = data;
+	struct ua_chinfo *ch;
 
-	return (ch->dir == PCMDIR_PLAY) ? &ua_playcaps : & ua_reccaps;
+	ch = data;
+	return (ch->dir == PCMDIR_PLAY) ? &(ch->parent->ua_playcaps) : &(ch->parent->ua_reccaps);
 }
 
 static kobj_method_t ua_chan_methods[] = {
@@ -327,42 +314,63 @@ ua_attach(device_t dev)
 {
 	struct ua_info *ua;
 	char status[SND_STATUSLEN];
+	device_t pa_dev;
+	u_int32_t nplay, nrec;
+	int i;
 
-	ua = (struct ua_info *)malloc(sizeof *ua, M_DEVBUF, M_NOWAIT);
-	if (!ua)
+	ua = (struct ua_info *)malloc(sizeof *ua, M_DEVBUF, M_ZERO | M_NOWAIT);
+	if (ua == NULL)
 		return ENXIO;
-	bzero(ua, sizeof *ua);
 
 	ua->sc_dev = dev;
 
+	pa_dev = device_get_parent(dev);
+
 	ua->bufsz = pcm_getbuffersize(dev, 4096, UAUDIO_DEFAULT_BUFSZ, 65536);
 	if (bootverbose)
 		device_printf(dev, "using a default buffer size of %jd\n", (intmax_t)ua->bufsz);
 
 	if (mixer_init(dev, &ua_mixer_class, ua)) {
-		return(ENXIO);
+		goto bad;
 	}
 
 	snprintf(status, SND_STATUSLEN, "at ? %s", PCM_KLDSTRING(snd_uaudio));
 
+	ua->ua_playcaps.fmtlist = ua->ua_playfmt;
+	ua->ua_reccaps.fmtlist = ua->ua_recfmt;
+	nplay = uaudio_query_formats(pa_dev, PCMDIR_PLAY, FORMAT_NUM * 2, &ua->ua_playcaps);
+	nrec = uaudio_query_formats(pa_dev, PCMDIR_REC, FORMAT_NUM * 2, &ua->ua_reccaps);
+
+	if (nplay > 1)
+		nplay = 1;
+	if (nrec > 1)
+		nrec = 1;
+
 #ifndef NO_RECORDING
-	if (pcm_register(dev, ua, 1, 1)) {
+	if (pcm_register(dev, ua, nplay, nrec)) {
 #else
-	if (pcm_register(dev, ua, 1, 0)) {
+	if (pcm_register(dev, ua, nplay, 0)) {
 #endif
-		return(ENXIO);
+		goto bad;
 	}
 
 	sndstat_unregister(dev);
 	uaudio_sndstat_register(dev);
 
-	pcm_addchan(dev, PCMDIR_PLAY, &ua_chan_class, ua);
+	for (i = 0; i < nplay; i++) {
+		pcm_addchan(dev, PCMDIR_PLAY, &ua_chan_class, ua);
+	}
 #ifndef NO_RECORDING
-	pcm_addchan(dev, PCMDIR_REC, &ua_chan_class, ua);
+	for (i = 0; i < nrec; i++) {
+		pcm_addchan(dev, PCMDIR_REC, &ua_chan_class, ua);
+	}
 #endif
 	pcm_setstatus(dev, status);
 
 	return 0;
+
+bad:	free(ua, M_DEVBUF);
+	return ENXIO;
 }
 
 static int

----Next_Part(Tue_Nov__8_05:04:31_2005_613)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="normal_6_patch"

--- uaudio.c.orig	Tue Nov  8 04:17:47 2005
+++ uaudio.c	Tue Nov  8 04:50:46 2005
@@ -231,6 +231,7 @@ struct uaudio_softc {
 #define HAS_MULAW	0x10
 #define UA_NOFRAC	0x20		/* don't do sample rate adjustment */
 #define HAS_24		0x40
+#define HAS_32		0x80
 	int		sc_mode;	/* play/record capability */
 	struct mixerctl *sc_ctls;	/* mixer controls */
 	int		sc_nctls;	/* # of mixer controls */
@@ -2027,7 +2028,7 @@ uaudio_process_as(struct uaudio_softc *s
 	format = UGETW(asid->wFormatTag);
 	chan = asf1d->bNrChannels;
 	prec = asf1d->bBitResolution;
-	if (prec != 8 && prec != 16 && prec != 24) {
+	if (prec != 8 && prec != 16 && prec != 24 && prec != 32) {
 		printf("%s: ignored setting with precision %d\n",
 		       USBDEVNAME(sc->sc_dev), prec);
 		return (USBD_NORMAL_COMPLETION);
@@ -2040,6 +2041,8 @@ uaudio_process_as(struct uaudio_softc *s
 			sc->sc_altflags |= HAS_16;
 		} else if (prec == 24) {
 			sc->sc_altflags |= HAS_24;
+		} else if (prec == 32) {
+			sc->sc_altflags |= HAS_32;
 		}
 		enc = AUDIO_ENCODING_SLINEAR_LE;
 		format_str = "pcm";
@@ -3690,7 +3693,7 @@ uaudio_init_params(struct uaudio_softc *
 	if ((sc->sc_playchan.pipe != NULL) || (sc->sc_recchan.pipe != NULL))
 		return (-1);
 
-	switch(ch->format & 0x0000FFFF) {
+	switch(ch->format & 0x000FFFFF) {
 	case AFMT_U8:
 		enc = AUDIO_ENCODING_ULINEAR_LE;
 		ch->precision = 8;
@@ -3723,6 +3726,38 @@ uaudio_init_params(struct uaudio_softc *
 		enc = AUDIO_ENCODING_ULINEAR_BE;
 		ch->precision = 16;
 		break;
+	case AFMT_S24_LE:
+		enc = AUDIO_ENCODING_SLINEAR_LE;
+		ch->precision = 24;
+		break;
+	case AFMT_S24_BE:
+		enc = AUDIO_ENCODING_SLINEAR_BE;
+		ch->precision = 24;
+		break;
+	case AFMT_U24_LE:
+		enc = AUDIO_ENCODING_ULINEAR_LE;
+		ch->precision = 24;
+		break;
+	case AFMT_U24_BE:
+		enc = AUDIO_ENCODING_ULINEAR_BE;
+		ch->precision = 24;
+		break;
+	case AFMT_S32_LE:
+		enc = AUDIO_ENCODING_SLINEAR_LE;
+		ch->precision = 32;
+		break;
+	case AFMT_S32_BE:
+		enc = AUDIO_ENCODING_SLINEAR_BE;
+		ch->precision = 32;
+		break;
+	case AFMT_U32_LE:
+		enc = AUDIO_ENCODING_ULINEAR_LE;
+		ch->precision = 32;
+		break;
+	case AFMT_U32_BE:
+		enc = AUDIO_ENCODING_ULINEAR_BE;
+		ch->precision = 32;
+		break;
 	default:
 		enc = 0;
 		ch->precision = 16;
@@ -3805,83 +3840,135 @@ uaudio_init_params(struct uaudio_softc *
 	return (0);
 }
 
-void
-uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt)
+struct uaudio_conversion {
+	uint8_t uaudio_fmt;
+	uint8_t uaudio_prec;
+	uint32_t freebsd_fmt;
+};
+
+const struct uaudio_conversion const accepted_conversion[] = {
+	{AUDIO_ENCODING_ULINEAR_LE, 8, AFMT_U8},
+	{AUDIO_ENCODING_ULINEAR_LE, 16, AFMT_U16_LE},
+	{AUDIO_ENCODING_ULINEAR_LE, 24, AFMT_U24_LE},
+	{AUDIO_ENCODING_ULINEAR_LE, 32, AFMT_U32_LE},
+	{AUDIO_ENCODING_ULINEAR_BE, 16, AFMT_U16_BE},
+	{AUDIO_ENCODING_ULINEAR_BE, 24, AFMT_U24_BE},
+	{AUDIO_ENCODING_ULINEAR_BE, 32, AFMT_U32_BE},
+	{AUDIO_ENCODING_SLINEAR_LE, 8, AFMT_S8},
+	{AUDIO_ENCODING_SLINEAR_LE, 16, AFMT_S16_LE},
+	{AUDIO_ENCODING_SLINEAR_LE, 24, AFMT_S24_LE},
+	{AUDIO_ENCODING_SLINEAR_LE, 24, AFMT_S32_LE},
+	{AUDIO_ENCODING_SLINEAR_BE, 16, AFMT_S16_BE},
+	{AUDIO_ENCODING_SLINEAR_BE, 24, AFMT_S24_BE},
+	{AUDIO_ENCODING_SLINEAR_BE, 24, AFMT_S32_BE},
+	{AUDIO_ENCODING_ALAW, 8, AFMT_A_LAW},
+	{AUDIO_ENCODING_ULAW, 8, AFMT_MU_LAW},
+	{0,0,0}
+};
+
+unsigned
+uaudio_query_formats(device_t dev, int reqdir, unsigned maxfmt, struct pcmchan_caps *cap)
 {
-	int i, pn=0, rn=0;
-	int prec, dir;
-	u_int32_t fmt;
 	struct uaudio_softc *sc;
-
-	const struct usb_audio_streaming_type1_descriptor *a1d;
+	const struct usb_audio_streaming_type1_descriptor *asf1d;
+	const struct uaudio_conversion *iterator;
+	unsigned fmtcount, foundcount;
+	u_int32_t fmt;
+	uint8_t format, numchan, subframesize, prec, dir, iscontinuous;
+	int freq, freq_min, freq_max;
+	char *numchannel_descr;
+	char freq_descr[64];
+	int i,r;
 
 	sc = device_get_softc(dev);
+	if (sc == NULL)
+		return 0;
+
+	cap->minspeed = cap->maxspeed = 0;
+	foundcount = fmtcount = 0;
 
 	for (i = 0; i < sc->sc_nalts; i++) {
-		fmt = 0;
-		a1d = sc->sc_alts[i].asf1desc;
-		prec = a1d->bBitResolution;	/* precision */
+		dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
 
-		switch (sc->sc_alts[i].encoding) {
-		case AUDIO_ENCODING_ULINEAR_LE:
-			if (prec == 8) {
-				fmt = AFMT_U8;
-			} else if (prec == 16) {
-				fmt = AFMT_U16_LE;
-			}
-			break;
-		case AUDIO_ENCODING_SLINEAR_LE:
-			if (prec == 8) {
-				fmt = AFMT_S8;
-			} else if (prec == 16) {
-				fmt = AFMT_S16_LE;
-			}
-			break;
-		case AUDIO_ENCODING_ULINEAR_BE:
-			if (prec == 16) {
-				fmt = AFMT_U16_BE;
-			}
-			break;
-		case AUDIO_ENCODING_SLINEAR_BE:
-			if (prec == 16) {
-				fmt = AFMT_S16_BE;
-			}
-			break;
-		case AUDIO_ENCODING_ALAW:
-			if (prec == 8) {
-				fmt = AFMT_A_LAW;
-			}
-			break;
-		case AUDIO_ENCODING_ULAW:
-			if (prec == 8) {
-				fmt = AFMT_MU_LAW;
-			}
-			break;
-		}
+		if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY))
+			continue;
 
-		if (fmt != 0) {
-			if (a1d->bNrChannels == 2) {	/* stereo/mono */
-				fmt |= AFMT_STEREO;
-			} else if (a1d->bNrChannels != 1) {
-				fmt = 0;
-			}
+		asf1d = sc->sc_alts[i].asf1desc;
+		format = sc->sc_alts[i].encoding;
+
+		numchan = asf1d->bNrChannels;
+		subframesize = asf1d->bSubFrameSize;
+		prec = asf1d->bBitResolution;	/* precision */
+		iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS;
+
+		if (iscontinuous)
+			snprintf(freq_descr, sizeof(freq_descr), "continous min %d max %d", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+		else
+			snprintf(freq_descr, sizeof(freq_descr), "fixed frequency (%d listed formats)", asf1d->bSamFreqType);
+
+		if (numchan == 1)
+			numchannel_descr = " (mono)";
+		else if (numchan == 2)
+			numchannel_descr = " (stereo)";
+		else
+			numchannel_descr = "";
+
+		if (bootverbose) {
+			device_printf(dev, "uaudio_query_formats: found a native %s channel%s %s %dbit %dbytes/subframe X %d channels = %d bytes per sample\n",
+					(dir==UE_DIR_OUT)?"playback":"record",
+					numchannel_descr, freq_descr,
+					prec, subframesize, numchan, subframesize*numchan);
 		}
+		/*
+		 * Now start rejecting the ones that don't map to FreeBSD
+		 */
 
-		if (fmt != 0) {
-			dir= UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
-			if (dir == UE_DIR_OUT) {
-				pfmt[pn++] = fmt;
-			} else if (dir == UE_DIR_IN) {
-				rfmt[rn++] = fmt;
+		if (numchan != 1 && numchan != 2)
+			continue;
+
+		for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++)
+			if (iterator->uaudio_fmt == format && iterator->uaudio_prec == prec)
+				break;
+
+		if (iterator->uaudio_fmt == 0)
+			continue;
+
+		fmt = iterator->freebsd_fmt;
+
+		if (numchan == 2)
+			fmt |= AFMT_STEREO;
+
+		foundcount++;
+
+		if (fmtcount >= maxfmt)
+			continue;
+
+		cap->fmtlist[fmtcount++] = fmt;
+
+		if (iscontinuous) {
+			freq_min = UA_SAMP_LO(asf1d);
+			freq_max = UA_SAMP_HI(asf1d);
+
+			if (cap->minspeed == 0 || freq_min < cap->minspeed)
+				cap->minspeed = freq_min;
+			if (cap->maxspeed == 0)
+				cap->maxspeed = cap->minspeed;
+			if (freq_max > cap->maxspeed)
+				cap->maxspeed = freq_max;
+		} else {
+			for (r = 0; r < asf1d->bSamFreqType; r++) {
+				freq = UA_GETSAMP(asf1d, r);
+				if (cap->minspeed == 0 || freq < cap->minspeed)
+					cap->minspeed = freq;
+				if (cap->maxspeed == 0)
+					cap->maxspeed = cap->minspeed;
+				if (freq > cap->maxspeed)
+					cap->maxspeed = freq;
 			}
 		}
-
-		if ((pn > 8*2) || (rn > 8*2))
-			break;
 	}
-	pfmt[pn] = 0;
-	rfmt[rn] = 0;
-	return;
+	cap->fmtlist[fmtcount] = 0;
+	return foundcount;
 }
 
 void
@@ -3930,25 +4017,81 @@ uaudio_chan_set_param_blocksize(device_t
 	return;
 }
 
-void
-uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int dir)
+int
+uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir)
 {
+	const struct uaudio_conversion *iterator;
 	struct uaudio_softc *sc;
 	struct chan *ch;
+	int i, r, score, hiscore, bestspeed;
 
 	sc = device_get_softc(dev);
 #ifndef NO_RECORDING
-	if (dir == PCMDIR_PLAY)
+	if (reqdir == PCMDIR_PLAY)
 		ch = &sc->sc_playchan;
 	else
 		ch = &sc->sc_recchan;
 #else
 	ch = &sc->sc_playchan;
 #endif
+	/*
+	 * We are successful if we find an endpoint that matches our selected format and it
+	 * supports the requested speed.
+	 */
+	hiscore = 0;
+	bestspeed = 1;
+	for (i = 0; i < sc->sc_nalts; i++) {
+		int dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
+		int format = sc->sc_alts[i].encoding;
+		const struct usb_audio_streaming_type1_descriptor *asf1d = sc->sc_alts[i].asf1desc;
+		int iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS;
 
-	ch->sample_rate = speed;
+		if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY))
+			continue;
 
-	return;
+		for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++)
+			if (iterator->uaudio_fmt != format || iterator->freebsd_fmt != (ch->format&0xfffffff))
+				continue;
+			if (iscontinuous) {
+				if (speed >= UA_SAMP_LO(asf1d) && speed <= UA_SAMP_HI(asf1d)) {
+					ch->sample_rate = speed;
+					return speed;
+				} else if (speed < UA_SAMP_LO(asf1d)) {
+					score = 0xfff * speed / UA_SAMP_LO(asf1d);
+					if (score > hiscore) {
+						bestspeed = UA_SAMP_LO(asf1d);
+						hiscore = score;
+					}
+				} else if (speed < UA_SAMP_HI(asf1d)) {
+					score = 0xfff * UA_SAMP_HI(asf1d) / speed;
+					if (score > hiscore) {
+						bestspeed = UA_SAMP_HI(asf1d);
+						hiscore = score;
+					}
+				}
+				continue;
+			}
+			for (r = 0; r < asf1d->bSamFreqType; r++) {
+				if (speed == UA_GETSAMP(asf1d, r)) {
+					ch->sample_rate = speed;
+					return speed;
+				}
+				if (speed > UA_GETSAMP(asf1d, r))
+					score = 0xfff * UA_GETSAMP(asf1d, r) / speed;
+				else
+					score = 0xfff * speed / UA_GETSAMP(asf1d, r);
+				if (score > hiscore) { 
+					bestspeed = UA_GETSAMP(asf1d, r);
+					hiscore = score;
+				}
+			}
+	}
+	if (bestspeed != 1) {
+		ch->sample_rate = bestspeed;
+		return bestspeed;
+	}
+
+	return 0;
 }
 
 int
--- uaudio.h.orig	Tue Nov  8 04:17:58 2005
+++ uaudio.h	Tue Nov  8 04:50:46 2005
@@ -41,7 +41,7 @@ int	uaudio_halt_in_dma(device_t dev);
 #endif
 void	uaudio_chan_set_param(device_t, u_char *, u_char *);
 void	uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir);
-void	uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int dir);
+int	uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir);
 void	uaudio_chan_set_param_format(device_t dev, u_int32_t format,int dir);
 int	uaudio_chan_getptr(device_t dev, int);
 void	uaudio_mixer_set(device_t dev, unsigned type, unsigned left,
@@ -49,5 +49,5 @@ void	uaudio_mixer_set(device_t dev, unsi
 u_int32_t uaudio_mixer_setrecsrc(device_t dev, u_int32_t src);
 u_int32_t uaudio_query_mix_info(device_t dev);
 u_int32_t uaudio_query_recsrc_info(device_t dev);
-void	uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt);
+unsigned uaudio_query_formats(device_t dev, int dir, unsigned maxfmt, struct pcmchan_caps *fmt);
 void	uaudio_sndstat_register(device_t dev);
--- uaudio_pcm.c.orig	Tue Nov  8 04:17:47 2005
+++ uaudio_pcm.c	Tue Nov  8 04:54:47 2005
@@ -49,16 +49,13 @@ struct ua_info {
 	device_t sc_dev;
 	u_int32_t bufsz;
 	struct ua_chinfo pch, rch;
+#define FORMAT_NUM	32
+	u_int32_t ua_playfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */
+	u_int32_t ua_recfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */
+	struct pcmchan_caps ua_playcaps;
+	struct pcmchan_caps ua_reccaps;
 };
 
-static u_int32_t ua_playfmt[8*2+1]; /* 8 format * (stereo or mono) + endptr */
-
-static struct pcmchan_caps ua_playcaps = {8000, 48000, ua_playfmt, 0};
-
-static u_int32_t ua_recfmt[8*2+1]; /* 8 format * (stereo or mono) + endptr */
-
-static struct pcmchan_caps ua_reccaps = {8000, 48000, ua_recfmt, 0};
-
 #define UAUDIO_DEFAULT_BUFSZ		16*1024
 
 /************************************************************/
@@ -76,23 +73,9 @@ ua_chan_init(kobj_t obj, void *devinfo, 
 	ch->dir = dir;
 
 	pa_dev = device_get_parent(sc->sc_dev);
-     	/* Create ua_playfmt[] & ua_recfmt[] */
-	uaudio_query_formats(pa_dev, (u_int32_t *)&ua_playfmt, (u_int32_t *)&ua_recfmt);
-	if (dir == PCMDIR_PLAY) {
-		if (ua_playfmt[0] == 0) {
-			printf("play channel supported format list invalid\n");
-			return NULL;
-		}
-	} else {
-		if (ua_recfmt[0] == 0) {
-			printf("record channel supported format list invalid\n");
-			return NULL;
-		}
-
-	}
 
 	ch->buf = malloc(sc->bufsz, M_DEVBUF, M_NOWAIT);
-        if (ch->buf == NULL)
+	if (ch->buf == NULL)
 		return NULL;
 	if (sndbuf_setup(b, ch->buf, sc->bufsz) != 0) {
 		free(ch->buf, M_DEVBUF);
@@ -133,6 +116,9 @@ ua_chan_setformat(kobj_t obj, void *data
 
 	struct ua_chinfo *ch = data;
 
+	/*
+	 * At this point, no need to query as we shouldn't select an unsorted format
+	 */
 	ua = ch->parent;
 	pa_dev = device_get_parent(ua->sc_dev);
 	uaudio_chan_set_param_format(pa_dev, format, ch->dir);
@@ -144,15 +130,15 @@ ua_chan_setformat(kobj_t obj, void *data
 static int
 ua_chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
 {
+	struct ua_chinfo *ch;
 	device_t pa_dev;
-	struct ua_info *ua;
+	int bestspeed;
 
-	struct ua_chinfo *ch = data;
-	ch->spd = speed;
+	ch = data;
+	pa_dev = device_get_parent(ch->parent->sc_dev);
 
-	ua = ch->parent;
-	pa_dev = device_get_parent(ua->sc_dev);
-	uaudio_chan_set_param_speed(pa_dev, speed, ch->dir);
+	if ((bestspeed = uaudio_chan_set_param_speed(pa_dev, speed, ch->dir)))
+		ch->spd = bestspeed;
 
 	return ch->spd;
 }
@@ -224,9 +210,10 @@ ua_chan_getptr(kobj_t obj, void *data)
 static struct pcmchan_caps *
 ua_chan_getcaps(kobj_t obj, void *data)
 {
-	struct ua_chinfo *ch = data;
+	struct ua_chinfo *ch;
 
-	return (ch->dir == PCMDIR_PLAY) ? &ua_playcaps : & ua_reccaps;
+	ch = data;
+	return (ch->dir == PCMDIR_PLAY) ? &(ch->parent->ua_playcaps) : &(ch->parent->ua_reccaps);
 }
 
 static kobj_method_t ua_chan_methods[] = {
@@ -318,42 +305,63 @@ ua_attach(device_t dev)
 {
 	struct ua_info *ua;
 	char status[SND_STATUSLEN];
+	device_t pa_dev;
+	u_int32_t nplay, nrec;
+	int i;
 
-	ua = (struct ua_info *)malloc(sizeof *ua, M_DEVBUF, M_NOWAIT);
-	if (!ua)
+	ua = (struct ua_info *)malloc(sizeof *ua, M_DEVBUF, M_ZERO | M_NOWAIT);
+	if (ua == NULL)
 		return ENXIO;
-	bzero(ua, sizeof *ua);
 
 	ua->sc_dev = dev;
 
+	pa_dev = device_get_parent(dev);
+
 	ua->bufsz = pcm_getbuffersize(dev, 4096, UAUDIO_DEFAULT_BUFSZ, 65536);
 	if (bootverbose)
 		device_printf(dev, "using a default buffer size of %jd\n", (intmax_t)ua->bufsz);
 
 	if (mixer_init(dev, &ua_mixer_class, ua)) {
-		return(ENXIO);
+		goto bad;
 	}
 
 	snprintf(status, SND_STATUSLEN, "at addr ?");
 
+	ua->ua_playcaps.fmtlist = ua->ua_playfmt;
+	ua->ua_reccaps.fmtlist = ua->ua_recfmt;
+	nplay = uaudio_query_formats(pa_dev, PCMDIR_PLAY, FORMAT_NUM * 2, &ua->ua_playcaps);
+	nrec = uaudio_query_formats(pa_dev, PCMDIR_REC, FORMAT_NUM * 2, &ua->ua_reccaps);
+
+	if (nplay > 1)
+		nplay = 1;
+	if (nrec > 1)
+		nrec = 1;
+
 #ifndef NO_RECORDING
-	if (pcm_register(dev, ua, 1, 1)) {
+	if (pcm_register(dev, ua, nplay, nrec)) {
 #else
-	if (pcm_register(dev, ua, 1, 0)) {
+	if (pcm_register(dev, ua, nplay, 0)) {
 #endif
-		return(ENXIO);
+		goto bad;
 	}
 
 	sndstat_unregister(dev);
 	uaudio_sndstat_register(dev);
 
-	pcm_addchan(dev, PCMDIR_PLAY, &ua_chan_class, ua);
+	for (i = 0; i < nplay; i++) {
+		pcm_addchan(dev, PCMDIR_PLAY, &ua_chan_class, ua);
+	}
 #ifndef NO_RECORDING
-	pcm_addchan(dev, PCMDIR_REC, &ua_chan_class, ua);
+	for (i = 0; i < nrec; i++) {
+		pcm_addchan(dev, PCMDIR_REC, &ua_chan_class, ua);
+	}
 #endif
 	pcm_setstatus(dev, status);
 
 	return 0;
+
+bad:	free(ua, M_DEVBUF);
+	return ENXIO;
 }
 
 static int

----Next_Part(Tue_Nov__8_05:04:31_2005_613)----



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20051108.050431.343192595.kazuhito>