From owner-svn-src-stable-9@FreeBSD.ORG Sun May 12 12:20:04 2013 Return-Path: Delivered-To: svn-src-stable-9@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id E223D794; Sun, 12 May 2013 12:20:04 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id B4DCC942; Sun, 12 May 2013 12:20:04 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.6/8.14.6) with ESMTP id r4CCK4l5089965; Sun, 12 May 2013 12:20:04 GMT (envelope-from hselasky@svn.freebsd.org) Received: (from hselasky@localhost) by svn.freebsd.org (8.14.6/8.14.5/Submit) id r4CCK4B6089964; Sun, 12 May 2013 12:20:04 GMT (envelope-from hselasky@svn.freebsd.org) Message-Id: <201305121220.r4CCK4B6089964@svn.freebsd.org> From: Hans Petter Selasky Date: Sun, 12 May 2013 12:20:04 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org Subject: svn commit: r250561 - stable/9/sys/dev/sound/usb X-SVN-Group: stable-9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-9@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for only the 9-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 May 2013 12:20:05 -0000 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 #include #include +#include #define USB_DEBUG_VAR uaudio_debug #include @@ -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); }