From owner-freebsd-multimedia@FreeBSD.ORG Mon Nov 7 20:04:37 2005 Return-Path: X-Original-To: freebsd-multimedia@freebsd.org Delivered-To: freebsd-multimedia@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 9B81D16A41F for ; Mon, 7 Nov 2005 20:04:37 +0000 (GMT) (envelope-from kazuhito@ph.noda.tus.ac.jp) Received: from t-mta7.odn.ne.jp (mfep7.odn.ne.jp [143.90.131.185]) by mx1.FreeBSD.org (Postfix) with ESMTP id 0ADA243D5D for ; Mon, 7 Nov 2005 20:04:33 +0000 (GMT) (envelope-from kazuhito@ph.noda.tus.ac.jp) Received: from localhost ([219.66.59.219]) by t-mta7.odn.ne.jp with ESMTP id <20051107200431771.BWBL.2824.t-mta7.odn.ne.jp@mta7.odn.ne.jp>; Tue, 8 Nov 2005 05:04:31 +0900 Date: Tue, 08 Nov 2005 05:04:31 +0900 (JST) Message-Id: <20051108.050431.343192595.kazuhito@ph.noda.tus.ac.jp> To: julian@elischer.org From: Kazuhito HONDA In-Reply-To: <436F8EFE.9090704@elischer.org> References: <43691146.7030404@elischer.org> <20051103034216.379caa05.skywizard@MyBSD.org.my> <436F8EFE.9090704@elischer.org> X-Mailer: Mew version 3.3 on XEmacs 21.4.17 (Jumbo Shrimp) Mime-Version: 1.0 Content-Type: Multipart/Mixed; boundary="--Next_Part(Tue_Nov__8_05:04:31_2005_613)--" Content-Transfer-Encoding: 7bit Cc: freebsd-multimedia@freebsd.org, Alexander@Leidinger.net, skywizard@MyBSD.org.my Subject: Re: uaudio and Digigram UAX220 X-BeenThere: freebsd-multimedia@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Multimedia discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 07 Nov 2005 20:04:37 -0000 ----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 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)----