Author: hselasky
Date: Sun May 12 12:20:04 2013
New Revision: 250561
URL: http://svnweb.freebsd.org/changeset/base/250561

Log:
  MFC r249796, r249830, r249844 and r249845:
  USB audio fixes and improvements.
  - Fix runtime switching of sample rate
  - Fix feedback endpoint algorithm

Modified:
  stable/9/sys/dev/sound/usb/uaudio.c
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/dev/   (props changed)

Modified: stable/9/sys/dev/sound/usb/uaudio.c
==============================================================================
--- stable/9/sys/dev/sound/usb/uaudio.c Sun May 12 12:13:23 2013        
(r250560)
+++ stable/9/sys/dev/sound/usb/uaudio.c Sun May 12 12:20:04 2013        
(r250561)
@@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$");
 #include <dev/usb/usbdi_util.h>
 #include <dev/usb/usbhid.h>
 #include <dev/usb/usb_request.h>
+#include <dev/usb/usb_process.h>
 
 #define        USB_DEBUG_VAR uaudio_debug
 #include <dev/usb/usb_debug.h>
@@ -176,19 +177,34 @@ struct uaudio_mixer_node {
        struct uaudio_mixer_node *next;
 };
 
+struct uaudio_configure_msg {
+       struct usb_proc_msg hdr;
+       struct uaudio_softc *sc;
+};
+
+#define        CHAN_MAX_ALT 20
+
+struct uaudio_chan_alt {
+       union uaudio_asf1d p_asf1d;
+       union uaudio_sed p_sed;
+       const usb_endpoint_descriptor_audio_t *p_ed1;
+       const struct uaudio_format *p_fmt;
+       const struct usb_config *usb_cfg;
+       uint32_t sample_rate;   /* in Hz */
+       uint16_t sample_size;
+       uint8_t iface_index;
+       uint8_t iface_alt_index;
+       uint8_t channels;
+};
+
 struct uaudio_chan {
        struct pcmchan_caps pcm_cap;    /* capabilities */
-
+       struct uaudio_chan_alt usb_alt[CHAN_MAX_ALT];
        struct snd_dbuf *pcm_buf;
-       const struct usb_config *usb_cfg;
        struct mtx *pcm_mtx;            /* lock protecting this structure */
        struct uaudio_softc *priv_sc;
        struct pcm_channel *pcm_ch;
        struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1];
-       union uaudio_asf1d p_asf1d;
-       union uaudio_sed p_sed;
-       const usb_endpoint_descriptor_audio_t *p_ed1;
-       const struct uaudio_format *p_fmt;
 
        uint8_t *buf;                   /* pointer to buffer */
        uint8_t *start;                 /* upper layer buffer start */
