Author: thompsa
Date: Thu Dec 31 00:15:36 2009
New Revision: 201310
URL: http://svn.freebsd.org/changeset/base/201310

Log:
  MFC r200825
  
   - 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:
  stable/8/sys/dev/sound/usb/uaudio.c
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)
  stable/8/sys/dev/xen/xenpci/   (props changed)

Modified: stable/8/sys/dev/sound/usb/uaudio.c
==============================================================================
--- stable/8/sys/dev/sound/usb/uaudio.c Thu Dec 31 00:14:37 2009        
(r201309)
+++ stable/8/sys/dev/sound/usb/uaudio.c Thu Dec 31 00:15:36 2009        
(r201310)
@@ -87,20 +87,27 @@
 #include <dev/sound/chip.h>
 #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");
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to