uaudio and Digigram UAX220

Kazuhito HONDA kazuhito at ph.noda.tus.ac.jp
Wed Nov 2 06:14:16 PST 2005


From: Ariff Abdullah <skywizard at MyBSD.org.my>
Subject: Re: uaudio and Digigram UAX220
Date: Sat, 29 Oct 2005 09:48:31 +0800

> Could you please do the cleanup? :) The only thing is that Julian
> already said that those patch break his USB microphone, so this must
> undergo some serious regression test to be accepted.

Oh, I missed that.  I wonder why it isn't committed.
I have searched the patch and found one problem.

In uaudio_query_formats() of uaudio.c
+
+		if ( numchan != 1 && numchan == 2 )
+
+		foundcount++;
+

`foundcount' is a flag to indicate which a valid format is found or not.
`numchan' is a number of channel.  
If a device has mono channels only, foundcont flag isn't changed.
I guess that it broke Julian's USB microphone, 
because microphone is usually mono.

`if' line is unnecessary.  
We should remove this line and lieve `foundcount++'.

And thank you for your comments and advice.
I have done the cleanup and improvements of the patch along them.
(include bug fix, 24 bit and 32 bit sound stream, and speed setting)
It is attached at the end of this mail.

Sincerely yours,
Kazuhito HONDA
-------------- next part --------------
--- uaudio.c.old	Wed Nov  2 16:16:00 2005
+++ uaudio.c	Wed Nov  2 16:18:32 2005
@@ -1,5 +1,5 @@
 /*	$NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $	*/
-/*	$FreeBSD: src/sys/dev/sound/usb/uaudio.c,v 1.16 2005/09/18 15:13:06 netchild Exp $ */
+/*	$FreeBSD: src/sys/dev/sound/usb/uaudio.c, kazuhito Exp $ */
 
 /*-
  * Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -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_convesion {
+	uint8_t uaudio_fmt;
+	uint8_t uaudio_prec;
+	uint32_t freebsd_fmt;
+};
+
+const struct uaudio_convesion 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_convesion *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, 64, "continous min %d max %d", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+		else
+			snprintf(freq_descr, 64, "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_convesion *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_pcm.c.old	Wed Nov  2 16:15:42 2005
+++ uaudio_pcm.c	Wed Nov  2 22:42:25 2005
@@ -1,4 +1,4 @@
-/* $FreeBSD: src/sys/dev/sound/usb/uaudio_pcm.c,v 1.17 2005/10/02 15:51:19 netchild Exp $ */
+/* $FreeBSD: src/sys/dev/sound/usb/uaudio_pcm.c,kazuhito Exp $ */
 
 /*-
  * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu at navi.org>
@@ -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 queury 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


More information about the freebsd-multimedia mailing list