@@ -196,24 +212,24 @@ struct uaudio_chan {
        uint8_t *cur;                   /* current position in upper layer
                                         * buffer */
 
-       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 max_buf;
 
-       uint32_t format;
        uint32_t pcm_format[2];
 
        uint16_t bytes_per_frame[2];
 
-       uint16_t sample_size;
-
-       uint8_t valid;
-       uint8_t iface_index;
-       uint8_t iface_alt_index;
-       uint8_t channels;
+       uint8_t num_alt;
+       uint8_t cur_alt;
+       uint8_t set_alt;
+       uint8_t operation;
+#define        CHAN_OP_NONE 0
+#define        CHAN_OP_START 1
+#define        CHAN_OP_STOP 2
+#define        CHAN_OP_DRAIN 3
 
        uint8_t last_sync_time;
        uint8_t last_sync_state;
@@ -309,6 +325,7 @@ struct uaudio_softc {
        struct uaudio_hid sc_hid;
        struct uaudio_search_result sc_mixer_clocks;
        struct uaudio_mixer_node sc_mixer_node;
+       struct uaudio_configure_msg sc_config_msg[2];
 
        struct mtx *sc_mixer_lock;
        struct snd_mixer *sc_mixer_dev;
@@ -434,6 +451,8 @@ static usb_callback_t umidi_bulk_read_ca
 static usb_callback_t umidi_bulk_write_callback;
 static usb_callback_t uaudio_hid_rx_callback;
 
+static usb_proc_callback_t uaudio_configure_msg;
+
 /* ==== USB mixer ==== */
 
 static int uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS);
@@ -856,6 +875,10 @@ uaudio_attach(device_t dev)
        sc->sc_udev = uaa->device;
        sc->sc_mixer_iface_index = uaa->info.bIfaceIndex;
        sc->sc_mixer_iface_no = uaa->info.bIfaceNum;
+       sc->sc_config_msg[0].hdr.pm_callback = &uaudio_configure_msg;
+       sc->sc_config_msg[0].sc = sc;
+       sc->sc_config_msg[1].hdr.pm_callback = &uaudio_configure_msg;
+       sc->sc_config_msg[1].sc = sc;
 
        if (usb_test_quirk(uaa, UQ_AUDIO_SWAP_LR))
                sc->sc_uq_audio_swap_lr = 1;
@@ -900,22 +923,28 @@ uaudio_attach(device_t dev)
        DPRINTF("%d mixer controls\n",
            sc->sc_mixer_count);
 
-       if (sc->sc_play_chan.valid) {
-               device_printf(dev, "Play: %d Hz, %d ch, %s format, "
-                   "2x8ms buffer.\n",
-                   sc->sc_play_chan.sample_rate,
-                   sc->sc_play_chan.channels,
-                   sc->sc_play_chan.p_fmt->description);
+       if (sc->sc_play_chan.num_alt > 0) {
+               uint8_t x;
+               for (x = 0; x != sc->sc_play_chan.num_alt; x++) {
+                       device_printf(dev, "Play: %d Hz, %d ch, %s format, "
+                           "2x8ms buffer.\n",
+                           sc->sc_play_chan.usb_alt[x].sample_rate,
+                           sc->sc_play_chan.usb_alt[x].channels,
+                           sc->sc_play_chan.usb_alt[x].p_fmt->description);
+               }
        } else {
                device_printf(dev, "No playback.\n");
        }
 
-       if (sc->sc_rec_chan.valid) {
-               device_printf(dev, "Record: %d Hz, %d ch, %s format, "
-                   "2x8ms buffer.\n",
-                   sc->sc_rec_chan.sample_rate,
-                   sc->sc_rec_chan.channels,
-                   sc->sc_rec_chan.p_fmt->description);
+       if (sc->sc_rec_chan.num_alt > 0) {
+               uint8_t x;
+               for (x = 0; x != sc->sc_rec_chan.num_alt; x++) {
+                       device_printf(dev, "Record: %d Hz, %d ch, %s format, "
+                           "2x8ms buffer.\n",
+                           sc->sc_rec_chan.usb_alt[x].sample_rate,
+                           sc->sc_rec_chan.usb_alt[x].channels,
+                           sc->sc_rec_chan.usb_alt[x].p_fmt->description);
+               }
        } else {
                device_printf(dev, "No recording.\n");
        }
@@ -950,8 +979,8 @@ uaudio_attach(device_t dev)
         * Only attach a PCM device if we have a playback, recording
         * or mixer device present:
         */
-       if (sc->sc_play_chan.valid ||
-           sc->sc_rec_chan.valid ||
+       if (sc->sc_play_chan.num_alt > 0 ||
+           sc->sc_rec_chan.num_alt > 0 ||
            sc->sc_mix_info) {
                child = device_add_child(dev, "pcm", -1);
 
@@ -1020,18 +1049,18 @@ uaudio_attach_sub(device_t dev, kobj_cla
        snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
 
        if (pcm_register(dev, sc,
-           sc->sc_play_chan.valid ? 1 : 0,
-           sc->sc_rec_chan.valid ? 1 : 0)) {
+           (sc->sc_play_chan.num_alt > 0) ? 1 : 0,
+           (sc->sc_rec_chan.num_alt > 0) ? 1 : 0)) {
                goto detach;
        }
 
        uaudio_pcm_setflags(dev, SD_F_MPSAFE);
        sc->sc_pcm_registered = 1;
 
-       if (sc->sc_play_chan.valid) {
+       if (sc->sc_play_chan.num_alt > 0) {
                pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc);
        }
-       if (sc->sc_rec_chan.valid) {
+       if (sc->sc_rec_chan.num_alt > 0) {
                pcm_addchan(dev, PCMDIR_REC, chan_class, sc);
        }
        pcm_setstatus(dev, status);
@@ -1078,10 +1107,15 @@ uaudio_detach(device_t dev)
         * will time out and close opened /dev/dspX.Y device(s), if
         * any.
         */
-       if (sc->sc_play_chan.valid)
-               usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 
1);
-       if (sc->sc_rec_chan.valid)
-               usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 
1);
+       usb_proc_explore_lock(sc->sc_udev);
+       sc->sc_play_chan.operation = CHAN_OP_DRAIN;
+       sc->sc_rec_chan.operation = CHAN_OP_DRAIN;
+       usb_proc_explore_mwait(sc->sc_udev,
+           &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
+       usb_proc_explore_unlock(sc->sc_udev);
+
+       usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1);
+       usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1);
 
        uaudio_hid_detach(sc);
 
@@ -1100,6 +1134,201 @@ uaudio_detach(device_t dev)
        return (0);
 }
 
+static uint32_t
+uaudio_get_buffer_size(struct uaudio_chan *ch, uint8_t alt)
+{
+       struct uaudio_chan_alt *chan_alt = &ch->usb_alt[alt];
+       /* We use 2 times 8ms of buffer */
+       uint32_t buf_size = (((chan_alt->sample_rate * (UAUDIO_NFRAMES / 8)) +
+           1000 - 1) / 1000) * chan_alt->sample_size;
+       return (buf_size);
+}
+
+static void
+uaudio_configure_msg_sub(struct uaudio_softc *sc,
+    struct uaudio_chan *chan, int dir)
+{
+       struct uaudio_chan_alt *chan_alt;
+       uint32_t frames;
+       uint32_t buf_size;
+       uint16_t fps;
+       uint8_t set_alt;
+       uint8_t fps_shift;
+       uint8_t operation;
+       usb_error_t err;
+
+       if (chan->num_alt <= 0)
+               return;
+
+       DPRINTF("\n");
+
+       usb_proc_explore_lock(sc->sc_udev);
+       operation = chan->operation;
+       chan->operation = CHAN_OP_NONE;
+       usb_proc_explore_unlock(sc->sc_udev);
+
+       mtx_lock(chan->pcm_mtx);
+       if (chan->cur_alt != chan->set_alt)
+               set_alt = chan->set_alt;
+       else
+               set_alt = CHAN_MAX_ALT;
+       mtx_unlock(chan->pcm_mtx);
+
+       if (set_alt >= chan->num_alt)
+               goto done;
+
+       chan_alt = chan->usb_alt + set_alt;
+
+       usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1);
+
+       err = usbd_set_alt_interface_index(sc->sc_udev,
+           chan_alt->iface_index, chan_alt->iface_alt_index);
+       if (err) {
+               DPRINTF("setting of alternate index failed: %s!\n",
+                   usbd_errstr(err));
+               goto error;
+       }
+
+       /*
+        * Only set the sample rate if the channel reports that it
+        * supports the frequency control.
+        */
+
+       if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
+               /* FALLTHROUGH */
+       } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
+               unsigned int x;
+         
+               for (x = 0; x != 256; x++) {
+                       if (dir == PCMDIR_PLAY) {
+                               if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
+                                   (1 << (x % 8)))) {
+                                       continue;
+                               }
+                       } else {
+                               if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
+                                   (1 << (x % 8)))) {
+                                       continue;
+                               }
+                       }
+
+                       if (uaudio20_set_speed(sc->sc_udev,
+                           sc->sc_mixer_iface_no, x, chan_alt->sample_rate)) {
+                               /*
+                                * If the endpoint is adaptive setting
+                                * the speed may fail.
+                                */
+                               DPRINTF("setting of sample rate failed! "
+                                   "(continuing anyway)\n");
+                       }
+               }
+       } else if (chan_alt->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) {
+               if (uaudio_set_speed(sc->sc_udev,
+                   chan_alt->p_ed1->bEndpointAddress, chan_alt->sample_rate)) {
+                       /*
+                        * If the endpoint is adaptive setting the
+                        * speed may fail.
+                        */
+                       DPRINTF("setting of sample rate failed! "
+                           "(continuing anyway)\n");
+               }
+       }
+       if (usbd_transfer_setup(sc->sc_udev, &chan_alt->iface_index, chan->xfer,
+           chan_alt->usb_cfg, UAUDIO_NCHANBUFS + 1, chan, chan->pcm_mtx)) {
+               DPRINTF("could not allocate USB transfers!\n");
+               goto error;
+       }
+
+       fps = usbd_get_isoc_fps(sc->sc_udev);
+
+       if (fps < 8000) {
+               /* FULL speed USB */
+               frames = 8;
+       } else {
+               /* HIGH speed USB */
+               frames = UAUDIO_NFRAMES;
+       }
+
+       fps_shift = usbd_xfer_get_fps_shift(chan->xfer[0]);
+
+       /* down shift number of frames per second, if any */
+       fps >>= fps_shift;
+       frames >>= fps_shift;
+
+       /* bytes per frame should not be zero */
+       chan->bytes_per_frame[0] =
+           ((chan_alt->sample_rate / fps) * chan_alt->sample_size);
+       chan->bytes_per_frame[1] =
+           (((chan_alt->sample_rate + fps - 1) / fps) * chan_alt->sample_size);
+
+       /* setup data rate dithering, if any */
+       chan->frames_per_second = fps;
+       chan->sample_rem = chan_alt->sample_rate % fps;
+       chan->sample_curr = 0;
+       chan->frames_per_second = fps;
+
+       /* compute required buffer size */
+       buf_size = (chan->bytes_per_frame[1] * frames);
+
+       if (buf_size > (chan->end - chan->start)) {
+               DPRINTF("buffer size is too big\n");
+               goto error;
+       }
+
+       chan->intr_frames = frames;
+
+       DPRINTF("fps=%d sample_rem=%d\n", (int)fps, (int)chan->sample_rem);
+
+       if (chan->intr_frames == 0) {
+               DPRINTF("frame shift is too high!\n");
+               goto error;
+       }
+
+       mtx_lock(chan->pcm_mtx);
+       chan->cur_alt = set_alt;
+       mtx_unlock(chan->pcm_mtx);
+
+done:
+#if (UAUDIO_NCHANBUFS != 2)
+#error "please update code"
+#endif
+       switch (operation) {
+       case CHAN_OP_START:
+               mtx_lock(chan->pcm_mtx);
+               usbd_transfer_start(chan->xfer[0]);
+               usbd_transfer_start(chan->xfer[1]);
+               mtx_unlock(chan->pcm_mtx);
+               break;
+       case CHAN_OP_STOP:
+               mtx_lock(chan->pcm_mtx);
+               usbd_transfer_stop(chan->xfer[0]);
+               usbd_transfer_stop(chan->xfer[1]);
+               mtx_unlock(chan->pcm_mtx);
+               break;
+       default:
+               break;
+       }
+       return;
+
+error:
+       usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1);
+
+       mtx_lock(chan->pcm_mtx);
+       chan->cur_alt = CHAN_MAX_ALT;
+       mtx_unlock(chan->pcm_mtx);
+}
+
+static void
+uaudio_configure_msg(struct usb_proc_msg *pm)
+{
+       struct uaudio_softc *sc = ((struct uaudio_configure_msg *)pm)->sc;
+
+       usb_proc_explore_unlock(sc->sc_udev);
+       uaudio_configure_msg_sub(sc, &sc->sc_play_chan, PCMDIR_PLAY);
+       uaudio_configure_msg_sub(sc, &sc->sc_rec_chan, PCMDIR_REC);
+       usb_proc_explore_lock(sc->sc_udev);
+}
+
 /*========================================================================*
  * AS - Audio Stream - routines
  *========================================================================*/
