[alsa-devel] M-Audio FTU issues

Juan Pablo Bouza jpbouza at hotmail.com
Thu Jul 21 07:08:47 CEST 2011


Hi! I've patched kernel 3 rc7 and playback does not work...

Capture works perfectly fine.

Playback does not work with alsa apps  or with jack. 

 This is my /proc/asound/cards output

 0 [SB             ]: HDA-Intel - HDA ATI SB
                      HDA ATI SB at 0xf9ff4000 irq 16
 1 [Ultra          ]: USB-Audio - Fast Track Ultra
                      M-Audio Fast Track Ultra at usb-0000:00:13.5-1, high speed


This is what happens when trying to playback a sound file with aplay -D plughw:Ultra /media/Edipo/Triangulo/Hadas\ por\ ahora.mp3 

Playing raw data '/media/Edipo/Triangulo/Hadas por ahora.mp3' : Unsigned 8 bit, Rate 8000 Hz, Mono
aplay: set_params:1137: Unable to install hw params:
ACCESS:  RW_INTERLEAVED
FORMAT:  U8
SUBFORMAT:  STD
SAMPLE_BITS: 8
FRAME_BITS: 8
CHANNELS: 1
RATE: 8000
PERIOD_TIME: 125000
PERIOD_SIZE: 1000
PERIOD_BYTES: 1000
PERIODS: 4
BUFFER_TIME: 500000
BUFFER_SIZE: 4000
BUFFER_BYTES: 4000
TICK_TIME: 0

Is there any extra option I need to activate in the kernel in order to make playback work?

I compiled the kernel without the patch and I do have playback, so my conclusion is that the problem is with the patch applied

Thanks!

PS: just to make sure, this is the patch I applied:

diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index 0fd3fbd..0f138de 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -377,6 +377,11 @@ struct usb_endpoint_descriptor {
 #define USB_ENDPOINT_NUMBER_MASK    0x0f    /* in bEndpointAddress */
 #define USB_ENDPOINT_DIR_MASK        0x80
 
+#define USB_ENDPOINT_USAGE_MASK        0x30
+#define USB_ENDPOINT_USAGE_DATA        0x00
+#define USB_ENDPOINT_USAGE_FEEDBACK    0x10
+#define USB_ENDPOINT_USAGE_IMPLICIT_FB    0x20    /* Implicit feedback Data endpoint */
+
 #define USB_ENDPOINT_SYNCTYPE        0x0c
 #define USB_ENDPOINT_SYNC_NONE        (0 << 2)
 #define USB_ENDPOINT_SYNC_ASYNC        (1 << 2)
diff --git a/sound/usb/card.h b/sound/usb/card.h
index ae4251d..b9ba132 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -3,7 +3,7 @@
 
 #define MAX_PACKS    20
 #define MAX_PACKS_HS    (MAX_PACKS * 8)    /* in high speed mode */
-#define MAX_URBS    8
+#define MAX_URBS    16
 #define SYNC_URBS    4    /* always four urbs for sync */
 #define MAX_QUEUE    24    /* try not to exceed this queue length, in ms */
 
@@ -36,9 +36,11 @@ struct snd_urb_ctx {
     struct snd_usb_substream *subs;
     int index;    /* index for urb array */
     int packets;    /* number of packets per urb */
+    int packet_size[MAX_PACKS_HS]; /* size of packets for next submission */
 };
 
 struct snd_urb_ops {
+    int (*prepare_size)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
     int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
     int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
     int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
@@ -73,6 +75,8 @@ struct snd_usb_substream {
     unsigned int fill_max: 1;    /* fill max packet size always */
     unsigned int txfr_quirk:1;    /* allow sub-frame alignment */
     unsigned int fmt_type;        /* USB audio format type (1-3) */
+    unsigned int next_out_urb;    /* index of next URB to use for output
+                       (implicit feedback mode only) */
 
     unsigned int running: 1;    /* running status */
 
@@ -103,6 +107,8 @@ struct snd_usb_stream {
     unsigned int fmt_type;        /* USB audio format type (1-3) */
     struct snd_usb_substream substream[2];
     struct list_head list;
+    unsigned int implicit_feedback: 1;     /* stream uses its capture data
+                           substream to clock its playback substream */
 };
 
 #endif /* __USBAUDIO_CARD_H */
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index b8dcbf4..1cd1db2 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -185,7 +185,8 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
 /*
  * find a matching format and set up the interface
  */
-static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
+int snd_usb_set_pcm_format(struct snd_usb_substream *subs,
+               struct audioformat *fmt)
 {
     struct usb_device *dev = subs->dev;
     struct usb_host_interface *alts;
@@ -206,6 +207,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
     if (fmt == subs->cur_audiofmt)
         return 0;
 
+printk(KERN_ERR "%s() :%d\n", __func__, __LINE__);
+
     /* close the old interface */
     if (subs->interface >= 0 && subs->interface != fmt->iface) {
         if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
@@ -292,6 +295,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
     if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
         subs->fill_max = 1;
 
+    if (!is_playback &&
+        (fmt->ep_attr & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC &&
+        (fmt->ep_attr & USB_ENDPOINT_USAGE_MASK) == USB_ENDPOINT_USAGE_IMPLICIT_FB)
+        subs->stream->implicit_feedback = 1;
+
     if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0)
         return err;
 
@@ -299,6 +307,9 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 
     snd_usb_set_format_quirk(subs, fmt);
 
+    if (subs->stream->implicit_feedback)
+        snd_printk(KERN_INFO "operating in implicit feedback mode\n");
+
 #if 0
     printk(KERN_DEBUG
            "setting done: format = %d, rate = %d..%d, channels = %d\n",
@@ -347,7 +358,16 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
     changed = subs->cur_audiofmt != fmt ||
         subs->period_bytes != params_period_bytes(hw_params) ||
         subs->cur_rate != rate;
-    if ((ret = set_format(subs, fmt)) < 0)
+
+    if (subs->stream->implicit_feedback) {
+        struct snd_usb_substream *other =
+            &subs->stream->substream[!subs->direction];
+
+        if (other && other->running && changed)
+            return -EBUSY;
+    }
+
+    if ((ret = snd_usb_set_pcm_format(subs, fmt)) < 0)
         return ret;
 
     if (subs->cur_rate != rate) {
@@ -815,6 +835,9 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
     struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
     struct snd_usb_substream *subs = &as->substream[direction];
 
+    if (snd_usb_capture_subs_used_by_playback(subs))
+        return 0;
+
     if (!as->chip->shutdown && subs->interface >= 0) {
         usb_set_interface(subs->dev, subs->interface, 0);
         subs->interface = -1;
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
index ed3e283..9e02cf8 100644
--- a/sound/usb/pcm.h
+++ b/sound/usb/pcm.h
@@ -3,6 +3,9 @@
 
 void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
 
+int snd_usb_set_pcm_format(struct snd_usb_substream *subs,
+               struct audioformat *fmt);
+
 int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
                struct usb_host_interface *alts,
                struct audioformat *fmt);
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 090e193..1353663 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -644,6 +644,10 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
     case USB_ID(0x041e, 0x3f19): /* E-Mu 0204 USB */
         set_format_emu_quirk(subs, fmt);
         break;
+    case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
+    case USB_ID(0x0763, 0x2081):
+        subs->stream->implicit_feedback = 1;
+        break;
     }
 }
 
diff --git a/sound/usb/urb.c b/sound/usb/urb.c
index e184349..3b7a81b 100644
--- a/sound/usb/urb.c
+++ b/sound/usb/urb.c
@@ -29,6 +29,25 @@
 #include "urb.h"
 #include "pcm.h"
 
+/* Streaming modes
+ *
+ * This driver can operate in different streaming modes which use the internal
+ * functions of this file differently.
+ *
+ * One is independent input and output streaming which is the default and most
+ * usual for USB audio devices. IN endpoints (device sinks) can either take any
+ * amount of data that is sent or provide a sync endpoint to tell the host at
+ * which data rate the stream runs at. This streaming model allows playback and
+ * capture streams to be started independently.
+ *
+ * The other is an 'implicit feedback mode' in which the device does not tell
+ * the host its speed through an sync endpoint but expects the driver to always
+ * receive the record stream and use the data rate to determine the actual
+ * device speed. For the USB side, this means that the driver has to queue
+ * capture URBs even if the userspace is not interested in actual capture data.
+ *
+ */
+
 /*
  * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
  * this will overflow at approx 524 kHz
@@ -135,10 +154,26 @@ static int wait_clear_urbs(struct snd_usb_substream *subs)
         schedule_timeout_uninterruptible(1);
     } while (time_before(jiffies, end_time));
     if (alive)
-        snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
+        snd_printk(KERN_ERR "timeout: still %d active urbs (%s)\n",
+               alive, subs->direction == SNDRV_PCM_STREAM_CAPTURE ?
+                        "capture" : "playback");
     return 0;
 }
 
+int snd_usb_capture_subs_used_by_playback(struct snd_usb_substream *capture)
+{
+    struct snd_usb_stream *stream = capture->stream;
+    struct snd_usb_substream *playback = &stream->substream[SNDRV_PCM_STREAM_PLAYBACK];
+
+    if (!playback)
+        return 0;
+
+    if (capture->direction != SNDRV_PCM_STREAM_CAPTURE)
+        return 0;
+
+    return stream->implicit_feedback && playback->running;
+}
+
 /*
  * release a substream
  */
@@ -146,6 +181,9 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
 {
     int i;
 
+    if (snd_usb_capture_subs_used_by_playback(subs))
+        return;
+
     /* stop urbs (to be sure) */
     deactivate_urbs(subs, force, 1);
     wait_clear_urbs(subs);
@@ -168,17 +206,34 @@ static void snd_complete_urb(struct urb *urb)
     struct snd_urb_ctx *ctx = urb->context;
     struct snd_usb_substream *subs = ctx->subs;
     struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
+    int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
     int err = 0;
 
     if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
-        !subs->running || /* can be stopped during retire callback */
-        (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
-        (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-        clear_bit(ctx->index, &subs->active_mask);
-        if (err < 0) {
-            snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
-            snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-        }
+        !subs->running /* can be stopped during retire callback */)
+        goto exit_clear_bit;
+
+    /*
+     * For devices that operate in implicit feedback mode, we won't requeue
+     * the URB at this point. Instead, we will wait for the next record
+     * input packet to arrive and queue a packet from the corresponding
+     * retire callback.
+     */
+    if (is_playback && subs->stream->implicit_feedback)
+        goto exit_clear_bit;
+
+    if ((err = subs->ops.prepare(subs, substream->runtime, urb)) == 0 &&
+        (err = subs->ops.prepare_size(subs, substream->runtime, urb)) == 0 &&
+        (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0)
+        return;
+
+    /* fall through */
+
+exit_clear_bit:
+    clear_bit(ctx->index, &subs->active_mask);
+    if (err < 0) {
+        snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
+        snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
     }
 }
 
@@ -250,7 +305,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
     else
         packs_per_ms = 1;
 
-    if (is_playback) {
+    if (is_playback && !subs->stream->implicit_feedback) {
         urb_packs = max(chip->nrpacks, 1);
         urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
     } else
@@ -260,7 +315,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
         urb_packs = min(urb_packs, 1U << subs->syncinterval);
 
     /* decide how many packets to be used */
-    if (is_playback) {
+    if (is_playback && !subs->stream->implicit_feedback) {
         unsigned int minsize, maxpacks;
         /* determine how small a packet can be */
         minsize = (subs->freqn >> (16 - subs->datainterval))
@@ -320,6 +375,9 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
         u->urb->complete = snd_complete_urb;
     }
 
+    if (subs->stream->implicit_feedback)
+        subs->syncpipe = 0;
+
     if (subs->syncpipe) {
         /* allocate and initialize sync urbs */
         subs->syncbuf = usb_alloc_coherent(subs->dev, SYNC_URBS * 4,
@@ -410,6 +468,15 @@ static int retire_capture_sync_urb(struct snd_usb_substream *subs,
 }
 
 /*
+ * prepare data sizes for caputure urbs. nothing to do. */
+static int prepare_capture_size_urb(struct snd_usb_substream *subs,
+                    struct snd_pcm_runtime *runtime,
+                    struct urb *urb)
+{
+    return 0;
+}
+
+/*
  * prepare urb for capture data pipe
  *
  * fill the offset and length of each descriptor.
@@ -438,11 +505,93 @@ static int prepare_capture_urb(struct snd_usb_substream *subs,
     return 0;
 }
 
+static int queue_next_playback_urb(struct snd_usb_substream *capture,
+                   struct snd_pcm_runtime *runtime,
+                   struct urb *in_urb)
+{
+    unsigned int stride = runtime->frame_bits >> 3;
+    unsigned int bytes = 0, i;
+    unsigned long flags;
+    struct snd_urb_ctx *in_ctx = in_urb->context;
+    struct snd_usb_substream *playback =
+        &capture->stream->substream[SNDRV_PCM_STREAM_PLAYBACK];
+    struct snd_urb_ctx *out_ctx;
+    struct urb *out_urb;
+    int err;
+
+    /* this may happen during substream transitions */
+    if (stride == 0)
+        return 0;
+
+    if (!playback->running)
+        return 0;
+
+    /* Count overall packet size */
+    for (i = 0; i < in_ctx->packets; i++)
+        if (in_urb->iso_frame_desc[i].status == 0)
+            bytes += in_urb->iso_frame_desc[i].actual_length;
+
+    /* skip empty packets. At least M-Audio's Fast Track Ultra stops
+     * streaming once it received a 0-byte OUT URB */
+    if (bytes == 0)
+        return 0;
+
+    spin_lock_irqsave(&capture->lock, flags);
+    out_ctx = &playback->dataurb[playback->next_out_urb];
+    spin_unlock_irqrestore(&capture->lock, flags);
+
+    out_urb = out_ctx->urb;
+
+    /* If the output urb is still in use, we have to drop this packet.
+     * This usually only occurs when the stream is starting */
+    if (test_bit(out_ctx->index, &playback->active_mask))
+        return 0;
+
+    /*
+     * Iterate through the inbound packet and prepare the lengths
+     * for output packets. The OUT packet we are about to send will
+     * have the same amount of payload than the IN packet we just
+     * received.
+     */
+
+    out_ctx->packets = in_ctx->packets;
+    for (i = 0; i < in_ctx->packets; i++) {
+        if (in_urb->iso_frame_desc[i].status == 0)
+            out_ctx->packet_size[i] =
+                in_urb->iso_frame_desc[i].actual_length / stride;
+        else
+            out_ctx->packet_size[i] = 0;
+    }
+
+    err = playback->ops.prepare(playback, playback->pcm_substream->runtime, out_urb);
+    if (err < 0)
+        return err;
+
+    err = usb_submit_urb(out_urb, GFP_ATOMIC);
+    if (err < 0) {
+        snd_printk(KERN_ERR "%s(): Unable to submit urb #%d: %d\n",
+                __func__, out_ctx->index, err);
+        snd_pcm_stop(capture->pcm_substream, SNDRV_PCM_STATE_XRUN);
+        snd_pcm_stop(playback->pcm_substream, SNDRV_PCM_STATE_XRUN);
+        return err;
+    } else {
+        spin_lock_irqsave(&capture->lock, flags);
+        set_bit(out_ctx->index, &playback->active_mask);
+        playback->next_out_urb++;
+        playback->next_out_urb %= playback->nurbs;
+        spin_unlock_irqrestore(&capture->lock, flags);
+    }
+
+    return 0;
+}
+
 /*
  * process after capture complete
  *
  * copy the data from each desctiptor to the pcm buffer, and
  * update the current position.
+ *
+ * For implicit feedback mode, eventually call queue_next_playback_urb().
  */
 static int retire_capture_urb(struct snd_usb_substream *subs,
                   struct snd_pcm_runtime *runtime,
@@ -499,16 +648,24 @@ static int retire_capture_urb(struct snd_usb_substream *subs,
     }
     if (period_elapsed)
         snd_pcm_period_elapsed(subs->pcm_substream);
+
+    if (subs->stream->implicit_feedback)
+        return queue_next_playback_urb(subs, runtime, urb);
+
     return 0;
 }
 
 /*
- * Process after capture complete when paused.  Nothing to do.
+ * Process after capture complete when paused.
+ * For implicit feedback mode, eventually call queue_next_playback_urb().
  */
 static int retire_paused_capture_urb(struct snd_usb_substream *subs,
                      struct snd_pcm_runtime *runtime,
                      struct urb *urb)
 {
+    if (subs->stream->implicit_feedback)
+        return queue_next_playback_urb(subs, runtime, urb);
+
     return 0;
 }
 
@@ -615,6 +772,25 @@ static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
 }
 
 /*
+ * Prepare urb data sizes for asynchronous output streaming.
+ * This is seperated from the code that fills the data because syncronous
+ * streaming (in implicit feedback mode) uses a different method to determine
+ * the output data packet sizes.
+ */
+static int prepare_playback_size_urb(struct snd_usb_substream *subs,
+                     struct snd_pcm_runtime *runtime,
+                     struct urb *urb)
+{
+    int i;
+    struct snd_urb_ctx *ctx = urb->context;
+
+    for (i = 0; i < ctx->packets; ++i)
+        ctx->packet_size[i] = snd_usb_audio_next_packet_size(subs);
+
+    return 0;
+}
+
+/*
  * Prepare urb for streaming before playback starts or when paused.
  *
  * We don't have any data, so we send silence.
@@ -630,7 +806,7 @@ static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
     offs = 0;
     urb->dev = ctx->subs->dev;
     for (i = 0; i < ctx->packets; ++i) {
-        counts = snd_usb_audio_next_packet_size(subs);
+        counts = ctx->packet_size[i];
         urb->iso_frame_desc[i].offset = offs * stride;
         urb->iso_frame_desc[i].length = counts * stride;
         offs += counts;
@@ -668,7 +844,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
     urb->number_of_packets = 0;
     spin_lock_irqsave(&subs->lock, flags);
     for (i = 0; i < ctx->packets; i++) {
-        counts = snd_usb_audio_next_packet_size(subs);
+        counts = ctx->packet_size[i];
         /* set up descriptor */
         urb->iso_frame_desc[i].offset = frames * stride;
         urb->iso_frame_desc[i].length = counts * stride;
@@ -786,6 +962,10 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru
     for (i = 0; i < subs->nurbs; i++) {
         if (snd_BUG_ON(!subs->dataurb[i].urb))
             return -EINVAL;
+        if (subs->ops.prepare_size(subs, runtime, subs->dataurb[i].urb) < 0) {
+            snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
+            goto __error;
+        }
         if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
             snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
             goto __error;
@@ -805,6 +985,7 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru
     subs->active_mask = 0;
     subs->unlink_mask = 0;
     subs->running = 1;
+
     for (i = 0; i < subs->nurbs; i++) {
         err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
         if (err < 0) {
@@ -840,12 +1021,14 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru
  */
 static struct snd_urb_ops audio_urb_ops[2] = {
     {
+        .prepare_size =    prepare_playback_size_urb,
         .prepare =    prepare_nodata_playback_urb,
         .retire =    retire_playback_urb,
         .prepare_sync =    prepare_playback_sync_urb,
         .retire_sync =    retire_playback_sync_urb,
     },
     {
+        .prepare_size =    prepare_capture_size_urb,
         .prepare =    prepare_capture_urb,
         .retire =    retire_capture_urb,
         .prepare_sync =    prepare_capture_sync_urb,
@@ -892,6 +1075,18 @@ int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int
         subs->ops.prepare = prepare_playback_urb;
         return 0;
     case SNDRV_PCM_TRIGGER_STOP:
+        if (subs->stream->implicit_feedback) {
+            struct snd_usb_substream *capture =
+                &subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE];
+            if (capture &&
+                capture->pcm_substream &&
+                capture->pcm_substream->runtime) {
+                struct snd_pcm_runtime *runtime =
+                    capture->pcm_substream->runtime;
+                if (runtime->status->state != SNDRV_PCM_STATE_OPEN)
+                    deactivate_urbs(capture, 0, 0);
+            }
+        }
         return deactivate_urbs(subs, 0, 0);
     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
         subs->ops.prepare = prepare_nodata_playback_urb;
@@ -908,8 +1103,15 @@ int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int c
     switch (cmd) {
     case SNDRV_PCM_TRIGGER_START:
         subs->ops.retire = retire_capture_urb;
-        return start_urbs(subs, substream->runtime);
+        if (!subs->running)
+            return start_urbs(subs, substream->runtime);
+
+        return 0;
     case SNDRV_PCM_TRIGGER_STOP:
+        if (snd_usb_capture_subs_used_by_playback(subs)) {
+            subs->ops.retire = retire_paused_capture_urb;
+            return 0;
+        }
         return deactivate_urbs(subs, 0, 0);
     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
         subs->ops.retire = retire_paused_capture_urb;
@@ -922,18 +1124,90 @@ int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int c
     return -EINVAL;
 }
 
+/*
+ * Fake a start for the capture stream.
+ * Only used for implicit feedback mode.
+ */
+static int start_capture_stream(struct snd_usb_substream *playback,
+                struct snd_usb_substream *capture,
+                struct snd_pcm_runtime *runtime)
+{
+    int err;
+
+
+usb_set_interface(capture->dev, 2, 1);
+
+    capture->ops.prepare = prepare_capture_urb;
+    capture->ops.retire = retire_paused_capture_urb;
+    capture->pcm_substream = playback->pcm_substream;
+    err = snd_usb_set_pcm_format(capture, playback->cur_audiofmt);
+    if (err)
+        return err;
+
+    err = snd_usb_init_substream_urbs(capture, playback->period_bytes,
+                      playback->cur_rate,
+                      (playback->curpacksize /
+                       playback->curframesize) * 8);
+    if (err)
+        return err;
+
+    return start_urbs(capture, runtime);
+}
+
 int snd_usb_substream_prepare(struct snd_usb_substream *subs,
                   struct snd_pcm_runtime *runtime)
 {
     /* clear urbs (to be sure) */
-    deactivate_urbs(subs, 0, 1);
-    wait_clear_urbs(subs);
+    if (!snd_usb_capture_subs_used_by_playback(subs)) {
+        deactivate_urbs(subs, 0, 1);
+        wait_clear_urbs(subs);
+    }
 
-    /* for playback, submit the URBs now; otherwise, the first hwptr_done
-     * updates for all URBs would happen at the same time when starting */
     if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
         subs->ops.prepare = prepare_nodata_playback_urb;
-        return start_urbs(subs, runtime);
+
+        if (subs->stream->implicit_feedback) {
+            struct snd_usb_substream *capture =
+                &subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE];
+
+            if (!capture) {
+                snd_printk(KERN_ERR "Unable to start implicit feedback mode "
+                       "without capture stream\n");
+                return -EINVAL;
+            }
+
+            /*
+             * Mark the stream running without calling start_urbs().
+             * We don't want to queue any URBs yet at this point
+             */
+            subs->active_mask = 0;
+            subs->unlink_mask = 0;
+            subs->running = 1;
+
+            /*
+             * if this stream is in implicit feedback mode, start the
+             * capture stream now as the playback stream relies on the
+             * amount of data we see on the capture IN endpoint.
+             */
+            if (!capture->running) {
+                int err = start_capture_stream(subs, capture, runtime);
+                if (err < 0) {
+                    deactivate_urbs(subs, 0, 0);
+                    return err;
+                }
+            }
+        } else {
+            /*
+             * For playback, submit the URBs now; otherwise, the
+             * first hwptr_done updates for all URBs would happen
+             * at the same time when starting.
+             * However, don't start the stream here if we are in
+             * implicit feedback stream mode - all OUT URBs will
+             * be queued once data is received on the IN endpooint
+             * in this case.
+             */
+            return start_urbs(subs, runtime);
+        }
     }
 
     return 0;
diff --git a/sound/usb/urb.h b/sound/usb/urb.h
index 888da38..593a9e1 100644
--- a/sound/usb/urb.h
+++ b/sound/usb/urb.h
@@ -17,5 +17,6 @@ int snd_usb_substream_prepare(struct snd_usb_substream *subs,
 
 int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd);
 int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd);
+int snd_usb_capture_subs_used_by_playback(struct snd_usb_substream *substream);
 
 #endif /* __USBAUDIO_URB_H */

 		 	   		  


More information about the Alsa-devel mailing list