[alsa-devel] How to use implicit feedback with full duplex?

Eldad Zack eldad at fogrefinery.com
Sun Mar 24 23:49:54 CET 2013


Hi Clemens,

On Wed, 6 Feb 2013, Clemens Ladisch wrote:

> Eldad Zack wrote:
> > On Tue, 5 Feb 2013, Clemens Ladisch wrote:
> >> I thought I'd try to use implicit feedback with my simple audio device:
> >> [...]
> >> This works fine when playing something:
> > ...
> >> But when I then try to record at the same time, the driver refuses to
> >> configure the input endpoint (to the only format, which is already set):
> >> And despite that "alreay in use" check, the input endpoint is affected
> >> so much that playback breaks.
> >>
> >> Is full duplex supposed to work?  Does it work with other devices?
> >
> > This is probably a "yes, but" :)
> > I use my device mostly full duplex, but with jack opening both
> > playback and capture at the same time.
> >
> > I assume you are opening two different streams, one for playback and
> > one for capture.
> 
> Jack *also* uses two different streams, but it opens them at the same
> time.
> 
> > Can you try using jackd -d alsa -d hw:x with the device and see if that
> > works for you?
> 
> That works.  This means that there is a race condition in the driver, or
> that the different open/hw_params/prepare order trips it up.

Yes, and also on closing (close/hw_free).
I finally had some time to look into this - patch below adds some 
checks and with it:
    
* Starting playback, waiting, starting capture: capture doesn't start,
  playback continues without breaking.
    
* Starting capture, waiting, starting playback: playback doesn't start,
  capture breaks - but it is possible to restart the streams afterwards.
  [On my setup (with xhci), when the streams break I must restart my box
  to get them to work again with current code].

* Starting both - no change, works good.

This is the order the ops get called when both are start at once, if it 
helps anyone:

playback	capture
open
		open
		hw_params
		 set_format
 		prepare
 		 set_format
hw_params
 set_format
prepare
 set_format
prepare
 set_format
		prepare
		 set_format
 trigger
		trigger

I'll try to figure out why capture breaks next.
I still quite slow around the code and don't understand some parts of 
it so this might take me a while.

@Daniel, do you have any hints for this case (capture breaking
when starting playback)?

I can also submit this as a proper patch if it makes sense.

Cheers,
Eldad
    
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 7e9c55a..7cdf9b3 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -934,12 +934,12 @@ int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
 	if (!ep)
 		return -EINVAL;
 
-	deactivate_urbs(ep, true);
-	wait_clear_urbs(ep);
-
 	if (ep->use_count != 0)
 		return 0;
 
+	deactivate_urbs(ep, true);
+	wait_clear_urbs(ep);
+
 	clear_bit(EP_FLAG_ACTIVATED, &ep->flags);
 
 	return 0;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 9531355..76d7e18 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -323,6 +323,19 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 	if (fmt == subs->cur_audiofmt)
 		return 0;
 
+	subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+						   alts, fmt->endpoint, subs->direction,
+						   SND_USB_ENDPOINT_TYPE_DATA);
+	if (!subs->data_endpoint)
+		return -EINVAL;
+
+	if (subs->data_endpoint->use_count != 0) {
+		snd_printk("set_format ep use count !0\n");
+		subs->cur_audiofmt = fmt;
+		snd_usb_set_format_quirk(subs, fmt);
+		return 0;
+	}
+
 	/* close the old interface */
 	if (subs->interface >= 0 && subs->interface != fmt->iface) {
 		err = usb_set_interface(subs->dev, subs->interface, 0);
@@ -350,12 +363,6 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 		subs->altset_idx = fmt->altset_idx;
 	}
 
-	subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
-						   alts, fmt->endpoint, subs->direction,
-						   SND_USB_ENDPOINT_TYPE_DATA);
-	if (!subs->data_endpoint)
-		return -EINVAL;
-
 	/* we need a sync pipe in async OUT or adaptive IN mode */
 	/* check the number of EP, since some devices have broken
 	 * descriptors which fool us.  if it has only one EP,
@@ -1128,7 +1135,8 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
 
 	stop_endpoints(subs, true);
 
-	if (!as->chip->shutdown && subs->interface >= 0) {
+	if (!as->chip->shutdown && subs->interface >= 0 &&
+	    subs->data_endpoint->use_count == 0) {
 		usb_set_interface(subs->dev, subs->interface, 0);
 		subs->interface = -1;
 	}
 


More information about the Alsa-devel mailing list