From owner-svn-src-all@FreeBSD.ORG Tue Dec 22 02:11:38 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 5615D106568B; Tue, 22 Dec 2009 02:11:38 +0000 (UTC) (envelope-from thompsa@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 447298FC17; Tue, 22 Dec 2009 02:11:38 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id nBM2Bc3I047527; Tue, 22 Dec 2009 02:11:38 GMT (envelope-from thompsa@svn.freebsd.org) Received: (from thompsa@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id nBM2BcxU047525; Tue, 22 Dec 2009 02:11:38 GMT (envelope-from thompsa@svn.freebsd.org) Message-Id: <200912220211.nBM2BcxU047525@svn.freebsd.org> From: Andrew Thompson Date: Tue, 22 Dec 2009 02:11:38 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r200825 - head/sys/dev/sound/usb X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 22 Dec 2009 02:11:38 -0000 Author: thompsa Date: Tue Dec 22 02:11:37 2009 New Revision: 200825 URL: http://svn.freebsd.org/changeset/base/200825 Log: - add support for more than 2 audio channels. [1] - add support for more sample rates Submitted by: [1] ariff (earlier version), Hans Petter Selasky Modified: head/sys/dev/sound/usb/uaudio.c Modified: head/sys/dev/sound/usb/uaudio.c ============================================================================== --- head/sys/dev/sound/usb/uaudio.c Tue Dec 22 02:04:16 2009 (r200824) +++ head/sys/dev/sound/usb/uaudio.c Tue Dec 22 02:11:37 2009 (r200825) @@ -87,20 +87,27 @@ #include #include "feeder_if.h" -static int uaudio_default_rate = 96000; +static int uaudio_default_rate = 0; /* use rate list */ static int uaudio_default_bits = 32; -static int uaudio_default_channels = 2; +static int uaudio_default_channels = 0; /* use default */ #if USB_DEBUG static int uaudio_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio"); + SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW, &uaudio_debug, 0, "uaudio debug level"); + +TUNABLE_INT("hw.usb.uaudio.default_rate", &uaudio_default_rate); SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_rate, CTLFLAG_RW, &uaudio_default_rate, 0, "uaudio default sample rate"); + +TUNABLE_INT("hw.usb.uaudio.default_bits", &uaudio_default_bits); SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_bits, CTLFLAG_RW, &uaudio_default_bits, 0, "uaudio default sample bits"); + +TUNABLE_INT("hw.usb.uaudio.default_channels", &uaudio_default_channels); SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_channels, CTLFLAG_RW, &uaudio_default_channels, 0, "uaudio default sample channels"); #endif @@ -169,10 +176,16 @@ struct uaudio_chan { uint32_t intr_size; /* in bytes */ uint32_t intr_frames; /* in units */ uint32_t sample_rate; + uint32_t frames_per_second; + uint32_t sample_rem; + uint32_t sample_curr; + uint32_t format; uint32_t pcm_format[2]; - uint16_t bytes_per_frame; + uint16_t bytes_per_frame[2]; + + uint16_t sample_size; uint8_t valid; uint8_t iface_index; @@ -330,7 +343,7 @@ static usb_callback_t umidi_write_clear_ static usb_callback_t umidi_bulk_write_callback; static void uaudio_chan_fill_info_sub(struct uaudio_softc *, - struct usb_device *, uint32_t, uint16_t, uint8_t, uint8_t); + struct usb_device *, uint32_t, uint8_t, uint8_t); static void uaudio_chan_fill_info(struct uaudio_softc *, struct usb_device *); static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *, @@ -787,8 +800,7 @@ uaudio_chan_dump_ep_desc(const usb2_endp static void uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev, - uint32_t rate, uint16_t fps, uint8_t channels, - uint8_t bit_resolution) + uint32_t rate, uint8_t channels, uint8_t bit_resolution) { struct usb_descriptor *desc = NULL; const struct usb2_audio_streaming_interface_descriptor *asid = NULL; @@ -811,7 +823,6 @@ uaudio_chan_fill_info_sub(struct uaudio_ uint8_t bBitResolution; uint8_t x; uint8_t audio_if = 0; - uint8_t sample_size; while ((desc = usb_desc_foreach(cd, desc))) { @@ -1040,16 +1051,10 @@ uaudio_chan_fill_info_sub(struct uaudio_ chan->usb2_cfg = uaudio_cfg_play; - sample_size = (( + chan->sample_size = (( UAUDIO_MAX_CHAN(chan->p_asf1d->bNrChannels) * chan->p_asf1d->bBitResolution) / 8); - /* - * NOTE: "chan->bytes_per_frame" - * should not be zero! - */ - chan->bytes_per_frame = ((rate / fps) * sample_size); - if (sc->sc_sndstat_valid) { sbuf_printf(&sc->sc_sndstat, "\n\t" "mode %d.%d:(%s) %dch, %d/%dbit, %s, %dHz", @@ -1067,12 +1072,32 @@ uaudio_chan_fill_info_sub(struct uaudio_ } } +/* This structure defines all the supported rates. */ + +static const uint32_t uaudio_rate_list[] = { + 96000, + 88000, + 80000, + 72000, + 64000, + 56000, + 48000, + 44100, + 40000, + 32000, + 24000, + 22050, + 16000, + 11025, + 8000, + 0 +}; + static void uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev) { uint32_t rate = uaudio_default_rate; - uint32_t z; - uint16_t fps = usbd_get_isoc_fps(udev); + uint8_t z; uint8_t bits = uaudio_default_bits; uint8_t y; uint8_t channels = uaudio_default_channels; @@ -1083,14 +1108,24 @@ uaudio_chan_fill_info(struct uaudio_soft /* set a valid value */ bits = 32; } - rate -= (rate % fps); - if ((rate == 0) || (rate > 192000)) { - /* set a valid value */ - rate = 192000 - (192000 % fps); - } - if ((channels == 0) || (channels > 2)) { - /* set a valid value */ - channels = 2; + if (channels == 0) { + switch (usbd_get_speed(udev)) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + /* + * Due to high bandwidth usage and problems + * with HIGH-speed split transactions we + * disable surround setups on FULL-speed USB + * by default + */ + channels = 2; + break; + default: + channels = 16; + break; + } + } else if (channels > 16) { + channels = 16; } if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND)) { sc->sc_sndstat_valid = 1; @@ -1099,8 +1134,14 @@ uaudio_chan_fill_info(struct uaudio_soft for (x = channels; x; x--) { for (y = bits; y; y -= 8) { - for (z = rate; z; z -= fps) { - uaudio_chan_fill_info_sub(sc, udev, z, fps, x, y); + + /* try user defined rate, if any */ + if (rate != 0) + uaudio_chan_fill_info_sub(sc, udev, rate, x, y); + + /* try find a matching rate, if any */ + for (z = 0; uaudio_rate_list[z]; z++) { + uaudio_chan_fill_info_sub(sc, udev, uaudio_rate_list[z], x, y); if (sc->sc_rec_chan.valid && sc->sc_play_chan.valid) { @@ -1116,18 +1157,6 @@ done: } } -/* - * The following function sets up data size and block count for the - * next audio transfer. - */ -static void -uaudio_setup_blockcount(struct uaudio_chan *ch, - uint32_t *total, uint32_t *blockcount) -{ - *total = ch->intr_size; - *blockcount = ch->intr_frames; -} - static void uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error) { @@ -1137,12 +1166,11 @@ uaudio_chan_play_callback(struct usb_xfe uint32_t blockcount; uint32_t n; uint32_t offset; - int actlen, sumlen; + int actlen; + int sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); - uaudio_setup_blockcount(ch, &total, &blockcount); - if (ch->end == ch->start) { DPRINTF("no buffer!\n"); return; @@ -1153,22 +1181,39 @@ uaudio_chan_play_callback(struct usb_xfe tr_transferred: if (actlen < sumlen) { DPRINTF("short transfer, " - "%d of %d bytes\n", actlen, total); + "%d of %d bytes\n", actlen, sumlen); } chn_intr(ch->pcm_ch); case USB_ST_SETUP: - if (ch->bytes_per_frame > usbd_xfer_max_framelen(xfer)) { + if (ch->bytes_per_frame[1] > usbd_xfer_max_framelen(xfer)) { DPRINTF("bytes per transfer, %d, " "exceeds maximum, %d!\n", - ch->bytes_per_frame, + ch->bytes_per_frame[1], usbd_xfer_max_framelen(xfer)); break; } - /* setup frame length */ + + blockcount = ch->intr_frames; + + /* setup number of frames */ usbd_xfer_set_frames(xfer, blockcount); - for (n = 0; n != blockcount; n++) - usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame); + + /* reset total length */ + total = 0; + + /* setup frame lengths */ + for (n = 0; n != blockcount; n++) { + ch->sample_curr += ch->sample_rem; + if (ch->sample_curr >= ch->frames_per_second) { + ch->sample_curr -= ch->frames_per_second; + usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame[1]); + total += ch->bytes_per_frame[1]; + } else { + usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame[0]); + total += ch->bytes_per_frame[0]; + } + } DPRINTFN(6, "transfer %d bytes\n", total); @@ -1210,7 +1255,6 @@ uaudio_chan_record_callback(struct usb_x struct usb_page_cache *pc; uint32_t n; uint32_t m; - uint32_t total; uint32_t blockcount; uint32_t offset0; uint32_t offset1; @@ -1222,8 +1266,6 @@ uaudio_chan_record_callback(struct usb_x usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); mfl = usbd_xfer_max_framelen(xfer); - uaudio_setup_blockcount(ch, &total, &blockcount); - if (ch->end == ch->start) { DPRINTF("no buffer!\n"); return; @@ -1231,12 +1273,8 @@ uaudio_chan_record_callback(struct usb_x switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: - if (actlen < total) { - DPRINTF("short transfer, " - "%d of %d bytes\n", actlen, total); - } else { - DPRINTFN(6, "transferred %d bytes\n", actlen); - } + + DPRINTFN(6, "transferred %d bytes\n", actlen); offset0 = 0; pc = usbd_xfer_get_frame(xfer, 0); @@ -1271,6 +1309,8 @@ uaudio_chan_record_callback(struct usb_x case USB_ST_SETUP: tr_setup: + blockcount = ch->intr_frames; + usbd_xfer_set_frames(xfer, blockcount); for (n = 0; n < blockcount; n++) { usbd_xfer_set_frame_len(xfer, n, mfl); @@ -1295,6 +1335,8 @@ uaudio_chan_init(struct uaudio_softc *sc &sc->sc_play_chan : &sc->sc_rec_chan); uint32_t buf_size; uint32_t frames; + uint32_t format; + uint16_t fps; uint8_t endpoint; uint8_t blocks; uint8_t iface_index; @@ -1302,7 +1344,9 @@ uaudio_chan_init(struct uaudio_softc *sc uint8_t fps_shift; usb_error_t err; - if (usbd_get_isoc_fps(sc->sc_udev) < 8000) { + fps = usbd_get_isoc_fps(sc->sc_udev); + + if (fps < 8000) { /* FULL speed USB */ frames = 8; } else { @@ -1310,10 +1354,6 @@ uaudio_chan_init(struct uaudio_softc *sc frames = UAUDIO_NFRAMES; } - /* compute required buffer size */ - - buf_size = (ch->bytes_per_frame * frames); - /* setup play/record format */ ch->pcm_cap.fmtlist = ch->pcm_format; @@ -1329,15 +1369,34 @@ uaudio_chan_init(struct uaudio_softc *sc ch->pcm_ch = c; ch->pcm_mtx = c->lock; - if (ch->p_asf1d->bNrChannels >= 2) - ch->pcm_cap.fmtlist[0] = - SND_FORMAT(ch->p_fmt->freebsd_fmt, 2, 0); - else - ch->pcm_cap.fmtlist[0] = - SND_FORMAT(ch->p_fmt->freebsd_fmt, 1, 0); + format = ch->p_fmt->freebsd_fmt; + + switch (ch->p_asf1d->bNrChannels) { + case 2: + /* stereo */ + format = SND_FORMAT(format, 2, 0); + break; + case 1: + /* mono */ + format = SND_FORMAT(format, 1, 0); + break; + default: + /* surround and more */ + format = feeder_matrix_default_format( + SND_FORMAT(format, ch->p_asf1d->bNrChannels, 0)); + break; + } + ch->pcm_cap.fmtlist[0] = format; ch->pcm_cap.fmtlist[1] = 0; + /* check if format is not supported */ + + if (format == 0) { + DPRINTF("The selected audio format is not supported\n"); + goto error; + } + /* set alternate interface corresponding to the mode */ endpoint = ch->p_ed1->bEndpointAddress; @@ -1377,10 +1436,27 @@ uaudio_chan_init(struct uaudio_softc *sc fps_shift = usbd_xfer_get_fps_shift(ch->xfer[0]); - /* setup frame sizes */ + /* down shift number of frames per second, if any */ + fps >>= fps_shift; + frames >>= fps_shift; + + /* bytes per frame should not be zero */ + ch->bytes_per_frame[0] = ((ch->sample_rate / fps) * ch->sample_size); + ch->bytes_per_frame[1] = (((ch->sample_rate + fps - 1) / fps) * ch->sample_size); + + /* setup data rate dithering, if any */ + ch->frames_per_second = fps; + ch->sample_rem = ch->sample_rate % fps; + ch->sample_curr = 0; + ch->frames_per_second = fps; + + /* compute required buffer size */ + buf_size = (ch->bytes_per_frame[1] * frames); + ch->intr_size = buf_size; - ch->intr_frames = (frames >> fps_shift); - ch->bytes_per_frame <<= fps_shift; + ch->intr_frames = frames; + + DPRINTF("fps=%d sample_rem=%d\n", fps, ch->sample_rem); if (ch->intr_frames == 0) { DPRINTF("frame shift is too high!\n");