Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 12 May 2013 12:30:42 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r250563 - stable/8/sys/dev/sound/usb
Message-ID:  <201305121230.r4CCUg6O095204@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Sun May 12 12:30:42 2013
New Revision: 250563
URL: http://svnweb.freebsd.org/changeset/base/250563

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

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

Modified: stable/8/sys/dev/sound/usb/uaudio.c
==============================================================================
--- stable/8/sys/dev/sound/usb/uaudio.c	Sun May 12 12:24:29 2013	(r250562)
+++ stable/8/sys/dev/sound/usb/uaudio.c	Sun May 12 12:30:42 2013	(r250563)
@@ -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);
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201305121230.r4CCUg6O095204>