@@ -1237,6 +1466,8 @@ uaudio_chan_fill_info_sub(struct uaudio_
        struct usb_interface_descriptor *id;
        const struct uaudio_format *p_fmt = NULL;
        struct uaudio_chan *chan;
+       struct uaudio_chan_alt *chan_alt;
+       uint32_t format;
        uint16_t curidx = 0xFFFF;
        uint16_t lastidx = 0xFFFF;
        uint16_t alt_index = 0;
@@ -1414,6 +1645,10 @@ uaudio_chan_fill_info_sub(struct uaudio_
                        bBitResolution = asf1d.v2->bBitResolution;
                        bSubslotSize = asf1d.v2->bSubslotSize;
 
+                       /* Map 4-byte aligned 24-bit samples into 32-bit */
+                       if (bBitResolution == 24 && bSubslotSize == 4)
+                               bBitResolution = 32;
+
                        if (bBitResolution != (bSubslotSize * 8)) {
                                DPRINTF("Invalid bSubslotSize\n");
                                goto next_ep;
@@ -1508,14 +1743,19 @@ uaudio_chan_fill_info_sub(struct uaudio_
                chan = (ep_dir == UE_DIR_IN) ?
                    &sc->sc_rec_chan : &sc->sc_play_chan;
 
-               if (chan->valid != 0 ||
-                   usbd_get_iface(udev, curidx) == NULL) {
-                       DPRINTF("Channel already exists or "
-                           "interface is not valid\n");
+               if (usbd_get_iface(udev, curidx) == NULL) {
+                       DPRINTF("Interface is not valid\n");
                        goto next_ep;
                }
+               if (chan->num_alt == CHAN_MAX_ALT) {
+                       DPRINTF("Too many alternate settings\n");
+                       goto next_ep;
+               }
+               chan->set_alt = 0;
+               chan->cur_alt = CHAN_MAX_ALT;
+
+               chan_alt = &chan->usb_alt[chan->num_alt++];
 
-               chan->valid = 1;
 #ifdef USB_DEBUG
                uaudio_chan_dump_ep_desc(ed1);
 #endif
@@ -1523,30 +1763,81 @@ uaudio_chan_fill_info_sub(struct uaudio_
                    "bits = %d, format = %s\n", rate, channels,
                    bit_resolution, p_fmt->description);
 
-               chan->sample_rate = rate;
-               chan->p_asf1d = asf1d;
-               chan->p_ed1 = ed1;
-               chan->p_fmt = p_fmt;
-               chan->p_sed = sed;
-               chan->iface_index = curidx;
-               chan->iface_alt_index = alt_index;
+               chan_alt->sample_rate = rate;
+               chan_alt->p_asf1d = asf1d;
+               chan_alt->p_ed1 = ed1;
+               chan_alt->p_fmt = p_fmt;
+               chan_alt->p_sed = sed;
+               chan_alt->iface_index = curidx;
+               chan_alt->iface_alt_index = alt_index;
+
+               usbd_set_parent_iface(sc->sc_udev, curidx,
+                   sc->sc_mixer_iface_index);
 
                if (ep_dir == UE_DIR_IN)
-                       chan->usb_cfg = uaudio_cfg_record;
+                       chan_alt->usb_cfg = uaudio_cfg_record;
                else
-                       chan->usb_cfg = uaudio_cfg_play;
+                       chan_alt->usb_cfg = uaudio_cfg_play;
 
-               chan->sample_size = (UAUDIO_MAX_CHAN(channels) *
+               chan_alt->sample_size = (UAUDIO_MAX_CHAN(channels) *
                    p_fmt->bPrecision) / 8;
-               chan->channels = channels;
+               chan_alt->channels = channels;
 
                if (ep_dir == UE_DIR_IN &&
                    usbd_get_speed(udev) == USB_SPEED_FULL) {
                        uaudio_record_fix_fs(ed1,
-                           chan->sample_size * (rate / 1000),
-                           chan->sample_size * (rate / 4000));
+                           chan_alt->sample_size * (rate / 1000),
+                           chan_alt->sample_size * (rate / 4000));
                }
 
+               /* setup play/record format */
+
+               format = chan_alt->p_fmt->freebsd_fmt;
+
+               switch (chan_alt->channels) {
+               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, chan_alt->channels, 0));
+                       break;
+               }
+
+               /* check if format is not supported */
+               if (format == 0) {
+                       DPRINTF("The selected audio format is not supported\n");
+                       chan->num_alt--;
+                       goto next_ep;
+               }
+               if (chan->num_alt > 1) {
+                       /* we only accumulate one format at different sample 
rates */
+                       if (chan->pcm_format[0] != format) {
+                               DPRINTF("Multiple formats is not supported\n");
+                               chan->num_alt--;
+                               goto next_ep;
+                       }
+                       /* ignore if duplicate sample rate entry */
+                       if (rate == chan->usb_alt[chan->num_alt - 
2].sample_rate) {
+                               DPRINTF("Duplicate sample rate detected\n");
+                               chan->num_alt--;
+                               goto next_ep;
+                       }
+               }
+               chan->pcm_cap.fmtlist = chan->pcm_format;
+               chan->pcm_cap.fmtlist[0] = format;
+
+               if (rate < chan->pcm_cap.minspeed || chan->pcm_cap.minspeed == 
0)
+                       chan->pcm_cap.minspeed = rate;
+               if (rate > chan->pcm_cap.maxspeed || chan->pcm_cap.maxspeed == 
0)
+                       chan->pcm_cap.maxspeed = rate;
+
                if (sc->sc_sndstat_valid != 0) {
                        sbuf_printf(&sc->sc_sndstat, "\n\t"
                            "mode %d.%d:(%s) %dch, %dbit, %s, %dHz",
@@ -1564,8 +1855,9 @@ uaudio_chan_fill_info_sub(struct uaudio_
 
 /* This structure defines all the supported rates. */
 
-static const uint32_t uaudio_rate_list[] = {
+static const uint32_t uaudio_rate_list[CHAN_MAX_ALT] = {
        96000,
+       88200,
        88000,
        80000,
        72000,
@@ -1630,21 +1922,12 @@ uaudio_chan_fill_info(struct uaudio_soft
                                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++) {
+                       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) {
-                                       goto done;
-                               }
-                       }
                }
        }
-
-done:
-       if (sc->sc_sndstat_valid) {
+       if (sc->sc_sndstat_valid)
                sbuf_finish(&sc->sc_sndstat);
-       }
 }
 
 static void
@@ -1652,6 +1935,7 @@ uaudio_chan_play_sync_callback(struct us
 {
        struct uaudio_chan *ch = usbd_xfer_softc(xfer);
        struct usb_page_cache *pc;
+       uint64_t sample_rate = ch->usb_alt[ch->cur_alt].sample_rate;
        uint8_t buf[4];
        uint64_t temp;
        int len;
@@ -1698,24 +1982,20 @@ uaudio_chan_play_sync_callback(struct us
 
                /* auto adjust */
 
-               while (temp < (ch->sample_rate - (ch->sample_rate / 4)))
+               while (temp < (sample_rate - (sample_rate / 4)))
                        temp *= 2;
 
-               while (temp > (ch->sample_rate + (ch->sample_rate / 2)))
+               while (temp > (sample_rate + (sample_rate / 2)))
                        temp /= 2;
 
-               /* bias */
-
-               temp += (ch->sample_rate + 1999) / 2000;
-
                /* compare */
 
                DPRINTF("Comparing %d < %d\n",
-                   (int)temp, (int)ch->sample_rate);
+                   (int)temp, (int)sample_rate);
 
-               if (temp == ch->sample_rate)
+               if (temp == sample_rate)
                        ch->last_sync_state = UAUDIO_SYNC_NONE;
-               else if (temp > ch->sample_rate)
+               else if (temp > sample_rate)
                        ch->last_sync_state = UAUDIO_SYNC_MORE;
                else
                        ch->last_sync_state = UAUDIO_SYNC_LESS;
@@ -1737,6 +2017,7 @@ uaudio_chan_play_callback(struct usb_xfe
 {
        struct uaudio_chan *ch = usbd_xfer_softc(xfer);
        struct usb_page_cache *pc;
+       uint32_t sample_size = ch->usb_alt[ch->cur_alt].sample_size;
        uint32_t mfl;
        uint32_t total;
        uint32_t blockcount;
@@ -1800,14 +2081,14 @@ tr_transferred:
                                switch (ch->last_sync_state) {
                                case UAUDIO_SYNC_MORE:
                                        DPRINTFN(6, "sending one sample 
more\n");
-                                       if ((frame_len + ch->sample_size) <= 
mfl)
-                                               frame_len += ch->sample_size;
+                                       if ((frame_len + sample_size) <= mfl)
+                                               frame_len += sample_size;
                                        ch->last_sync_state = UAUDIO_SYNC_NONE;
                                        break;
                                case UAUDIO_SYNC_LESS:
                                        DPRINTFN(6, "sending one sample 
less\n");
-                                       if (frame_len >= ch->sample_size)
-                                               frame_len -= ch->sample_size;
+                                       if (frame_len >= sample_size)
+                                               frame_len -= sample_size;
                                        ch->last_sync_state = UAUDIO_SYNC_NONE;
                                        break;
                                default:
@@ -1944,187 +2225,43 @@ uaudio_chan_init(struct uaudio_softc *sc
        struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ?
            &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;
-       uint8_t alt_index;
-       uint8_t fps_shift;
-       usb_error_t err;
-
-       fps = usbd_get_isoc_fps(sc->sc_udev);
-
-       if (fps < 8000) {
-               /* FULL speed USB */
-               frames = 8;
-       } else {
-               /* HIGH speed USB */
-               frames = UAUDIO_NFRAMES;
-       }
-
-       /* setup play/record format */
-
-       ch->pcm_cap.fmtlist = ch->pcm_format;
-
-       ch->pcm_format[0] = 0;
-       ch->pcm_format[1] = 0;
-
-       ch->pcm_cap.minspeed = ch->sample_rate;
-       ch->pcm_cap.maxspeed = ch->sample_rate;
+       uint8_t x;
 
-       /* setup mutex and PCM channel */
+       /* store mutex and PCM channel */
 
        ch->pcm_ch = c;
        ch->pcm_mtx = c->lock;
 
-       format = ch->p_fmt->freebsd_fmt;
-
-       switch (ch->channels) {
-       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->channels, 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;
-       iface_index = ch->iface_index;
-       alt_index = ch->iface_alt_index;
-
-       DPRINTF("endpoint=0x%02x, speed=%d, iface=%d alt=%d\n",
-           endpoint, ch->sample_rate, iface_index, alt_index);
+       /* compute worst case buffer */
 
-       err = usbd_set_alt_interface_index(sc->sc_udev, iface_index, alt_index);
-       if (err) {
-               DPRINTF("setting of alternate index failed: %s!\n",
-                   usbd_errstr(err));
-               goto error;
+       buf_size = 0;
+       for (x = 0; x != ch->num_alt; x++) {
+               uint32_t temp = uaudio_get_buffer_size(ch, x);
+               if (temp > buf_size)
+                       buf_size = temp;
        }
-       usbd_set_parent_iface(sc->sc_udev, iface_index,
-           sc->sc_mixer_iface_index);
-
-       /*
-        * Only set the sample rate if the channel reports that it
-        * supports the frequency control.
-        */
 
-       if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
-               /* FALLTHROUGH */
-       } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
-               unsigned int x;
-         
-               for (x = 0; x != 256; x++) {
-                       if (dir == PCMDIR_PLAY) {
-                               if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
-                                   (1 << (x % 8)))) {
-                                       continue;
-                               }
-                       } else {
-                               if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
-                                   (1 << (x % 8)))) {
-                                       continue;
-                               }
-                       }
-
-                       if (uaudio20_set_speed(sc->sc_udev,
-                           sc->sc_mixer_iface_no, x, ch->sample_rate)) {
-                               /*
-                                * If the endpoint is adaptive setting
-                                * the speed may fail.
-                                */
-                               DPRINTF("setting of sample rate failed! "
-                                   "(continuing anyway)\n");
-                       }
-               }
-       } else if (ch->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) {
-               if (uaudio_set_speed(sc->sc_udev, endpoint, ch->sample_rate)) {
-                       /*
-                        * If the endpoint is adaptive setting the
-                        * speed may fail.
-                        */
-                       DPRINTF("setting of sample rate failed! "
-                           "(continuing anyway)\n");
-               }
-       }
-       if (usbd_transfer_setup(sc->sc_udev, &iface_index, ch->xfer,
-           ch->usb_cfg, UAUDIO_NCHANBUFS + 1, ch, ch->pcm_mtx)) {
-               DPRINTF("could not allocate USB transfers!\n");
-               goto error;
-       }
-
-       fps_shift = usbd_xfer_get_fps_shift(ch->xfer[0]);
-
-       /* 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;
-
-       DPRINTF("fps=%d sample_rem=%d\n", fps, ch->sample_rem);
-
-       if (ch->intr_frames == 0) {
-               DPRINTF("frame shift is too high!\n");
-               goto error;
-       }
-
-       /* setup double buffering */
+       /* allow double buffering */
        buf_size *= 2;
-       blocks = 2;
+
+       DPRINTF("Worst case buffer is %d bytes\n", (int)buf_size);
 
        ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO);
        if (ch->buf == NULL)
                goto error;
        if (sndbuf_setup(b, ch->buf, buf_size) != 0)
                goto error;
-       if (sndbuf_resize(b, blocks, ch->intr_size)) 
-               goto error;
 
        ch->start = ch->buf;
        ch->end = ch->buf + buf_size;
        ch->cur = ch->buf;
        ch->pcm_buf = b;
+       ch->max_buf = buf_size;
 
        if (ch->pcm_mtx == NULL) {
                DPRINTF("ERROR: PCM channels does not have a mutex!\n");
                goto error;
        }
-
        return (ch);
 
 error:
@@ -2141,7 +2278,7 @@ uaudio_chan_free(struct uaudio_chan *ch)
        }
        usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS + 1);
 
-       ch->valid = 0;
+       ch->num_alt = 0;
 
        return (0);
 }
@@ -2149,7 +2286,15 @@ uaudio_chan_free(struct uaudio_chan *ch)
 int
 uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize)
 {
-       return (ch->intr_size);
+       uint32_t temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
+
+       sndbuf_setup(ch->pcm_buf, ch->buf, temp);
+
+       ch->start = ch->buf;
+       ch->end = ch->buf + temp;
+       ch->cur = ch->buf;
+
+       return (temp / 2);
 }
 
 int
@@ -2162,10 +2307,23 @@ uaudio_chan_set_param_fragments(struct u
 int
 uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed)
 {
-       if (speed != ch->sample_rate) {
-               DPRINTF("rate conversion required\n");
+       uint8_t x;
+
+       for (x = 0; x < ch->num_alt; x++) {
+               if (ch->usb_alt[x].sample_rate < speed) {
+                       /* sample rate is too low */
+                       break;
+               }
        }
-       return (ch->sample_rate);
+
+       if (x != 0)
+               x--;
+
+       ch->set_alt = x;
+
+       DPRINTF("Selecting alt %d\n", (int)x);
+
+       return (ch->usb_alt[x].sample_rate);
 }
 
 int
@@ -2228,31 +2386,61 @@ uaudio_chan_getmatrix(struct uaudio_chan
 int
 uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format)
 {
-       ch->format = format;
+       DPRINTF("Selecting format 0x%08x\n", (unsigned int)format);
        return (0);
 }
 
 int
 uaudio_chan_start(struct uaudio_chan *ch)
 {
-       ch->cur = ch->start;
+       struct uaudio_softc *sc = ch->priv_sc;
+       int do_start = 0;
 
-#if (UAUDIO_NCHANBUFS != 2)
-#error "please update code"
-#endif
-       usbd_transfer_start(ch->xfer[0]);
-       usbd_transfer_start(ch->xfer[1]);
+       usb_proc_explore_lock(sc->sc_udev);
+       if (ch->operation != CHAN_OP_DRAIN) {
+               if (ch->cur_alt == ch->set_alt &&
+                   ch->operation == CHAN_OP_NONE) {
+                       /* save doing the explore task */
+                       do_start = 1;
+               } else {
+                       ch->operation = CHAN_OP_START;
+                       (void)usb_proc_explore_msignal(sc->sc_udev,
+                           &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
+               }
+       }
+       usb_proc_explore_unlock(sc->sc_udev);
+
+       if (do_start) {
+               usbd_transfer_start(ch->xfer[0]);
+               usbd_transfer_start(ch->xfer[1]);
+       }
        return (0);
 }
 
 int
 uaudio_chan_stop(struct uaudio_chan *ch)
 {
-#if (UAUDIO_NCHANBUFS != 2)
-#error "please update code"
-#endif
-       usbd_transfer_stop(ch->xfer[0]);
-       usbd_transfer_stop(ch->xfer[1]);
+       struct uaudio_softc *sc = ch->priv_sc;
+       int do_stop = 0;
+
+       usb_proc_explore_lock(sc->sc_udev);
+       if (ch->operation != CHAN_OP_DRAIN) {
+               if (ch->cur_alt == ch->set_alt &&
+                   ch->operation == CHAN_OP_NONE) {
+                       /* save doing the explore task */
+                       do_stop = 1;
+               } else {
+                       ch->operation = CHAN_OP_STOP;
+                       (void)usb_proc_explore_msignal(sc->sc_udev,
+                           &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
+               }
+       }
+       usb_proc_explore_unlock(sc->sc_udev);
+
+       if (do_stop) {
+               usbd_transfer_stop(ch->xfer[0]);
+               usbd_transfer_stop(ch->xfer[1]);
+       }
        return (0);
 }
 
_______________________________________________
svn-src-stable-9@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-stable-9
To unsubscribe, send any mail to "svn-src-stable-9-unsubscr...@freebsd.org"

Reply via email to