All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] snd-usb endpoint rework, take 5
@ 2011-12-20 23:34 Daniel Mack
  2011-12-20 23:34 ` [PATCH 1/5] ALSA: snd-usb: add snd_usb_audio-wide mutex Daniel Mack
                   ` (4 more replies)
  0 siblings, 5 replies; 20+ messages in thread
From: Daniel Mack @ 2011-12-20 23:34 UTC (permalink / raw)
  To: alsa-devel; +Cc: gdiffey, tiwai, clemens, linuxaudio, Daniel Mack, blablack

I actually wanted to wait for more input, but as the flaws reported so
far were rather minor, I addressed to following details in this new
round as mentioned by Takashi and Clemens:

 - removed the faulty "(!u || u->urb)" check
 - guard the result of min() with the spinlock inside next_packet_size()
 - removed the tasklet and made the handler a function that is called
   in-place

Thanks again for the reviews, and keep them coming :)

Daniel

Daniel Mack (5):
  ALSA: snd-usb: add snd_usb_audio-wide mutex
  ALSA: snd-usb: implement new endpoint streaming model
  ALSA: snd-usb: switch over to new endpoint streaming logic
  ALSA: snd-usb: remove old streaming logic
  ALSA: snd-usb: add support for implicit feedback

 sound/usb/card.c     |   10 +-
 sound/usb/card.h     |   62 +++
 sound/usb/endpoint.c | 1454 +++++++++++++++++++++++++------------------------
 sound/usb/endpoint.h |   32 +-
 sound/usb/pcm.c      |  437 +++++++++++++---
 sound/usb/stream.c   |   31 +-
 sound/usb/usbaudio.h |    2 +
 7 files changed, 1224 insertions(+), 804 deletions(-)

-- 
1.7.5.4

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/5] ALSA: snd-usb: add snd_usb_audio-wide mutex
  2011-12-20 23:34 [PATCH 0/5] snd-usb endpoint rework, take 5 Daniel Mack
@ 2011-12-20 23:34 ` Daniel Mack
  2011-12-20 23:34 ` [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model Daniel Mack
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 20+ messages in thread
From: Daniel Mack @ 2011-12-20 23:34 UTC (permalink / raw)
  To: alsa-devel; +Cc: gdiffey, tiwai, clemens, linuxaudio, Daniel Mack, blablack

This is needed for new card-wide list operations.

Signed-off-by: Daniel Mack <zonque@gmail.com>
---
 sound/usb/card.c     |    2 ++
 sound/usb/usbaudio.h |    1 +
 2 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/sound/usb/card.c b/sound/usb/card.c
index 0f6dc0d..ccfe1c6 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -276,6 +276,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
 
 static int snd_usb_audio_free(struct snd_usb_audio *chip)
 {
+	mutex_destroy(&chip->mutex);
 	kfree(chip);
 	return 0;
 }
@@ -336,6 +337,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
 		return -ENOMEM;
 	}
 
+	mutex_init(&chip->mutex);
 	mutex_init(&chip->shutdown_mutex);
 	chip->index = idx;
 	chip->dev = dev;
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 3e2b035..a16c21d 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -36,6 +36,7 @@ struct snd_usb_audio {
 	struct snd_card *card;
 	struct usb_interface *pm_intf;
 	u32 usb_id;
+	struct mutex mutex;
 	struct mutex shutdown_mutex;
 	unsigned int shutdown:1;
 	unsigned int probing:1;
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-20 23:34 [PATCH 0/5] snd-usb endpoint rework, take 5 Daniel Mack
  2011-12-20 23:34 ` [PATCH 1/5] ALSA: snd-usb: add snd_usb_audio-wide mutex Daniel Mack
@ 2011-12-20 23:34 ` Daniel Mack
  2011-12-21 16:16   ` Aurélien Leblond
  2011-12-20 23:34 ` [PATCH 3/5] ALSA: snd-usb: switch over to new endpoint streaming logic Daniel Mack
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 20+ messages in thread
From: Daniel Mack @ 2011-12-20 23:34 UTC (permalink / raw)
  To: alsa-devel; +Cc: gdiffey, tiwai, clemens, linuxaudio, Daniel Mack, blablack

In order to split changes properly, this patch only adds the new
implementation but leaves the old one around, so the the driver doesn't
change its behaviour. The switch to actually use the new code is
submitted separately.

Signed-off-by: Daniel Mack <zonque@gmail.com>
---
 sound/usb/card.h     |   58 ++++
 sound/usb/endpoint.c |  927 +++++++++++++++++++++++++++++++++++++++++++++++++-
 sound/usb/endpoint.h |   26 ++
 sound/usb/usbaudio.h |    1 +
 4 files changed, 1001 insertions(+), 11 deletions(-)

diff --git a/sound/usb/card.h b/sound/usb/card.h
index a39edcc..92228f1 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -29,13 +29,17 @@ struct audioformat {
 };
 
 struct snd_usb_substream;
+struct snd_usb_endpoint;
 
 struct snd_urb_ctx {
 	struct urb *urb;
 	unsigned int buffer_size;	/* size of data buffer, if data URB */
 	struct snd_usb_substream *subs;
+	struct snd_usb_endpoint *ep;
 	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 list_head ready_list;
 };
 
 struct snd_urb_ops {
@@ -45,6 +49,60 @@ struct snd_urb_ops {
 	int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
 };
 
+struct snd_usb_endpoint {
+	struct snd_usb_audio *chip;
+
+	int use_count;
+	int ep_num;		/* the referenced endpoint number */
+	int type;		/* SND_USB_ENDPOINT_TYPE_* */
+	unsigned long flags;
+
+	void (*prepare_data_urb) (struct snd_usb_substream *subs,
+				  struct urb *urb);
+	void (*retire_data_urb) (struct snd_usb_substream *subs,
+				 struct urb *urb);
+
+	struct snd_usb_substream *data_subs;
+	struct snd_usb_endpoint *sync_master;
+	struct snd_usb_endpoint *sync_slave;
+
+	struct snd_urb_ctx urb[MAX_URBS];
+
+	struct snd_usb_packet_info {
+		uint32_t packet_size[MAX_PACKS_HS];
+		int packets;
+	} next_packet[MAX_URBS];
+	int next_packet_read_pos, next_packet_write_pos;
+	struct list_head ready_playback_urbs;
+
+	unsigned int nurbs;		/* # urbs */
+	unsigned long active_mask;	/* bitmask of active urbs */
+	unsigned long unlink_mask;	/* bitmask of unlinked urbs */
+	char *syncbuf;			/* sync buffer for all sync URBs */
+	dma_addr_t sync_dma;		/* DMA address of syncbuf */
+
+	unsigned int pipe;		/* the data i/o pipe */
+	unsigned int freqn;		/* nominal sampling rate in fs/fps in Q16.16 format */
+	unsigned int freqm;		/* momentary sampling rate in fs/fps in Q16.16 format */
+	int	   freqshift;		/* how much to shift the feedback value to get Q16.16 */
+	unsigned int freqmax;		/* maximum sampling rate, used for buffer management */
+	unsigned int phase;		/* phase accumulator */
+	unsigned int maxpacksize;	/* max packet size in bytes */
+	unsigned int maxframesize;      /* max packet size in frames */
+	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
+	unsigned int curframesize;      /* current packet size in frames (for capture) */
+	unsigned int syncmaxsize;	/* sync endpoint packet size */
+	unsigned int fill_max:1;	/* fill max packet size always */
+	unsigned int datainterval;      /* log_2 of data packet interval */
+	unsigned int syncinterval;	/* P for adaptive mode, 0 otherwise */
+	unsigned char silence_value;
+	unsigned int stride;
+	int iface, alt_idx;
+
+	spinlock_t lock;
+	struct list_head list;
+};
+
 struct snd_usb_substream {
 	struct snd_usb_stream *stream;
 	struct usb_device *dev;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 81c6ede..3e06fdc 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -19,9 +19,11 @@
 #include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/slab.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 
 #include "usbaudio.h"
 #include "helper.h"
@@ -29,6 +31,9 @@
 #include "endpoint.h"
 #include "pcm.h"
 
+#define EP_FLAG_ACTIVATED	0
+#define EP_FLAG_RUNNING		1
+
 /*
  * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
  * this will overflow at approx 524 kHz
@@ -50,7 +55,7 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
 /*
  * unlink active urbs.
  */
-static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
+static int deactivate_urbs_old(struct snd_usb_substream *subs, int force, int can_sleep)
 {
 	struct snd_usb_audio *chip = subs->stream->chip;
 	unsigned int i;
@@ -112,7 +117,7 @@ static void release_urb_ctx(struct snd_urb_ctx *u)
 /*
  *  wait until all urbs are processed.
  */
-static int wait_clear_urbs(struct snd_usb_substream *subs)
+static int wait_clear_urbs_old(struct snd_usb_substream *subs)
 {
 	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
 	unsigned int i;
@@ -147,8 +152,8 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
 	int i;
 
 	/* stop urbs (to be sure) */
-	deactivate_urbs(subs, force, 1);
-	wait_clear_urbs(subs);
+	deactivate_urbs_old(subs, force, 1);
+	wait_clear_urbs_old(subs);
 
 	for (i = 0; i < MAX_URBS; i++)
 		release_urb_ctx(&subs->dataurb[i]);
@@ -163,7 +168,7 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
 /*
  * complete callback from data urb
  */
-static void snd_complete_urb(struct urb *urb)
+static void snd_complete_urb_old(struct urb *urb)
 {
 	struct snd_urb_ctx *ctx = urb->context;
 	struct snd_usb_substream *subs = ctx->subs;
@@ -317,7 +322,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
 		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
 		u->urb->interval = 1 << subs->datainterval;
 		u->urb->context = u;
-		u->urb->complete = snd_complete_urb;
+		u->urb->complete = snd_complete_urb_old;
 	}
 
 	if (subs->syncpipe) {
@@ -855,7 +860,7 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru
 
  __error:
 	// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
-	deactivate_urbs(subs, 0, 0);
+	deactivate_urbs_old(subs, 0, 0);
 	return -EPIPE;
 }
 
@@ -916,7 +921,7 @@ 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:
-		return deactivate_urbs(subs, 0, 0);
+		return deactivate_urbs_old(subs, 0, 0);
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		subs->ops.prepare = prepare_nodata_playback_urb;
 		return 0;
@@ -934,7 +939,7 @@ int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int c
 		subs->ops.retire = retire_capture_urb;
 		return start_urbs(subs, substream->runtime);
 	case SNDRV_PCM_TRIGGER_STOP:
-		return deactivate_urbs(subs, 0, 0);
+		return deactivate_urbs_old(subs, 0, 0);
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		subs->ops.retire = retire_paused_capture_urb;
 		return 0;
@@ -950,8 +955,8 @@ 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);
+	deactivate_urbs_old(subs, 0, 1);
+	wait_clear_urbs_old(subs);
 
 	/* for playback, submit the URBs now; otherwise, the first hwptr_done
 	 * updates for all URBs would happen at the same time when starting */
@@ -963,3 +968,903 @@ int snd_usb_substream_prepare(struct snd_usb_substream *subs,
 	return 0;
 }
 
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
+{
+	return  ep->sync_master &&
+		ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA &&
+		ep->type == SND_USB_ENDPOINT_TYPE_DATA &&
+		usb_pipeout(ep->pipe);
+}
+
+/* determine the number of frames in the next packet */
+static int next_packet_size(struct snd_usb_endpoint *ep)
+{
+	unsigned long flags;
+	int ret;
+
+	if (ep->fill_max)
+		return ep->maxframesize;
+
+	spin_lock_irqsave(&ep->lock, flags);
+	ep->phase = (ep->phase & 0xffff)
+		+ (ep->freqm << ep->datainterval);
+	ret = min(ep->phase >> 16, ep->maxframesize);
+	spin_unlock_irqrestore(&ep->lock, flags);
+
+	return ret;
+}
+
+static void retire_outbound_urb(struct snd_usb_endpoint *ep,
+				struct snd_urb_ctx *urb_ctx)
+{
+	if (ep->retire_data_urb)
+		ep->retire_data_urb(ep->data_subs, urb_ctx->urb);
+}
+
+static void retire_inbound_urb(struct snd_usb_endpoint *ep,
+			       struct snd_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+
+	if (ep->sync_slave)
+		snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
+
+	if (ep->retire_data_urb)
+		ep->retire_data_urb(ep->data_subs, urb);
+}
+
+static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep,
+				       struct snd_urb_ctx *ctx)
+{
+	int i;
+
+	for (i = 0; i < ctx->packets; ++i)
+		ctx->packet_size[i] = next_packet_size(ep);
+}
+
+/*
+ * Prepare a PLAYBACK urb for submission to the bus.
+ */
+static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
+				 struct snd_urb_ctx *ctx)
+{
+	int i;
+	struct urb *urb = ctx->urb;
+	unsigned char *cp = urb->transfer_buffer;
+
+	urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+	switch (ep->type) {
+	case SND_USB_ENDPOINT_TYPE_DATA:
+		if (ep->prepare_data_urb) {
+			ep->prepare_data_urb(ep->data_subs, urb);
+		} else {
+			/* no data provider, so send silence */
+			unsigned int offs = 0;
+			for (i = 0; i < ctx->packets; ++i) {
+				int counts = ctx->packet_size[i];
+				urb->iso_frame_desc[i].offset = offs * ep->stride;
+				urb->iso_frame_desc[i].length = counts * ep->stride;
+				offs += counts;
+			}
+
+			urb->number_of_packets = ctx->packets;
+			urb->transfer_buffer_length = offs * ep->stride;
+			memset(urb->transfer_buffer, ep->silence_value,
+			       offs * ep->stride);
+		}
+		break;
+
+	case SND_USB_ENDPOINT_TYPE_SYNC:
+		if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
+			/*
+			 * fill the length and offset of each urb descriptor.
+			 * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+			 */
+			urb->iso_frame_desc[0].length = 4;
+			urb->iso_frame_desc[0].offset = 0;
+			cp[0] = ep->freqn;
+			cp[1] = ep->freqn >> 8;
+			cp[2] = ep->freqn >> 16;
+			cp[3] = ep->freqn >> 24;
+		} else {
+			/*
+			 * fill the length and offset of each urb descriptor.
+			 * the fixed 10.14 frequency is passed through the pipe.
+			 */
+			urb->iso_frame_desc[0].length = 3;
+			urb->iso_frame_desc[0].offset = 0;
+			cp[0] = ep->freqn >> 2;
+			cp[1] = ep->freqn >> 10;
+			cp[2] = ep->freqn >> 18;
+		}
+
+		break;
+	}
+}
+
+/*
+ * Prepare a CAPTURE or SYNC urb for submission to the bus.
+ */
+static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
+				       struct snd_urb_ctx *urb_ctx)
+{
+	int i, offs;
+	struct urb *urb = urb_ctx->urb;
+
+	urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+	switch (ep->type) {
+	case SND_USB_ENDPOINT_TYPE_DATA:
+		offs = 0;
+		for (i = 0; i < urb_ctx->packets; i++) {
+			urb->iso_frame_desc[i].offset = offs;
+			urb->iso_frame_desc[i].length = ep->curpacksize;
+			offs += ep->curpacksize;
+		}
+
+		urb->transfer_buffer_length = offs;
+		urb->number_of_packets = urb_ctx->packets;
+		break;
+
+	case SND_USB_ENDPOINT_TYPE_SYNC:
+		urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize);
+		urb->iso_frame_desc[0].offset = 0;
+		break;
+	}
+}
+
+static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ep->lock, flags);
+
+	while (test_bit(EP_FLAG_RUNNING, &ep->flags) &&
+	       !list_empty(&ep->ready_playback_urbs) &&
+	       ep->next_packet_read_pos != ep->next_packet_write_pos) {
+
+		struct snd_usb_packet_info *packet;
+		struct snd_urb_ctx *ctx;
+		struct urb *urb;
+		int err, i;
+
+		packet = ep->next_packet + ep->next_packet_read_pos;
+		ep->next_packet_read_pos++;
+		ep->next_packet_read_pos %= MAX_URBS;
+
+		/* take URB out of FIFO */
+		ctx = list_first_entry(&ep->ready_playback_urbs,
+				       struct snd_urb_ctx, ready_list);
+		list_del(&ctx->ready_list);
+		urb = ctx->urb;
+
+		/* copy over the length information */
+		for (i = 0; i < packet->packets; i++)
+			ctx->packet_size[i] = packet->packet_size[i];
+
+		prepare_outbound_urb(ep, ctx);
+
+		err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
+		if (err < 0)
+			snd_printk(KERN_ERR "Unable to submit urb #%d: %d (urb %p)\n",
+				   ctx->index, err, ctx->urb);
+		else
+			set_bit(ctx->index, &ep->active_mask);
+	}
+
+	spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+/*
+ * complete callback for urbs
+ */
+static void snd_complete_urb(struct urb *urb)
+{
+	struct snd_urb_ctx *ctx = urb->context;
+	struct snd_usb_endpoint *ep = ctx->ep;
+	unsigned long flags;
+	int err;
+
+	if (unlikely(urb->status == -ENOENT ||		/* unlinked */
+		     urb->status == -ENODEV ||		/* device removed */
+		     urb->status == -ECONNRESET ||	/* unlinked */
+		     urb->status == -ESHUTDOWN))	/* device disabled */
+		goto exit_clear;
+
+	if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+		goto exit_clear;
+
+	if (usb_pipeout(ep->pipe)) {
+		retire_outbound_urb(ep, ctx);
+		/* can be stopped during retire callback */
+		if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+			goto exit_clear;
+
+		if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+			clear_bit(ctx->index, &ep->active_mask);
+			spin_lock_irqsave(&ep->lock, flags);
+			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+			spin_unlock_irqrestore(&ep->lock, flags);
+			queue_pending_output_urbs(ep);
+			return;
+		}
+
+		prepare_outbound_urb_sizes(ep, ctx);
+		prepare_outbound_urb(ep, ctx);
+	} else {
+		retire_inbound_urb(ep, ctx);
+		/* can be stopped during retire callback */
+		if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+			goto exit_clear;
+
+		prepare_inbound_urb(ep, ctx);
+	}
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err == 0)
+		return;
+
+	snd_printk(KERN_ERR "cannot submit urb (err = %d)\n", err);
+	//snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+exit_clear:
+	clear_bit(ctx->index, &ep->active_mask);
+}
+
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+					      struct usb_host_interface *alts,
+					      int ep_num, int direction, int type)
+{
+	struct list_head *p;
+	struct snd_usb_endpoint *ep;
+	int ret, is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
+
+	mutex_lock(&chip->mutex);
+
+	list_for_each(p, &chip->ep_list) {
+		ep = list_entry(p, struct snd_usb_endpoint, list);
+		if (ep->ep_num == ep_num &&
+		    ep->iface == alts->desc.bInterfaceNumber &&
+		    ep->alt_idx == alts->desc.bAlternateSetting) {
+			snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n",
+					ep_num, ep->iface, ep->alt_idx, ep);
+			goto __exit_unlock;
+		}
+	}
+
+	snd_printdd(KERN_DEBUG "Creating new %s %s endpoint #%x\n",
+		   is_playback ? "playback" : "capture",
+		   type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync",
+		   ep_num);
+
+	/* select the alt setting once so the endpoints become valid */
+	ret = usb_set_interface(chip->dev, alts->desc.bInterfaceNumber,
+				alts->desc.bAlternateSetting);
+	if (ret < 0) {
+		snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+					__func__, ret);
+		ep = NULL;
+		goto __exit_unlock;
+	}
+
+	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+	if (!ep)
+		goto __exit_unlock;
+
+	ep->chip = chip;
+	spin_lock_init(&ep->lock);
+	ep->type = type;
+	ep->ep_num = ep_num;
+	ep->iface = alts->desc.bInterfaceNumber;
+	ep->alt_idx = alts->desc.bAlternateSetting;
+	INIT_LIST_HEAD(&ep->ready_playback_urbs);
+	ep_num &= USB_ENDPOINT_NUMBER_MASK;
+
+	if (is_playback)
+		ep->pipe = usb_sndisocpipe(chip->dev, ep_num);
+	else
+		ep->pipe = usb_rcvisocpipe(chip->dev, ep_num);
+
+	if (type == SND_USB_ENDPOINT_TYPE_SYNC) {
+		if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+		    get_endpoint(alts, 1)->bRefresh >= 1 &&
+		    get_endpoint(alts, 1)->bRefresh <= 9)
+			ep->syncinterval = get_endpoint(alts, 1)->bRefresh;
+		else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL)
+			ep->syncinterval = 1;
+		else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+			 get_endpoint(alts, 1)->bInterval <= 16)
+			ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+		else
+			ep->syncinterval = 3;
+
+		ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
+	}
+
+	list_add_tail(&ep->list, &chip->ep_list);
+
+__exit_unlock:
+	mutex_unlock(&chip->mutex);
+
+	return ep;
+}
+
+/*
+ *  wait until all urbs are processed.
+ */
+static int wait_clear_urbs(struct snd_usb_endpoint *ep)
+{
+	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
+	unsigned int i;
+	int alive;
+
+	do {
+		alive = 0;
+		for (i = 0; i < ep->nurbs; i++)
+			if (test_bit(i, &ep->active_mask))
+				alive++;
+
+		if (!alive)
+			break;
+
+		schedule_timeout_uninterruptible(1);
+	} while (time_before(jiffies, end_time));
+
+	if (alive)
+		snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n",
+					alive, ep->ep_num);
+
+	return 0;
+}
+
+/*
+ * unlink active urbs.
+ */
+static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep)
+{
+	unsigned long flags;
+	unsigned int i;
+	int async;
+
+	if (!force && ep->chip->shutdown) /* to be sure... */
+		return -EBADFD;
+
+	async = !can_sleep && ep->chip->async_unlink;
+
+	clear_bit(EP_FLAG_RUNNING, &ep->flags);
+
+	spin_lock_irqsave(&ep->lock, flags);
+	INIT_LIST_HEAD(&ep->ready_playback_urbs);
+	ep->next_packet_read_pos = 0;
+	ep->next_packet_write_pos = 0;
+	spin_unlock_irqrestore(&ep->lock, flags);
+
+	if (!async && in_interrupt())
+		return 0;
+
+	for (i = 0; i < ep->nurbs; i++) {
+		if (test_bit(i, &ep->active_mask)) {
+			if (!test_and_set_bit(i, &ep->unlink_mask)) {
+				struct urb *u = ep->urb[i].urb;
+				if (async)
+					usb_unlink_urb(u);
+				else
+					usb_kill_urb(u);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * release an endpoint's urbs
+ */
+static void release_urbs(struct snd_usb_endpoint *ep, int force)
+{
+	int i;
+
+	/* route incoming urbs to nirvana */
+	ep->retire_data_urb = NULL;
+	ep->prepare_data_urb = NULL;
+
+	/* stop urbs */
+	deactivate_urbs(ep, force, 1);
+	wait_clear_urbs(ep);
+
+	for (i = 0; i < ep->nurbs; i++)
+		release_urb_ctx(&ep->urb[i]);
+
+	if (ep->syncbuf)
+		usb_free_coherent(ep->chip->dev, SYNC_URBS * 4,
+				  ep->syncbuf, ep->sync_dma);
+
+	ep->syncbuf = NULL;
+	ep->nurbs = 0;
+}
+
+static int data_ep_set_params(struct snd_usb_endpoint *ep,
+			      struct snd_pcm_hw_params *hw_params,
+			      struct audioformat *fmt,
+			      struct snd_usb_endpoint *sync_ep)
+{
+	unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
+	int period_bytes = params_period_bytes(hw_params);
+	int format = params_format(hw_params);
+	int is_playback = usb_pipeout(ep->pipe);
+	int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) *
+							params_channels(hw_params);
+
+	ep->datainterval = fmt->datainterval;
+	ep->stride = frame_bits >> 3;
+	ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
+
+	/* calculate max. frequency */
+	if (ep->maxpacksize) {
+		/* whatever fits into a max. size packet */
+		maxsize = ep->maxpacksize;
+		ep->freqmax = (maxsize / (frame_bits >> 3))
+				<< (16 - ep->datainterval);
+	} else {
+		/* no max. packet size: just take 25% higher than nominal */
+		ep->freqmax = ep->freqn + (ep->freqn >> 2);
+		maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
+				>> (16 - ep->datainterval);
+	}
+
+	if (ep->fill_max)
+		ep->curpacksize = ep->maxpacksize;
+	else
+		ep->curpacksize = maxsize;
+
+	if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL)
+		packs_per_ms = 8 >> ep->datainterval;
+	else
+		packs_per_ms = 1;
+
+	if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+		urb_packs = max(ep->chip->nrpacks, 1);
+		urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
+	} else {
+		urb_packs = 1;
+	}
+
+	urb_packs *= packs_per_ms;
+
+	if (sync_ep && !snd_usb_endpoint_implict_feedback_sink(ep))
+		urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
+
+	/* decide how many packets to be used */
+	if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+		unsigned int minsize, maxpacks;
+		/* determine how small a packet can be */
+		minsize = (ep->freqn >> (16 - ep->datainterval))
+			  * (frame_bits >> 3);
+		/* with sync from device, assume it can be 12% lower */
+		if (sync_ep)
+			minsize -= minsize >> 3;
+		minsize = max(minsize, 1u);
+		total_packs = (period_bytes + minsize - 1) / minsize;
+		/* we need at least two URBs for queueing */
+		if (total_packs < 2) {
+			total_packs = 2;
+		} else {
+			/* and we don't want too long a queue either */
+			maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
+			total_packs = min(total_packs, maxpacks);
+		}
+	} else {
+		while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+			urb_packs >>= 1;
+		total_packs = MAX_URBS * urb_packs;
+	}
+
+	ep->nurbs = (total_packs + urb_packs - 1) / urb_packs;
+	if (ep->nurbs > MAX_URBS) {
+		/* too much... */
+		ep->nurbs = MAX_URBS;
+		total_packs = MAX_URBS * urb_packs;
+	} else if (ep->nurbs < 2) {
+		/* too little - we need at least two packets
+		 * to ensure contiguous playback/capture
+		 */
+		ep->nurbs = 2;
+	}
+
+	/* allocate and initialize data urbs */
+	for (i = 0; i < ep->nurbs; i++) {
+		struct snd_urb_ctx *u = &ep->urb[i];
+		u->index = i;
+		u->ep = ep;
+		u->packets = (i + 1) * total_packs / ep->nurbs
+			- i * total_packs / ep->nurbs;
+		u->buffer_size = maxsize * u->packets;
+
+		if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
+			u->packets++; /* for transfer delimiter */
+		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+		if (!u->urb)
+			goto out_of_memory;
+
+		u->urb->transfer_buffer =
+			usb_alloc_coherent(ep->chip->dev, u->buffer_size,
+					   GFP_KERNEL, &u->urb->transfer_dma);
+		if (!u->urb->transfer_buffer)
+			goto out_of_memory;
+		u->urb->pipe = ep->pipe;
+		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+		u->urb->interval = 1 << ep->datainterval;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+	}
+
+	return 0;
+
+out_of_memory:
+	release_urbs(ep, 0);
+	return -ENOMEM;
+}
+
+static int sync_ep_set_params(struct snd_usb_endpoint *ep,
+			      struct snd_pcm_hw_params *hw_params,
+			      struct audioformat *fmt)
+{
+	int i;
+
+	ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
+					 GFP_KERNEL, &ep->sync_dma);
+	if (!ep->syncbuf)
+		return -ENOMEM;
+
+	for (i = 0; i < SYNC_URBS; i++) {
+		struct snd_urb_ctx *u = &ep->urb[i];
+		u->index = i;
+		u->ep = ep;
+		u->packets = 1;
+		u->urb = usb_alloc_urb(1, GFP_KERNEL);
+		if (!u->urb)
+			goto out_of_memory;
+		u->urb->transfer_buffer = ep->syncbuf + i * 4;
+		u->urb->transfer_dma = ep->sync_dma + i * 4;
+		u->urb->transfer_buffer_length = 4;
+		u->urb->pipe = ep->pipe;
+		u->urb->transfer_flags = URB_ISO_ASAP |
+					 URB_NO_TRANSFER_DMA_MAP;
+		u->urb->number_of_packets = 1;
+		u->urb->interval = 1 << ep->syncinterval;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+	}
+
+	ep->nurbs = SYNC_URBS;
+
+	return 0;
+
+out_of_memory:
+	release_urbs(ep, 0);
+	return -ENOMEM;
+}
+
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+				struct snd_pcm_hw_params *hw_params,
+				struct audioformat *fmt,
+				struct snd_usb_endpoint *sync_ep)
+{
+	int err;
+
+	if (ep->use_count != 0) {
+		snd_printk(KERN_WARNING "Unable to change format on ep #%x: already in use\n",
+			   ep->ep_num);
+		return -EBUSY;
+	}
+
+	/* release old buffers, if any */
+	release_urbs(ep, 0);
+
+	ep->datainterval = fmt->datainterval;
+	ep->maxpacksize = fmt->maxpacksize;
+	ep->fill_max = fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX;
+
+	if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
+		ep->freqn = get_usb_full_speed_rate(params_rate(hw_params));
+	else
+		ep->freqn = get_usb_high_speed_rate(params_rate(hw_params));
+
+	/* calculate the frequency in 16.16 format */
+	ep->freqm = ep->freqn;
+	ep->freqshift = INT_MIN;
+
+	ep->phase = 0;
+
+	switch (ep->type) {
+	case  SND_USB_ENDPOINT_TYPE_DATA:
+		err = data_ep_set_params(ep, hw_params, fmt, sync_ep);
+		break;
+	case  SND_USB_ENDPOINT_TYPE_SYNC:
+		err = sync_ep_set_params(ep, hw_params, fmt);
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	snd_printdd(KERN_DEBUG "Setting params for ep #%x (type %d, %d urbs), ret=%d\n",
+		   ep->ep_num, ep->type, ep->nurbs, err);
+
+	return err;
+}
+
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
+{
+	int err;
+	unsigned int i;
+
+	if (ep->chip->shutdown)
+		return -EBADFD;
+
+	/* already running? */
+	if (++ep->use_count != 1)
+		return 0;
+
+	if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
+		return -EINVAL;
+
+	/* just to be sure */
+	deactivate_urbs(ep, 0, 1);
+	wait_clear_urbs(ep);
+
+	ep->active_mask = 0;
+	ep->unlink_mask = 0;
+	ep->phase = 0;
+
+	/*
+	 * If this endpoint has a data endpoint as implicit feedback source,
+	 * don't start the urbs here. Instead, mark them all as available,
+	 * wait for the record urbs to arrive and queue from that context.
+	 */
+
+	set_bit(EP_FLAG_RUNNING, &ep->flags);
+
+	if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+		for (i = 0; i < ep->nurbs; i++) {
+			struct snd_urb_ctx *ctx = ep->urb + i;
+			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+		}
+
+		return 0;
+	}
+
+	for (i = 0; i < ep->nurbs; i++) {
+		struct urb *urb = ep->urb[i].urb;
+
+		if (snd_BUG_ON(!urb))
+			goto __error;
+
+		if (usb_pipeout(ep->pipe)) {
+			prepare_outbound_urb_sizes(ep, urb->context);
+			prepare_outbound_urb(ep, urb->context);
+		} else {
+			prepare_inbound_urb(ep, urb->context);
+		}
+
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0) {
+			snd_printk(KERN_ERR "cannot submit urb %d, error %d: %s\n",
+				   i, err, usb_error_string(err));
+			goto __error;
+		}
+		set_bit(i, &ep->active_mask);
+	}
+
+	return 0;
+
+__error:
+	clear_bit(EP_FLAG_RUNNING, &ep->flags);
+	ep->use_count--;
+	deactivate_urbs(ep, 0, 0);
+	return -EPIPE;
+}
+
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
+			   int force, int can_sleep, int wait)
+{
+	if (!ep)
+		return;
+
+	if (snd_BUG_ON(ep->use_count == 0))
+		return;
+
+	if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
+		return;
+
+	if (--ep->use_count == 0) {
+		deactivate_urbs(ep, force, can_sleep);
+		ep->data_subs = NULL;
+		ep->sync_slave = NULL;
+		ep->retire_data_urb = NULL;
+		ep->prepare_data_urb = NULL;
+
+		if (wait)
+			wait_clear_urbs(ep);
+	}
+}
+
+int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep)
+{
+	if (ep->use_count != 0)
+		return 0;
+
+	if (!ep->chip->shutdown &&
+	    !test_and_set_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
+		int ret;
+
+		ret = usb_set_interface(ep->chip->dev, ep->iface, ep->alt_idx);
+		if (ret < 0) {
+			snd_printk(KERN_ERR "%s() usb_set_interface() failed, ret = %d\n",
+						__func__, ret);
+			clear_bit(EP_FLAG_ACTIVATED, &ep->flags);
+			return ret;
+		}
+
+		return 0;
+	}
+
+	return -EBUSY;
+}
+
+int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
+{
+	if (!ep)
+		return -EINVAL;
+
+	if (ep->use_count != 0)
+		return 0;
+
+	if (!ep->chip->shutdown &&
+	    test_and_clear_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
+		int ret;
+
+		ret = usb_set_interface(ep->chip->dev, ep->iface, 0);
+		if (ret < 0) {
+			snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+						__func__, ret);
+			return ret;
+		}
+
+		return 0;
+	}
+
+	return -EBUSY;
+}
+
+void snd_usb_endpoint_free(struct list_head *head)
+{
+	struct snd_usb_endpoint *ep;
+
+	ep = list_entry(head, struct snd_usb_endpoint, list);
+	release_urbs(ep, 1);
+	kfree(ep);
+}
+
+/*
+ * process after playback sync complete
+ *
+ * Full speed devices report feedback values in 10.14 format as samples per
+ * frame, high speed devices in 16.16 format as samples per microframe.
+ * Because the Audio Class 1 spec was written before USB 2.0, many high speed
+ * devices use a wrong interpretation, some others use an entirely different
+ * format.  Therefore, we cannot predict what format any particular device uses
+ * and must detect it automatically.
+ */
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+			     struct snd_usb_endpoint *sender,
+			     const struct urb *urb)
+{
+	int shift;
+	unsigned int f;
+	unsigned long flags;
+
+	snd_BUG_ON(ep == sender);
+
+	if (snd_usb_endpoint_implict_feedback_sink(ep) &&
+	    ep->use_count != 0) {
+
+		/* implicit feedback case */
+		int i, bytes = 0;
+		struct snd_urb_ctx *in_ctx;
+		struct snd_usb_packet_info *out_packet;
+
+		in_ctx = urb->context;
+
+		/* Count overall packet size */
+		for (i = 0; i < in_ctx->packets; i++)
+			if (urb->iso_frame_desc[i].status == 0)
+				bytes += 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;
+
+		spin_lock_irqsave(&ep->lock, flags);
+		out_packet = ep->next_packet + ep->next_packet_write_pos;
+
+		/*
+		 * Iterate through the inbound packet and prepare the lengths
+		 * for the output packet. The OUT packet we are about to send
+		 * will have the same amount of payload than the IN packet we
+		 * just received.
+		 */
+
+		out_packet->packets = in_ctx->packets;
+		for (i = 0; i < in_ctx->packets; i++) {
+			if (urb->iso_frame_desc[i].status == 0)
+				out_packet->packet_size[i] =
+					urb->iso_frame_desc[i].actual_length / ep->stride;
+			else
+				out_packet->packet_size[i] = 0;
+		}
+
+		ep->next_packet_write_pos++;
+		ep->next_packet_write_pos %= MAX_URBS;
+		spin_unlock_irqrestore(&ep->lock, flags);
+		queue_pending_output_urbs(ep);
+
+		return;
+	}
+
+	if (urb->iso_frame_desc[0].status != 0 ||
+	    urb->iso_frame_desc[0].actual_length < 3)
+		return;
+
+	f = le32_to_cpup(urb->transfer_buffer);
+	if (urb->iso_frame_desc[0].actual_length == 3)
+		f &= 0x00ffffff;
+	else
+		f &= 0x0fffffff;
+
+	if (f == 0)
+		return;
+
+	if (unlikely(ep->freqshift == INT_MIN)) {
+		/*
+		 * The first time we see a feedback value, determine its format
+		 * by shifting it left or right until it matches the nominal
+		 * frequency value.  This assumes that the feedback does not
+		 * differ from the nominal value more than +50% or -25%.
+		 */
+		shift = 0;
+		while (f < ep->freqn - ep->freqn / 4) {
+			f <<= 1;
+			shift++;
+		}
+		while (f > ep->freqn + ep->freqn / 2) {
+			f >>= 1;
+			shift--;
+		}
+		ep->freqshift = shift;
+	}
+	else if (ep->freqshift >= 0)
+		f <<= ep->freqshift;
+	else
+		f >>= -ep->freqshift;
+
+	if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) {
+		/*
+		 * If the frequency looks valid, set it.
+		 * This value is referred to in prepare_playback_urb().
+		 */
+		spin_lock_irqsave(&ep->lock, flags);
+		ep->freqm = f;
+		spin_unlock_irqrestore(&ep->lock, flags);
+	} else {
+		/*
+		 * Out of range; maybe the shift value is wrong.
+		 * Reset it so that we autodetect again the next time.
+		 */
+		ep->freqshift = INT_MIN;
+	}
+}
+
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 88eb63a..9f083d7 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -18,4 +18,30 @@ 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);
 
+
+#define SND_USB_ENDPOINT_TYPE_DATA     0
+#define SND_USB_ENDPOINT_TYPE_SYNC     1
+
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+					      struct usb_host_interface *alts,
+					      int ep_num, int direction, int type);
+
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+				struct snd_pcm_hw_params *hw_params,
+				struct audioformat *fmt,
+				struct snd_usb_endpoint *sync_ep);
+
+int  snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
+			   int force, int can_sleep, int wait);
+int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
+int  snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_free(struct list_head *head);
+
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep);
+
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+			     struct snd_usb_endpoint *sender,
+			     const struct urb *urb);
+
 #endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index a16c21d..b8233eb 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -47,6 +47,7 @@ struct snd_usb_audio {
 	int num_suspended_intf;
 
 	struct list_head pcm_list;	/* list of pcm streams */
+	struct list_head ep_list;	/* list of audio-related endpoints */
 	int pcm_devs;
 
 	struct list_head midi_list;	/* list of midi interfaces */
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 3/5] ALSA: snd-usb: switch over to new endpoint streaming logic
  2011-12-20 23:34 [PATCH 0/5] snd-usb endpoint rework, take 5 Daniel Mack
  2011-12-20 23:34 ` [PATCH 1/5] ALSA: snd-usb: add snd_usb_audio-wide mutex Daniel Mack
  2011-12-20 23:34 ` [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model Daniel Mack
@ 2011-12-20 23:34 ` Daniel Mack
  2011-12-20 23:34 ` [PATCH 4/5] ALSA: snd-usb: remove old " Daniel Mack
  2011-12-20 23:34 ` [PATCH 5/5] ALSA: snd-usb: add support for implicit feedback Daniel Mack
  4 siblings, 0 replies; 20+ messages in thread
From: Daniel Mack @ 2011-12-20 23:34 UTC (permalink / raw)
  To: alsa-devel; +Cc: gdiffey, tiwai, clemens, linuxaudio, Daniel Mack, blablack

All endpoint related code is now in endpoint.c, and pcm.c only acts as
wrapper for handling the packet's payload.

Signed-off-by: Daniel Mack <zonque@gmail.com>
---
 sound/usb/card.c     |    8 +-
 sound/usb/card.h     |    4 +
 sound/usb/endpoint.c |   40 -----
 sound/usb/endpoint.h |    3 -
 sound/usb/pcm.c      |  414 +++++++++++++++++++++++++++++++++++++++++---------
 sound/usb/stream.c   |   31 ++++-
 6 files changed, 381 insertions(+), 119 deletions(-)

diff --git a/sound/usb/card.c b/sound/usb/card.c
index ccfe1c6..b4cb056 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -131,8 +131,9 @@ static void snd_usb_stream_disconnect(struct list_head *head)
 		subs = &as->substream[idx];
 		if (!subs->num_formats)
 			continue;
-		snd_usb_release_substream_urbs(subs, 1);
 		subs->interface = -1;
+		subs->data_endpoint = NULL;
+		subs->sync_endpoint = NULL;
 	}
 }
 
@@ -350,6 +351,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
 	chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
 			      le16_to_cpu(dev->descriptor.idProduct));
 	INIT_LIST_HEAD(&chip->pcm_list);
+	INIT_LIST_HEAD(&chip->ep_list);
 	INIT_LIST_HEAD(&chip->midi_list);
 	INIT_LIST_HEAD(&chip->mixer_list);
 
@@ -567,6 +569,10 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
 		list_for_each(p, &chip->pcm_list) {
 			snd_usb_stream_disconnect(p);
 		}
+		/* release the endpoint resources */
+		list_for_each(p, &chip->ep_list) {
+			snd_usb_endpoint_free(p);
+		}
 		/* release the midi resources */
 		list_for_each(p, &chip->midi_list) {
 			snd_usbmidi_disconnect(p);
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 92228f1..90681f7 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -144,6 +144,10 @@ struct snd_usb_substream {
 	struct snd_urb_ctx syncurb[SYNC_URBS];	/* sync urb table */
 	char *syncbuf;				/* sync buffer for all sync URBs */
 	dma_addr_t sync_dma;			/* DMA address of syncbuf */
+	/* data and sync endpoints for this stream */
+	struct snd_usb_endpoint *data_endpoint;
+	struct snd_usb_endpoint *sync_endpoint;
+	unsigned long flags;
 
 	u64 formats;			/* format bitmasks (all or'ed) */
 	unsigned int num_formats;		/* number of supported audio formats (list) */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 3e06fdc..d093b38 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -911,46 +911,6 @@ void snd_usb_init_substream(struct snd_usb_stream *as,
 	subs->fmt_type = fp->fmt_type;
 }
 
-int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	struct snd_usb_substream *subs = substream->runtime->private_data;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		subs->ops.prepare = prepare_playback_urb;
-		return 0;
-	case SNDRV_PCM_TRIGGER_STOP:
-		return deactivate_urbs_old(subs, 0, 0);
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		subs->ops.prepare = prepare_nodata_playback_urb;
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
-int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	struct snd_usb_substream *subs = substream->runtime->private_data;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		subs->ops.retire = retire_capture_urb;
-		return start_urbs(subs, substream->runtime);
-	case SNDRV_PCM_TRIGGER_STOP:
-		return deactivate_urbs_old(subs, 0, 0);
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		subs->ops.retire = retire_paused_capture_urb;
-		return 0;
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		subs->ops.retire = retire_capture_urb;
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
 int snd_usb_substream_prepare(struct snd_usb_substream *subs,
 			      struct snd_pcm_runtime *runtime)
 {
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 9f083d7..e540768 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -15,9 +15,6 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
 int snd_usb_substream_prepare(struct snd_usb_substream *subs,
 			      struct snd_pcm_runtime *runtime);
 
-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);
-
 
 #define SND_USB_ENDPOINT_TYPE_DATA     0
 #define SND_USB_ENDPOINT_TYPE_SYNC     1
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 0220b0f..37b57eb 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -34,6 +34,9 @@
 #include "clock.h"
 #include "power.h"
 
+#define SUBSTREAM_FLAG_DATA_EP_STARTED	0
+#define SUBSTREAM_FLAG_SYNC_EP_STARTED	1
+
 /* return the estimated delay based on USB frame counters */
 snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
 				    unsigned int rate)
@@ -208,6 +211,81 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
 	}
 }
 
+static int start_endpoints(struct snd_usb_substream *subs)
+{
+	int err;
+
+	if (!subs->data_endpoint)
+		return -EINVAL;
+
+	if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
+		struct snd_usb_endpoint *ep = subs->data_endpoint;
+
+		snd_printdd(KERN_DEBUG "Starting data EP @%p\n", ep);
+
+		ep->data_subs = subs;
+		err = snd_usb_endpoint_start(ep);
+		if (err < 0) {
+			clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
+			return err;
+		}
+	}
+
+	if (!test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
+		struct snd_usb_endpoint *ep = subs->sync_endpoint;
+
+		snd_printdd(KERN_DEBUG "Starting sync EP @%p\n", ep);
+
+		ep->sync_slave = subs->data_endpoint;
+		err = snd_usb_endpoint_start(ep);
+		if (err < 0) {
+			clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void stop_endpoints(struct snd_usb_substream *subs,
+			   int force, int can_sleep, int wait)
+{
+	if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags))
+		snd_usb_endpoint_stop(subs->sync_endpoint,
+				      force, can_sleep, wait);
+
+	if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags))
+		snd_usb_endpoint_stop(subs->data_endpoint,
+				      force, can_sleep, wait);
+}
+
+static int activate_endpoints(struct snd_usb_substream *subs)
+{
+	int ret;
+
+	ret = snd_usb_endpoint_activate(subs->sync_endpoint);
+	if (ret < 0)
+		return ret;
+
+	return snd_usb_endpoint_activate(subs->data_endpoint);
+}
+
+static int deactivate_endpoints(struct snd_usb_substream *subs)
+{
+	int reta, retb;
+
+	reta = snd_usb_endpoint_deactivate(subs->sync_endpoint);
+	retb = snd_usb_endpoint_deactivate(subs->data_endpoint);
+
+	if (reta < 0)
+		return reta;
+
+	if (retb < 0)
+		return retb;
+
+	return 0;
+}
+
 /*
  * find a matching format and set up the interface
  */
@@ -232,40 +310,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 	if (fmt == subs->cur_audiofmt)
 		return 0;
 
-	/* close the old interface */
-	if (subs->interface >= 0 && subs->interface != fmt->iface) {
-		if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
-			snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
-				dev->devnum, fmt->iface, fmt->altsetting);
-			return -EIO;
-		}
-		subs->interface = -1;
-		subs->altset_idx = 0;
-	}
-
-	/* set interface */
-	if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) {
-		if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {
-			snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
-				   dev->devnum, fmt->iface, fmt->altsetting);
-			return -EIO;
-		}
-		snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);
-		subs->interface = fmt->iface;
-		subs->altset_idx = fmt->altset_idx;
-	}
-
-	/* create a data pipe */
-	ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;
-	if (is_playback)
-		subs->datapipe = usb_sndisocpipe(dev, ep);
-	else
-		subs->datapipe = usb_rcvisocpipe(dev, ep);
-	subs->datainterval = fmt->datainterval;
-	subs->syncpipe = subs->syncinterval = 0;
-	subs->maxpacksize = fmt->maxpacksize;
-	subs->syncmaxsize = 0;
-	subs->fill_max = 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;
 
 	/* we need a sync pipe in async OUT or adaptive IN mode */
 	/* check the number of EP, since some devices have broken
@@ -276,6 +325,15 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 	if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
 	     (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
 	    altsd->bNumEndpoints >= 2) {
+		switch (subs->stream->chip->usb_id) {
+		case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
+		case USB_ID(0x0763, 0x2081):
+			ep = 0x81;
+			iface = usb_ifnum_to_if(dev, 2);
+			alts = &iface->altsetting[1];
+			goto add_sync_ep;
+		}
+
 		/* check sync-pipe endpoint */
 		/* ... and check descriptor size before accessing bSynchAddress
 		   because there is a version of the SB Audigy 2 NX firmware lacking
@@ -295,28 +353,16 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 				   dev->devnum, fmt->iface, fmt->altsetting);
 			return -EINVAL;
 		}
-		ep &= USB_ENDPOINT_NUMBER_MASK;
-		if (is_playback)
-			subs->syncpipe = usb_rcvisocpipe(dev, ep);
-		else
-			subs->syncpipe = usb_sndisocpipe(dev, ep);
-		if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-		    get_endpoint(alts, 1)->bRefresh >= 1 &&
-		    get_endpoint(alts, 1)->bRefresh <= 9)
-			subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
-		else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-			subs->syncinterval = 1;
-		else if (get_endpoint(alts, 1)->bInterval >= 1 &&
-			 get_endpoint(alts, 1)->bInterval <= 16)
-			subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
-		else
-			subs->syncinterval = 3;
-		subs->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
-	}
-
-	/* always fill max packet size */
-	if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
-		subs->fill_max = 1;
+add_sync_ep:
+		subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+							   alts, ep, !subs->direction,
+							   SND_USB_ENDPOINT_TYPE_SYNC);
+
+		if (!subs->sync_endpoint)
+			return -EINVAL;
+
+		subs->data_endpoint->sync_master = subs->sync_endpoint;
+	}
 
 	if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0)
 		return err;
@@ -390,12 +436,22 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
 	if (changed) {
 		mutex_lock(&subs->stream->chip->shutdown_mutex);
 		/* format changed */
-		snd_usb_release_substream_urbs(subs, 0);
-		/* influenced: period_bytes, channels, rate, format, */
-		ret = snd_usb_init_substream_urbs(subs, params_period_bytes(hw_params),
-						  params_rate(hw_params),
-						  snd_pcm_format_physical_width(params_format(hw_params)) *
-							params_channels(hw_params));
+		stop_endpoints(subs, 0, 0, 0);
+		deactivate_endpoints(subs);
+
+		ret = activate_endpoints(subs);
+		if (ret < 0)
+			goto unlock;
+
+		ret = snd_usb_endpoint_set_params(subs->data_endpoint, hw_params, fmt,
+						  subs->sync_endpoint);
+		if (ret < 0)
+			goto unlock;
+
+		if (subs->sync_endpoint)
+			ret = snd_usb_endpoint_set_params(subs->sync_endpoint,
+							  hw_params, fmt, NULL);
+unlock:
 		mutex_unlock(&subs->stream->chip->shutdown_mutex);
 	}
 
@@ -415,7 +471,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
 	subs->cur_rate = 0;
 	subs->period_bytes = 0;
 	mutex_lock(&subs->stream->chip->shutdown_mutex);
-	snd_usb_release_substream_urbs(subs, 0);
+	stop_endpoints(subs, 0, 1, 1);
 	mutex_unlock(&subs->stream->chip->shutdown_mutex);
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
@@ -435,19 +491,28 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
 		return -ENXIO;
 	}
 
+	if (snd_BUG_ON(!subs->data_endpoint))
+		return -EIO;
+
 	/* some unit conversions in runtime */
-	subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
-	subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
+	subs->data_endpoint->maxframesize =
+		bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);
+	subs->data_endpoint->curframesize =
+		bytes_to_frames(runtime, subs->data_endpoint->curpacksize);
 
 	/* reset the pointer */
 	subs->hwptr_done = 0;
 	subs->transfer_done = 0;
-	subs->phase = 0;
 	subs->last_delay = 0;
 	subs->last_frame_number = 0;
 	runtime->delay = 0;
 
-	return snd_usb_substream_prepare(subs, runtime);
+	/* 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)
+		return start_endpoints(subs);
+
+	return 0;
 }
 
 static struct snd_pcm_hardware snd_usb_hardware =
@@ -840,16 +905,171 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
 
 static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
 {
+	int ret;
 	struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
 	struct snd_usb_substream *subs = &as->substream[direction];
 
-	if (!as->chip->shutdown && subs->interface >= 0) {
-		usb_set_interface(subs->dev, subs->interface, 0);
-		subs->interface = -1;
-	}
+	stop_endpoints(subs, 0, 0, 0);
+	ret = deactivate_endpoints(subs);
 	subs->pcm_substream = NULL;
 	snd_usb_autosuspend(subs->stream->chip);
-	return 0;
+
+	return ret;
+}
+
+/* Since a URB can handle only a single linear buffer, we must use double
+ * buffering when the data to be transferred overflows the buffer boundary.
+ * To avoid inconsistencies when updating hwptr_done, we use double buffering
+ * for all URBs.
+ */
+static void retire_capture_urb(struct snd_usb_substream *subs,
+			       struct urb *urb)
+{
+	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+	unsigned int stride, frames, bytes, oldptr;
+	int i, period_elapsed = 0;
+	unsigned long flags;
+	unsigned char *cp;
+
+	stride = runtime->frame_bits >> 3;
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+		if (urb->iso_frame_desc[i].status) {
+			snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
+			// continue;
+		}
+		bytes = urb->iso_frame_desc[i].actual_length;
+		frames = bytes / stride;
+		if (!subs->txfr_quirk)
+			bytes = frames * stride;
+		if (bytes % (runtime->sample_bits >> 3) != 0) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+			int oldbytes = bytes;
+#endif
+			bytes = frames * stride;
+			snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
+							oldbytes, bytes);
+		}
+		/* update the current pointer */
+		spin_lock_irqsave(&subs->lock, flags);
+		oldptr = subs->hwptr_done;
+		subs->hwptr_done += bytes;
+		if (subs->hwptr_done >= runtime->buffer_size * stride)
+			subs->hwptr_done -= runtime->buffer_size * stride;
+		frames = (bytes + (oldptr % stride)) / stride;
+		subs->transfer_done += frames;
+		if (subs->transfer_done >= runtime->period_size) {
+			subs->transfer_done -= runtime->period_size;
+			period_elapsed = 1;
+		}
+		spin_unlock_irqrestore(&subs->lock, flags);
+		/* copy a data chunk */
+		if (oldptr + bytes > runtime->buffer_size * stride) {
+			unsigned int bytes1 =
+					runtime->buffer_size * stride - oldptr;
+			memcpy(runtime->dma_area + oldptr, cp, bytes1);
+			memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
+		} else {
+			memcpy(runtime->dma_area + oldptr, cp, bytes);
+		}
+	}
+
+	if (period_elapsed)
+		snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
+static void prepare_playback_urb(struct snd_usb_substream *subs,
+				 struct urb *urb)
+{
+	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+	struct snd_urb_ctx *ctx = urb->context;
+	unsigned int counts, frames, bytes;
+	int i, stride, period_elapsed = 0;
+	unsigned long flags;
+
+	stride = runtime->frame_bits >> 3;
+
+	frames = 0;
+	urb->number_of_packets = 0;
+	spin_lock_irqsave(&subs->lock, flags);
+	for (i = 0; i < ctx->packets; i++) {
+		counts = ctx->packet_size[i];
+		/* set up descriptor */
+		urb->iso_frame_desc[i].offset = frames * stride;
+		urb->iso_frame_desc[i].length = counts * stride;
+		frames += counts;
+		urb->number_of_packets++;
+		subs->transfer_done += counts;
+		if (subs->transfer_done >= runtime->period_size) {
+			subs->transfer_done -= runtime->period_size;
+			period_elapsed = 1;
+			if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
+				if (subs->transfer_done > 0) {
+					/* FIXME: fill-max mode is not
+					 * supported yet */
+					frames -= subs->transfer_done;
+					counts -= subs->transfer_done;
+					urb->iso_frame_desc[i].length =
+						counts * stride;
+					subs->transfer_done = 0;
+				}
+				i++;
+				if (i < ctx->packets) {
+					/* add a transfer delimiter */
+					urb->iso_frame_desc[i].offset =
+						frames * stride;
+					urb->iso_frame_desc[i].length = 0;
+					urb->number_of_packets++;
+				}
+				break;
+			}
+		}
+		if (period_elapsed &&
+		    !snd_usb_endpoint_implict_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */
+			break;
+	}
+	bytes = frames * stride;
+	if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+		/* err, the transferred area goes over buffer boundary. */
+		unsigned int bytes1 =
+			runtime->buffer_size * stride - subs->hwptr_done;
+		memcpy(urb->transfer_buffer,
+		       runtime->dma_area + subs->hwptr_done, bytes1);
+		memcpy(urb->transfer_buffer + bytes1,
+		       runtime->dma_area, bytes - bytes1);
+	} else {
+		memcpy(urb->transfer_buffer,
+		       runtime->dma_area + subs->hwptr_done, bytes);
+	}
+	subs->hwptr_done += bytes;
+	if (subs->hwptr_done >= runtime->buffer_size * stride)
+		subs->hwptr_done -= runtime->buffer_size * stride;
+	runtime->delay += frames;
+	spin_unlock_irqrestore(&subs->lock, flags);
+	urb->transfer_buffer_length = bytes;
+	if (period_elapsed)
+		snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
+/*
+ * process after playback data complete
+ * - decrease the delay count again
+ */
+static void retire_playback_urb(struct snd_usb_substream *subs,
+			       struct urb *urb)
+{
+	unsigned long flags;
+	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+	int stride = runtime->frame_bits >> 3;
+	int processed = urb->transfer_buffer_length / stride;
+
+	spin_lock_irqsave(&subs->lock, flags);
+	if (processed > runtime->delay)
+		runtime->delay = 0;
+	else
+		runtime->delay -= processed;
+	spin_unlock_irqrestore(&subs->lock, flags);
 }
 
 static int snd_usb_playback_open(struct snd_pcm_substream *substream)
@@ -872,6 +1092,56 @@ static int snd_usb_capture_close(struct snd_pcm_substream *substream)
 	return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
 }
 
+static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream,
+					      int cmd)
+{
+	struct snd_usb_substream *subs = substream->runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		subs->data_endpoint->prepare_data_urb = prepare_playback_urb;
+		subs->data_endpoint->retire_data_urb = retire_playback_urb;
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+		stop_endpoints(subs, 0, 0, 0);
+		return 0;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		subs->data_endpoint->prepare_data_urb = NULL;
+		subs->data_endpoint->retire_data_urb = NULL;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int err;
+	struct snd_usb_substream *subs = substream->runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		err = start_endpoints(subs);
+		if (err < 0)
+			return err;
+
+		subs->data_endpoint->retire_data_urb = retire_capture_urb;
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+		stop_endpoints(subs, 0, 0, 0);
+		return 0;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		subs->data_endpoint->retire_data_urb = NULL;
+		return 0;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		subs->data_endpoint->retire_data_urb = retire_capture_urb;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
 static struct snd_pcm_ops snd_usb_playback_ops = {
 	.open =		snd_usb_playback_open,
 	.close =	snd_usb_playback_close,
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 5ff8010..6b7d7a2 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -73,6 +73,31 @@ static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
 	}
 }
 
+/*
+ * initialize the substream instance.
+ */
+
+static void snd_usb_init_substream(struct snd_usb_stream *as,
+				   int stream,
+				   struct audioformat *fp)
+{
+	struct snd_usb_substream *subs = &as->substream[stream];
+
+	INIT_LIST_HEAD(&subs->fmt_list);
+	spin_lock_init(&subs->lock);
+
+	subs->stream = as;
+	subs->direction = stream;
+	subs->dev = as->chip->dev;
+	subs->txfr_quirk = as->chip->txfr_quirk;
+
+	snd_usb_set_pcm_ops(as->pcm, stream);
+
+	list_add_tail(&fp->list, &subs->fmt_list);
+	subs->formats |= fp->formats;
+	subs->num_formats++;
+	subs->fmt_type = fp->fmt_type;
+}
 
 /*
  * add this endpoint to the chip instance.
@@ -94,9 +119,9 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
 		if (as->fmt_type != fp->fmt_type)
 			continue;
 		subs = &as->substream[stream];
-		if (!subs->endpoint)
+		if (!subs->data_endpoint)
 			continue;
-		if (subs->endpoint == fp->endpoint) {
+		if (subs->data_endpoint->ep_num == fp->endpoint) {
 			list_add_tail(&fp->list, &subs->fmt_list);
 			subs->num_formats++;
 			subs->formats |= fp->formats;
@@ -109,7 +134,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
 		if (as->fmt_type != fp->fmt_type)
 			continue;
 		subs = &as->substream[stream];
-		if (subs->endpoint)
+		if (subs->data_endpoint)
 			continue;
 		err = snd_pcm_new_stream(as->pcm, stream, 1);
 		if (err < 0)
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 4/5] ALSA: snd-usb: remove old streaming logic
  2011-12-20 23:34 [PATCH 0/5] snd-usb endpoint rework, take 5 Daniel Mack
                   ` (2 preceding siblings ...)
  2011-12-20 23:34 ` [PATCH 3/5] ALSA: snd-usb: switch over to new endpoint streaming logic Daniel Mack
@ 2011-12-20 23:34 ` Daniel Mack
  2011-12-20 23:34 ` [PATCH 5/5] ALSA: snd-usb: add support for implicit feedback Daniel Mack
  4 siblings, 0 replies; 20+ messages in thread
From: Daniel Mack @ 2011-12-20 23:34 UTC (permalink / raw)
  To: alsa-devel; +Cc: gdiffey, tiwai, clemens, linuxaudio, Daniel Mack, blablack

Signed-off-by: Daniel Mack <zonque@gmail.com>
---
 sound/usb/endpoint.c |  851 +-------------------------------------------------
 sound/usb/endpoint.h |   15 -
 2 files changed, 8 insertions(+), 858 deletions(-)

diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index d093b38..147e57f 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -53,727 +53,19 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
 }
 
 /*
- * unlink active urbs.
- */
-static int deactivate_urbs_old(struct snd_usb_substream *subs, int force, int can_sleep)
-{
-	struct snd_usb_audio *chip = subs->stream->chip;
-	unsigned int i;
-	int async;
-
-	subs->running = 0;
-
-	if (!force && subs->stream->chip->shutdown) /* to be sure... */
-		return -EBADFD;
-
-	async = !can_sleep && chip->async_unlink;
-
-	if (!async && in_interrupt())
-		return 0;
-
-	for (i = 0; i < subs->nurbs; i++) {
-		if (test_bit(i, &subs->active_mask)) {
-			if (!test_and_set_bit(i, &subs->unlink_mask)) {
-				struct urb *u = subs->dataurb[i].urb;
-				if (async)
-					usb_unlink_urb(u);
-				else
-					usb_kill_urb(u);
-			}
-		}
-	}
-	if (subs->syncpipe) {
-		for (i = 0; i < SYNC_URBS; i++) {
-			if (test_bit(i+16, &subs->active_mask)) {
-				if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
-					struct urb *u = subs->syncurb[i].urb;
-					if (async)
-						usb_unlink_urb(u);
-					else
-						usb_kill_urb(u);
-				}
-			}
-		}
-	}
-	return 0;
-}
-
-
-/*
  * release a urb data
  */
 static void release_urb_ctx(struct snd_urb_ctx *u)
 {
-	if (u->urb) {
-		if (u->buffer_size)
-			usb_free_coherent(u->subs->dev, u->buffer_size,
-					u->urb->transfer_buffer,
-					u->urb->transfer_dma);
-		usb_free_urb(u->urb);
-		u->urb = NULL;
-	}
-}
-
-/*
- *  wait until all urbs are processed.
- */
-static int wait_clear_urbs_old(struct snd_usb_substream *subs)
-{
-	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
-	unsigned int i;
-	int alive;
-
-	do {
-		alive = 0;
-		for (i = 0; i < subs->nurbs; i++) {
-			if (test_bit(i, &subs->active_mask))
-				alive++;
-		}
-		if (subs->syncpipe) {
-			for (i = 0; i < SYNC_URBS; i++) {
-				if (test_bit(i + 16, &subs->active_mask))
-					alive++;
-			}
-		}
-		if (! alive)
-			break;
-		schedule_timeout_uninterruptible(1);
-	} while (time_before(jiffies, end_time));
-	if (alive)
-		snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
-	return 0;
-}
-
-/*
- * release a substream
- */
-void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
-{
-	int i;
-
-	/* stop urbs (to be sure) */
-	deactivate_urbs_old(subs, force, 1);
-	wait_clear_urbs_old(subs);
-
-	for (i = 0; i < MAX_URBS; i++)
-		release_urb_ctx(&subs->dataurb[i]);
-	for (i = 0; i < SYNC_URBS; i++)
-		release_urb_ctx(&subs->syncurb[i]);
-	usb_free_coherent(subs->dev, SYNC_URBS * 4,
-			subs->syncbuf, subs->sync_dma);
-	subs->syncbuf = NULL;
-	subs->nurbs = 0;
-}
-
-/*
- * complete callback from data urb
- */
-static void snd_complete_urb_old(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 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);
-		}
-	}
-}
-
-
-/*
- * complete callback from sync urb
- */
-static void snd_complete_sync_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 err = 0;
-
-	if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
-	    !subs->running || /* can be stopped during retire callback */
-	    (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
-	    (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-		clear_bit(ctx->index + 16, &subs->active_mask);
-		if (err < 0) {
-			snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
-			snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-		}
-	}
-}
-
-
-/*
- * initialize a substream for plaback/capture
- */
-int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
-				unsigned int period_bytes,
-				unsigned int rate,
-				unsigned int frame_bits)
-{
-	unsigned int maxsize, i;
-	int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-	unsigned int urb_packs, total_packs, packs_per_ms;
-	struct snd_usb_audio *chip = subs->stream->chip;
-
-	/* calculate the frequency in 16.16 format */
-	if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-		subs->freqn = get_usb_full_speed_rate(rate);
-	else
-		subs->freqn = get_usb_high_speed_rate(rate);
-	subs->freqm = subs->freqn;
-	subs->freqshift = INT_MIN;
-	/* calculate max. frequency */
-	if (subs->maxpacksize) {
-		/* whatever fits into a max. size packet */
-		maxsize = subs->maxpacksize;
-		subs->freqmax = (maxsize / (frame_bits >> 3))
-				<< (16 - subs->datainterval);
-	} else {
-		/* no max. packet size: just take 25% higher than nominal */
-		subs->freqmax = subs->freqn + (subs->freqn >> 2);
-		maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
-				>> (16 - subs->datainterval);
-	}
-	subs->phase = 0;
-
-	if (subs->fill_max)
-		subs->curpacksize = subs->maxpacksize;
-	else
-		subs->curpacksize = maxsize;
-
-	if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
-		packs_per_ms = 8 >> subs->datainterval;
-	else
-		packs_per_ms = 1;
-
-	if (is_playback) {
-		urb_packs = max(chip->nrpacks, 1);
-		urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
-	} else
-		urb_packs = 1;
-	urb_packs *= packs_per_ms;
-	if (subs->syncpipe)
-		urb_packs = min(urb_packs, 1U << subs->syncinterval);
-
-	/* decide how many packets to be used */
-	if (is_playback) {
-		unsigned int minsize, maxpacks;
-		/* determine how small a packet can be */
-		minsize = (subs->freqn >> (16 - subs->datainterval))
-			  * (frame_bits >> 3);
-		/* with sync from device, assume it can be 12% lower */
-		if (subs->syncpipe)
-			minsize -= minsize >> 3;
-		minsize = max(minsize, 1u);
-		total_packs = (period_bytes + minsize - 1) / minsize;
-		/* we need at least two URBs for queueing */
-		if (total_packs < 2) {
-			total_packs = 2;
-		} else {
-			/* and we don't want too long a queue either */
-			maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
-			total_packs = min(total_packs, maxpacks);
-		}
-	} else {
-		while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
-			urb_packs >>= 1;
-		total_packs = MAX_URBS * urb_packs;
-	}
-	subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
-	if (subs->nurbs > MAX_URBS) {
-		/* too much... */
-		subs->nurbs = MAX_URBS;
-		total_packs = MAX_URBS * urb_packs;
-	} else if (subs->nurbs < 2) {
-		/* too little - we need at least two packets
-		 * to ensure contiguous playback/capture
-		 */
-		subs->nurbs = 2;
-	}
-
-	/* allocate and initialize data urbs */
-	for (i = 0; i < subs->nurbs; i++) {
-		struct snd_urb_ctx *u = &subs->dataurb[i];
-		u->index = i;
-		u->subs = subs;
-		u->packets = (i + 1) * total_packs / subs->nurbs
-			- i * total_packs / subs->nurbs;
-		u->buffer_size = maxsize * u->packets;
-		if (subs->fmt_type == UAC_FORMAT_TYPE_II)
-			u->packets++; /* for transfer delimiter */
-		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
-		if (!u->urb)
-			goto out_of_memory;
-		u->urb->transfer_buffer =
-			usb_alloc_coherent(subs->dev, u->buffer_size,
-					   GFP_KERNEL, &u->urb->transfer_dma);
-		if (!u->urb->transfer_buffer)
-			goto out_of_memory;
-		u->urb->pipe = subs->datapipe;
-		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-		u->urb->interval = 1 << subs->datainterval;
-		u->urb->context = u;
-		u->urb->complete = snd_complete_urb_old;
-	}
-
-	if (subs->syncpipe) {
-		/* allocate and initialize sync urbs */
-		subs->syncbuf = usb_alloc_coherent(subs->dev, SYNC_URBS * 4,
-						 GFP_KERNEL, &subs->sync_dma);
-		if (!subs->syncbuf)
-			goto out_of_memory;
-		for (i = 0; i < SYNC_URBS; i++) {
-			struct snd_urb_ctx *u = &subs->syncurb[i];
-			u->index = i;
-			u->subs = subs;
-			u->packets = 1;
-			u->urb = usb_alloc_urb(1, GFP_KERNEL);
-			if (!u->urb)
-				goto out_of_memory;
-			u->urb->transfer_buffer = subs->syncbuf + i * 4;
-			u->urb->transfer_dma = subs->sync_dma + i * 4;
-			u->urb->transfer_buffer_length = 4;
-			u->urb->pipe = subs->syncpipe;
-			u->urb->transfer_flags = URB_ISO_ASAP |
-						 URB_NO_TRANSFER_DMA_MAP;
-			u->urb->number_of_packets = 1;
-			u->urb->interval = 1 << subs->syncinterval;
-			u->urb->context = u;
-			u->urb->complete = snd_complete_sync_urb;
-		}
-	}
-	return 0;
-
-out_of_memory:
-	snd_usb_release_substream_urbs(subs, 0);
-	return -ENOMEM;
-}
-
-/*
- * prepare urb for full speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 10.14 frequency is passed through the pipe.
- */
-static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
-				    struct snd_pcm_runtime *runtime,
-				    struct urb *urb)
-{
-	unsigned char *cp = urb->transfer_buffer;
-	struct snd_urb_ctx *ctx = urb->context;
-
-	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	urb->iso_frame_desc[0].length = 3;
-	urb->iso_frame_desc[0].offset = 0;
-	cp[0] = subs->freqn >> 2;
-	cp[1] = subs->freqn >> 10;
-	cp[2] = subs->freqn >> 18;
-	return 0;
-}
-
-/*
- * prepare urb for high speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 12.13 frequency is passed as 16.16 through the pipe.
- */
-static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
-				       struct snd_pcm_runtime *runtime,
-				       struct urb *urb)
-{
-	unsigned char *cp = urb->transfer_buffer;
-	struct snd_urb_ctx *ctx = urb->context;
-
-	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	urb->iso_frame_desc[0].length = 4;
-	urb->iso_frame_desc[0].offset = 0;
-	cp[0] = subs->freqn;
-	cp[1] = subs->freqn >> 8;
-	cp[2] = subs->freqn >> 16;
-	cp[3] = subs->freqn >> 24;
-	return 0;
-}
-
-/*
- * process after capture sync complete
- * - nothing to do
- */
-static int retire_capture_sync_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.
- *
- * we use a temporary buffer to write the captured data.
- * since the length of written data is determined by host, we cannot
- * write onto the pcm buffer directly...  the data is thus copied
- * later at complete callback to the global buffer.
- */
-static int prepare_capture_urb(struct snd_usb_substream *subs,
-			       struct snd_pcm_runtime *runtime,
-			       struct urb *urb)
-{
-	int i, offs;
-	struct snd_urb_ctx *ctx = urb->context;
-
-	offs = 0;
-	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	for (i = 0; i < ctx->packets; i++) {
-		urb->iso_frame_desc[i].offset = offs;
-		urb->iso_frame_desc[i].length = subs->curpacksize;
-		offs += subs->curpacksize;
-	}
-	urb->transfer_buffer_length = offs;
-	urb->number_of_packets = ctx->packets;
-	return 0;
-}
-
-/*
- * process after capture complete
- *
- * copy the data from each desctiptor to the pcm buffer, and
- * update the current position.
- */
-static int retire_capture_urb(struct snd_usb_substream *subs,
-			      struct snd_pcm_runtime *runtime,
-			      struct urb *urb)
-{
-	unsigned long flags;
-	unsigned char *cp;
-	int i;
-	unsigned int stride, frames, bytes, oldptr;
-	int period_elapsed = 0;
-
-	stride = runtime->frame_bits >> 3;
-
-	for (i = 0; i < urb->number_of_packets; i++) {
-		cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-		if (urb->iso_frame_desc[i].status) {
-			snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
-			// continue;
-		}
-		bytes = urb->iso_frame_desc[i].actual_length;
-		frames = bytes / stride;
-		if (!subs->txfr_quirk)
-			bytes = frames * stride;
-		if (bytes % (runtime->sample_bits >> 3) != 0) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-			int oldbytes = bytes;
-#endif
-			bytes = frames * stride;
-			snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
-							oldbytes, bytes);
-		}
-		/* update the current pointer */
-		spin_lock_irqsave(&subs->lock, flags);
-		oldptr = subs->hwptr_done;
-		subs->hwptr_done += bytes;
-		if (subs->hwptr_done >= runtime->buffer_size * stride)
-			subs->hwptr_done -= runtime->buffer_size * stride;
-		frames = (bytes + (oldptr % stride)) / stride;
-		subs->transfer_done += frames;
-		if (subs->transfer_done >= runtime->period_size) {
-			subs->transfer_done -= runtime->period_size;
-			period_elapsed = 1;
-		}
-		spin_unlock_irqrestore(&subs->lock, flags);
-		/* copy a data chunk */
-		if (oldptr + bytes > runtime->buffer_size * stride) {
-			unsigned int bytes1 =
-					runtime->buffer_size * stride - oldptr;
-			memcpy(runtime->dma_area + oldptr, cp, bytes1);
-			memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
-		} else {
-			memcpy(runtime->dma_area + oldptr, cp, bytes);
-		}
-	}
-	if (period_elapsed)
-		snd_pcm_period_elapsed(subs->pcm_substream);
-	return 0;
-}
-
-/*
- * Process after capture complete when paused.  Nothing to do.
- */
-static int retire_paused_capture_urb(struct snd_usb_substream *subs,
-				     struct snd_pcm_runtime *runtime,
-				     struct urb *urb)
-{
-	return 0;
-}
-
-
-/*
- * prepare urb for playback sync pipe
- *
- * set up the offset and length to receive the current frequency.
- */
-static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
-				     struct snd_pcm_runtime *runtime,
-				     struct urb *urb)
-{
-	struct snd_urb_ctx *ctx = urb->context;
-
-	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	urb->iso_frame_desc[0].length = min(4u, ctx->subs->syncmaxsize);
-	urb->iso_frame_desc[0].offset = 0;
-	return 0;
-}
-
-/*
- * process after playback sync complete
- *
- * Full speed devices report feedback values in 10.14 format as samples per
- * frame, high speed devices in 16.16 format as samples per microframe.
- * Because the Audio Class 1 spec was written before USB 2.0, many high speed
- * devices use a wrong interpretation, some others use an entirely different
- * format.  Therefore, we cannot predict what format any particular device uses
- * and must detect it automatically.
- */
-static int retire_playback_sync_urb(struct snd_usb_substream *subs,
-				    struct snd_pcm_runtime *runtime,
-				    struct urb *urb)
-{
-	unsigned int f;
-	int shift;
-	unsigned long flags;
-
-	if (urb->iso_frame_desc[0].status != 0 ||
-	    urb->iso_frame_desc[0].actual_length < 3)
-		return 0;
-
-	f = le32_to_cpup(urb->transfer_buffer);
-	if (urb->iso_frame_desc[0].actual_length == 3)
-		f &= 0x00ffffff;
-	else
-		f &= 0x0fffffff;
-	if (f == 0)
-		return 0;
-
-	if (unlikely(subs->freqshift == INT_MIN)) {
-		/*
-		 * The first time we see a feedback value, determine its format
-		 * by shifting it left or right until it matches the nominal
-		 * frequency value.  This assumes that the feedback does not
-		 * differ from the nominal value more than +50% or -25%.
-		 */
-		shift = 0;
-		while (f < subs->freqn - subs->freqn / 4) {
-			f <<= 1;
-			shift++;
-		}
-		while (f > subs->freqn + subs->freqn / 2) {
-			f >>= 1;
-			shift--;
-		}
-		subs->freqshift = shift;
-	}
-	else if (subs->freqshift >= 0)
-		f <<= subs->freqshift;
-	else
-		f >>= -subs->freqshift;
-
-	if (likely(f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax)) {
-		/*
-		 * If the frequency looks valid, set it.
-		 * This value is referred to in prepare_playback_urb().
-		 */
-		spin_lock_irqsave(&subs->lock, flags);
-		subs->freqm = f;
-		spin_unlock_irqrestore(&subs->lock, flags);
-	} else {
-		/*
-		 * Out of range; maybe the shift value is wrong.
-		 * Reset it so that we autodetect again the next time.
-		 */
-		subs->freqshift = INT_MIN;
-	}
-
-	return 0;
-}
-
-/* determine the number of frames in the next packet */
-static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
-{
-	if (subs->fill_max)
-		return subs->maxframesize;
-	else {
-		subs->phase = (subs->phase & 0xffff)
-			+ (subs->freqm << subs->datainterval);
-		return min(subs->phase >> 16, subs->maxframesize);
-	}
-}
-
-/*
- * Prepare urb for streaming before playback starts or when paused.
- *
- * We don't have any data, so we send silence.
- */
-static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
-				       struct snd_pcm_runtime *runtime,
-				       struct urb *urb)
-{
-	unsigned int i, offs, counts;
-	struct snd_urb_ctx *ctx = urb->context;
-	int stride = runtime->frame_bits >> 3;
-
-	offs = 0;
-	urb->dev = ctx->subs->dev;
-	for (i = 0; i < ctx->packets; ++i) {
-		counts = snd_usb_audio_next_packet_size(subs);
-		urb->iso_frame_desc[i].offset = offs * stride;
-		urb->iso_frame_desc[i].length = counts * stride;
-		offs += counts;
-	}
-	urb->number_of_packets = ctx->packets;
-	urb->transfer_buffer_length = offs * stride;
-	memset(urb->transfer_buffer,
-	       runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
-	       offs * stride);
-	return 0;
-}
-
-/*
- * prepare urb for playback data pipe
- *
- * Since a URB can handle only a single linear buffer, we must use double
- * buffering when the data to be transferred overflows the buffer boundary.
- * To avoid inconsistencies when updating hwptr_done, we use double buffering
- * for all URBs.
- */
-static int prepare_playback_urb(struct snd_usb_substream *subs,
-				struct snd_pcm_runtime *runtime,
-				struct urb *urb)
-{
-	int i, stride;
-	unsigned int counts, frames, bytes;
-	unsigned long flags;
-	int period_elapsed = 0;
-	struct snd_urb_ctx *ctx = urb->context;
+	if (!u->urb)
+		return;
 
-	stride = runtime->frame_bits >> 3;
-
-	frames = 0;
-	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	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);
-		/* set up descriptor */
-		urb->iso_frame_desc[i].offset = frames * stride;
-		urb->iso_frame_desc[i].length = counts * stride;
-		frames += counts;
-		urb->number_of_packets++;
-		subs->transfer_done += counts;
-		if (subs->transfer_done >= runtime->period_size) {
-			subs->transfer_done -= runtime->period_size;
-			period_elapsed = 1;
-			if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
-				if (subs->transfer_done > 0) {
-					/* FIXME: fill-max mode is not
-					 * supported yet */
-					frames -= subs->transfer_done;
-					counts -= subs->transfer_done;
-					urb->iso_frame_desc[i].length =
-						counts * stride;
-					subs->transfer_done = 0;
-				}
-				i++;
-				if (i < ctx->packets) {
-					/* add a transfer delimiter */
-					urb->iso_frame_desc[i].offset =
-						frames * stride;
-					urb->iso_frame_desc[i].length = 0;
-					urb->number_of_packets++;
-				}
-				break;
-			}
-		}
-		if (period_elapsed) /* finish at the period boundary */
-			break;
-	}
-	bytes = frames * stride;
-	if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
-		/* err, the transferred area goes over buffer boundary. */
-		unsigned int bytes1 =
-			runtime->buffer_size * stride - subs->hwptr_done;
-		memcpy(urb->transfer_buffer,
-		       runtime->dma_area + subs->hwptr_done, bytes1);
-		memcpy(urb->transfer_buffer + bytes1,
-		       runtime->dma_area, bytes - bytes1);
-	} else {
-		memcpy(urb->transfer_buffer,
-		       runtime->dma_area + subs->hwptr_done, bytes);
-	}
-	subs->hwptr_done += bytes;
-	if (subs->hwptr_done >= runtime->buffer_size * stride)
-		subs->hwptr_done -= runtime->buffer_size * stride;
-
-	/* update delay with exact number of samples queued */
-	runtime->delay = subs->last_delay;
-	runtime->delay += frames;
-	subs->last_delay = runtime->delay;
-
-	/* realign last_frame_number */
-	subs->last_frame_number = usb_get_current_frame_number(subs->dev);
-	subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
-
-	spin_unlock_irqrestore(&subs->lock, flags);
-	urb->transfer_buffer_length = bytes;
-	if (period_elapsed)
-		snd_pcm_period_elapsed(subs->pcm_substream);
-	return 0;
-}
-
-/*
- * process after playback data complete
- * - decrease the delay count again
- */
-static int retire_playback_urb(struct snd_usb_substream *subs,
-			       struct snd_pcm_runtime *runtime,
-			       struct urb *urb)
-{
-	unsigned long flags;
-	int stride = runtime->frame_bits >> 3;
-	int processed = urb->transfer_buffer_length / stride;
-	int est_delay;
-
-	spin_lock_irqsave(&subs->lock, flags);
-
-	est_delay = snd_usb_pcm_delay(subs, runtime->rate);
-	/* update delay with exact number of samples played */
-	if (processed > subs->last_delay)
-		subs->last_delay = 0;
-	else
-		subs->last_delay -= processed;
-	runtime->delay = subs->last_delay;
-
-	/*
-	 * Report when delay estimate is off by more than 2ms.
-	 * The error should be lower than 2ms since the estimate relies
-	 * on two reads of a counter updated every ms.
-	 */
-	if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2)
-		snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n",
-			est_delay, subs->last_delay);
-
-	spin_unlock_irqrestore(&subs->lock, flags);
-	return 0;
+	if (u->buffer_size)
+		usb_free_coherent(u->ep->chip->dev, u->buffer_size,
+				  u->urb->transfer_buffer,
+				  u->urb->transfer_dma);
+	usb_free_urb(u->urb);
+	u->urb = NULL;
 }
 
 static const char *usb_error_string(int err)
@@ -801,133 +93,6 @@ static const char *usb_error_string(int err)
 	}
 }
 
-/*
- * set up and start data/sync urbs
- */
-static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
-{
-	unsigned int i;
-	int err;
-
-	if (subs->stream->chip->shutdown)
-		return -EBADFD;
-
-	for (i = 0; i < subs->nurbs; i++) {
-		if (snd_BUG_ON(!subs->dataurb[i].urb))
-			return -EINVAL;
-		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;
-		}
-	}
-	if (subs->syncpipe) {
-		for (i = 0; i < SYNC_URBS; i++) {
-			if (snd_BUG_ON(!subs->syncurb[i].urb))
-				return -EINVAL;
-			if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
-				snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
-				goto __error;
-			}
-		}
-	}
-
-	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) {
-			snd_printk(KERN_ERR "cannot submit datapipe "
-				   "for urb %d, error %d: %s\n",
-				   i, err, usb_error_string(err));
-			goto __error;
-		}
-		set_bit(i, &subs->active_mask);
-	}
-	if (subs->syncpipe) {
-		for (i = 0; i < SYNC_URBS; i++) {
-			err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
-			if (err < 0) {
-				snd_printk(KERN_ERR "cannot submit syncpipe "
-					   "for urb %d, error %d: %s\n",
-					   i, err, usb_error_string(err));
-				goto __error;
-			}
-			set_bit(i + 16, &subs->active_mask);
-		}
-	}
-	return 0;
-
- __error:
-	// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
-	deactivate_urbs_old(subs, 0, 0);
-	return -EPIPE;
-}
-
-
-/*
- */
-static struct snd_urb_ops audio_urb_ops[2] = {
-	{
-		.prepare =	prepare_nodata_playback_urb,
-		.retire =	retire_playback_urb,
-		.prepare_sync =	prepare_playback_sync_urb,
-		.retire_sync =	retire_playback_sync_urb,
-	},
-	{
-		.prepare =	prepare_capture_urb,
-		.retire =	retire_capture_urb,
-		.prepare_sync =	prepare_capture_sync_urb,
-		.retire_sync =	retire_capture_sync_urb,
-	},
-};
-
-/*
- * initialize the substream instance.
- */
-
-void snd_usb_init_substream(struct snd_usb_stream *as,
-			    int stream, struct audioformat *fp)
-{
-	struct snd_usb_substream *subs = &as->substream[stream];
-
-	INIT_LIST_HEAD(&subs->fmt_list);
-	spin_lock_init(&subs->lock);
-
-	subs->stream = as;
-	subs->direction = stream;
-	subs->dev = as->chip->dev;
-	subs->txfr_quirk = as->chip->txfr_quirk;
-	subs->ops = audio_urb_ops[stream];
-	if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH)
-		subs->ops.prepare_sync = prepare_capture_sync_urb_hs;
-
-	snd_usb_set_pcm_ops(as->pcm, stream);
-
-	list_add_tail(&fp->list, &subs->fmt_list);
-	subs->formats |= fp->formats;
-	subs->endpoint = fp->endpoint;
-	subs->num_formats++;
-	subs->fmt_type = fp->fmt_type;
-}
-
-int snd_usb_substream_prepare(struct snd_usb_substream *subs,
-			      struct snd_pcm_runtime *runtime)
-{
-	/* clear urbs (to be sure) */
-	deactivate_urbs_old(subs, 0, 1);
-	wait_clear_urbs_old(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);
-	}
-
-	return 0;
-}
-
 int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
 {
 	return  ep->sync_master &&
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index e540768..ee2723f 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -1,21 +1,6 @@
 #ifndef __USBAUDIO_ENDPOINT_H
 #define __USBAUDIO_ENDPOINT_H
 
-void snd_usb_init_substream(struct snd_usb_stream *as,
-			    int stream,
-			    struct audioformat *fp);
-
-int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
-				unsigned int period_bytes,
-				unsigned int rate,
-				unsigned int frame_bits);
-
-void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
-
-int snd_usb_substream_prepare(struct snd_usb_substream *subs,
-			      struct snd_pcm_runtime *runtime);
-
-
 #define SND_USB_ENDPOINT_TYPE_DATA     0
 #define SND_USB_ENDPOINT_TYPE_SYNC     1
 
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 5/5] ALSA: snd-usb: add support for implicit feedback
  2011-12-20 23:34 [PATCH 0/5] snd-usb endpoint rework, take 5 Daniel Mack
                   ` (3 preceding siblings ...)
  2011-12-20 23:34 ` [PATCH 4/5] ALSA: snd-usb: remove old " Daniel Mack
@ 2011-12-20 23:34 ` Daniel Mack
  4 siblings, 0 replies; 20+ messages in thread
From: Daniel Mack @ 2011-12-20 23:34 UTC (permalink / raw)
  To: alsa-devel; +Cc: gdiffey, tiwai, clemens, linuxaudio, Daniel Mack, blablack

Implicit feedback is a streaming mode that does not rely on dedicated
sync endpoints but uses the information provided by record streams to
clock output streams. Now that the streaming logic is decoupled from the
PCM streams, this is easy to implement.

Signed-off-by: Daniel Mack <zonque@gmail.com>
---
 sound/usb/pcm.c |   37 ++++++++++++++++++++++++++-----------
 1 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 37b57eb..c6a1a51 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -297,7 +297,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 	struct usb_interface *iface;
 	unsigned int ep, attr;
 	int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-	int err;
+	int err, implicit_fb = 0;
 
 	iface = usb_ifnum_to_if(dev, fmt->iface);
 	if (WARN_ON(!iface))
@@ -322,25 +322,34 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 	 * assume it as adaptive-out or sync-in.
 	 */
 	attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
-	if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
-	     (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
-	    altsd->bNumEndpoints >= 2) {
-		switch (subs->stream->chip->usb_id) {
-		case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
-		case USB_ID(0x0763, 0x2081):
+
+	switch (subs->stream->chip->usb_id) {
+	case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
+	case USB_ID(0x0763, 0x2081):
+		if (is_playback) {
+			implicit_fb = 1;
 			ep = 0x81;
 			iface = usb_ifnum_to_if(dev, 2);
+
+			if (!iface || iface->num_altsetting == 0)
+				return -EINVAL;
+
 			alts = &iface->altsetting[1];
 			goto add_sync_ep;
 		}
+	}
 
+	if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
+	     (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
+	    altsd->bNumEndpoints >= 2) {
 		/* check sync-pipe endpoint */
 		/* ... and check descriptor size before accessing bSynchAddress
 		   because there is a version of the SB Audigy 2 NX firmware lacking
 		   the audio fields in the endpoint descriptors */
 		if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
 		    (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-		     get_endpoint(alts, 1)->bSynchAddress != 0)) {
+		     get_endpoint(alts, 1)->bSynchAddress != 0 &&
+		     !implicit_fb)) {
 			snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
 				   dev->devnum, fmt->iface, fmt->altsetting);
 			return -EINVAL;
@@ -348,16 +357,22 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 		ep = get_endpoint(alts, 1)->bEndpointAddress;
 		if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
 		    (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
-		     (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
+		     (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)) ||
+		     ( is_playback && !implicit_fb))) {
 			snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
 				   dev->devnum, fmt->iface, fmt->altsetting);
 			return -EINVAL;
 		}
+
+		implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
+				== USB_ENDPOINT_USAGE_IMPLICIT_FB;
+
 add_sync_ep:
 		subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
 							   alts, ep, !subs->direction,
-							   SND_USB_ENDPOINT_TYPE_SYNC);
-
+							   implicit_fb ?
+								SND_USB_ENDPOINT_TYPE_DATA :
+								SND_USB_ENDPOINT_TYPE_SYNC);
 		if (!subs->sync_endpoint)
 			return -EINVAL;
 
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-20 23:34 ` [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model Daniel Mack
@ 2011-12-21 16:16   ` Aurélien Leblond
  2011-12-21 16:34     ` Daniel Mack
  0 siblings, 1 reply; 20+ messages in thread
From: Aurélien Leblond @ 2011-12-21 16:16 UTC (permalink / raw)
  To: Daniel Mack; +Cc: tiwai, alsa-devel, clemens, gdiffey, linuxaudio

Hello Daniel,

I'm now running this new patch with the kernel git 3.2 rc6.

I do get sound through Alsa and Pulse Audio, but Jackd2 crashes at start.
The same version of jack works on 3.2 rc4 with your previous version
of the patch.

Let me know if there is any other info that I should provide you
outside the dmesg trace below.

In dmesg, I get the following:
[   92.185624] BUG: unable to handle kernel NULL pointer dereference
at 0000000000000008
[   92.185637] IP: [<ffffffffa05c9768>]
snd_usb_endpoint_activate+0x18/0x90 [snd_usb_audio]
[   92.185655] PGD 13328b067 PUD 10b118067 PMD 0
[   92.185665] Oops: 0000 [#1] PREEMPT SMP
[   92.185673] CPU 0
[   92.185676] Modules linked in: snd_seq_dummy speedstep_lib
parport_pc ppdev binfmt_misc joydev snd_usb_audio arc4 snd_pcm
brcmsmac fglrx(P) snd_page_alloc snd_hwdep mac80211 brcmutil
snd_usbmidi_lib snd_seq_midi snd_rawmidi snd_seq_midi_event cfg80211
snd_seq crc8 i7core_edac edac_core snd_timer snd_seq_device snd cordic
soundcore psmouse serio_raw hp_wmi sparse_keymap lp parport usbhid hid
uvesafb ahci libahci wmi video
[   92.185747]
[   92.185753] Pid: 2282, comm: jackd Tainted: P           O
3.2.0-rc6-music+ #1 Hewlett-Packard HP ENVY 17 Notebook PC/144D
[   92.185764] RIP: 0010:[<ffffffffa05c9768>]  [<ffffffffa05c9768>]
snd_usb_endpoint_activate+0x18/0x90 [snd_usb_audio]
[   92.185779] RSP: 0018:ffff8801101e3ce8  EFLAGS: 00010246
[   92.185784] RAX: 00000000fffffff0 RBX: ffff8801239221e8 RCX: 00000000008b5700
[   92.185790] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
[   92.185796] RBP: ffff8801101e3cf8 R08: ffffea0004785d00 R09: ffffffffa05caac2
[   92.185802] R10: 0000000000000001 R11: 0000000000000001 R12: 0000000000000000
[   92.185807] R13: ffff880133dd6f38 R14: ffff88011ce6e800 R15: 000000000000bb80
[   92.185815] FS:  00007fd7a8d89740(0000) GS:ffff88013fc00000(0000)
knlGS:0000000000000000
[   92.185821] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[   92.185826] CR2: 0000000000000008 CR3: 000000011d416000 CR4: 00000000000006f0
[   92.185831] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[   92.185836] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[   92.185842] Process jackd (pid: 2282, threadinfo ffff8801101e2000,
task ffff8801101badc0)
[   92.185846] Stack:
[   92.185849]  ffff8801239221e8 ffff880127490f60 ffff8801101e3d58
ffffffffa05d0d1f
[   92.185859]  0000000000000000 0000000000000000 0000000100000001
ffff8801385e3800
[   92.185869]  ffff8801101e3d58 0000000000000000 ffff88013391a200
ffff88011ce6f400
[   92.185878] Call Trace:
[   92.185892]  [<ffffffffa05d0d1f>] snd_usb_hw_params+0x38f/0x5e0
[snd_usb_audio]
[   92.185907]  [<ffffffffa050c4d0>] snd_pcm_hw_params+0xe0/0x3b0 [snd_pcm]
[   92.185919]  [<ffffffffa050d60b>] snd_pcm_common_ioctl1+0x30b/0xcb0 [snd_pcm]
[   92.185932]  [<ffffffffa050dffc>] snd_pcm_capture_ioctl1+0x4c/0x240 [snd_pcm]
[   92.185942]  [<ffffffff81616f00>] ? do_page_fault+0x210/0x5c0
[   92.185954]  [<ffffffffa050e224>] snd_pcm_capture_ioctl+0x34/0x40 [snd_pcm]
[   92.185962]  [<ffffffff81186fef>] do_vfs_ioctl+0x8f/0x500
[   92.185971]  [<ffffffff8117656a>] ? fget_light+0x6a/0x100
[   92.185978]  [<ffffffff811874f1>] sys_ioctl+0x91/0xa0
[   92.185987]  [<ffffffff8161afc2>] system_call_fastpath+0x16/0x1b
[   92.185992] Code: 74 c4 48 89 df e8 99 f1 ff ff 48 83 c4 18 5b 5d
c3 66 90 55 48 89 e5 48 83 ec 10 48 89 5d f0 4c 89 65 f8 66 66 66 66
90 45 31 e4 <8b> 47 08 48 89 fb 85 c0 75 3d 48 8b 07 41 bc f0 ff ff ff
f6 40
[   92.186084] RIP  [<ffffffffa05c9768>]
snd_usb_endpoint_activate+0x18/0x90 [snd_usb_audio]
[   92.186097]  RSP <ffff8801101e3ce8>
[   92.186101] CR2: 0000000000000008
[   92.186151] ---[ end trace aa91e071d7593ce2 ]---




On Tue, Dec 20, 2011 at 11:34 PM, Daniel Mack <zonque@gmail.com> wrote:
> In order to split changes properly, this patch only adds the new
> implementation but leaves the old one around, so the the driver doesn't
> change its behaviour. The switch to actually use the new code is
> submitted separately.
>
> Signed-off-by: Daniel Mack <zonque@gmail.com>
> ---
>  sound/usb/card.h     |   58 ++++
>  sound/usb/endpoint.c |  927 +++++++++++++++++++++++++++++++++++++++++++++++++-
>  sound/usb/endpoint.h |   26 ++
>  sound/usb/usbaudio.h |    1 +
>  4 files changed, 1001 insertions(+), 11 deletions(-)
>
> diff --git a/sound/usb/card.h b/sound/usb/card.h
> index a39edcc..92228f1 100644
> --- a/sound/usb/card.h
> +++ b/sound/usb/card.h
> @@ -29,13 +29,17 @@ struct audioformat {
>  };
>
>  struct snd_usb_substream;
> +struct snd_usb_endpoint;
>
>  struct snd_urb_ctx {
>        struct urb *urb;
>        unsigned int buffer_size;       /* size of data buffer, if data URB */
>        struct snd_usb_substream *subs;
> +       struct snd_usb_endpoint *ep;
>        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 list_head ready_list;
>  };
>
>  struct snd_urb_ops {
> @@ -45,6 +49,60 @@ struct snd_urb_ops {
>        int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
>  };
>
> +struct snd_usb_endpoint {
> +       struct snd_usb_audio *chip;
> +
> +       int use_count;
> +       int ep_num;             /* the referenced endpoint number */
> +       int type;               /* SND_USB_ENDPOINT_TYPE_* */
> +       unsigned long flags;
> +
> +       void (*prepare_data_urb) (struct snd_usb_substream *subs,
> +                                 struct urb *urb);
> +       void (*retire_data_urb) (struct snd_usb_substream *subs,
> +                                struct urb *urb);
> +
> +       struct snd_usb_substream *data_subs;
> +       struct snd_usb_endpoint *sync_master;
> +       struct snd_usb_endpoint *sync_slave;
> +
> +       struct snd_urb_ctx urb[MAX_URBS];
> +
> +       struct snd_usb_packet_info {
> +               uint32_t packet_size[MAX_PACKS_HS];
> +               int packets;
> +       } next_packet[MAX_URBS];
> +       int next_packet_read_pos, next_packet_write_pos;
> +       struct list_head ready_playback_urbs;
> +
> +       unsigned int nurbs;             /* # urbs */
> +       unsigned long active_mask;      /* bitmask of active urbs */
> +       unsigned long unlink_mask;      /* bitmask of unlinked urbs */
> +       char *syncbuf;                  /* sync buffer for all sync URBs */
> +       dma_addr_t sync_dma;            /* DMA address of syncbuf */
> +
> +       unsigned int pipe;              /* the data i/o pipe */
> +       unsigned int freqn;             /* nominal sampling rate in fs/fps in Q16.16 format */
> +       unsigned int freqm;             /* momentary sampling rate in fs/fps in Q16.16 format */
> +       int        freqshift;           /* how much to shift the feedback value to get Q16.16 */
> +       unsigned int freqmax;           /* maximum sampling rate, used for buffer management */
> +       unsigned int phase;             /* phase accumulator */
> +       unsigned int maxpacksize;       /* max packet size in bytes */
> +       unsigned int maxframesize;      /* max packet size in frames */
> +       unsigned int curpacksize;       /* current packet size in bytes (for capture) */
> +       unsigned int curframesize;      /* current packet size in frames (for capture) */
> +       unsigned int syncmaxsize;       /* sync endpoint packet size */
> +       unsigned int fill_max:1;        /* fill max packet size always */
> +       unsigned int datainterval;      /* log_2 of data packet interval */
> +       unsigned int syncinterval;      /* P for adaptive mode, 0 otherwise */
> +       unsigned char silence_value;
> +       unsigned int stride;
> +       int iface, alt_idx;
> +
> +       spinlock_t lock;
> +       struct list_head list;
> +};
> +
>  struct snd_usb_substream {
>        struct snd_usb_stream *stream;
>        struct usb_device *dev;
> diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
> index 81c6ede..3e06fdc 100644
> --- a/sound/usb/endpoint.c
> +++ b/sound/usb/endpoint.c
> @@ -19,9 +19,11 @@
>  #include <linux/init.h>
>  #include <linux/usb.h>
>  #include <linux/usb/audio.h>
> +#include <linux/slab.h>
>
>  #include <sound/core.h>
>  #include <sound/pcm.h>
> +#include <sound/pcm_params.h>
>
>  #include "usbaudio.h"
>  #include "helper.h"
> @@ -29,6 +31,9 @@
>  #include "endpoint.h"
>  #include "pcm.h"
>
> +#define EP_FLAG_ACTIVATED      0
> +#define EP_FLAG_RUNNING                1
> +
>  /*
>  * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
>  * this will overflow at approx 524 kHz
> @@ -50,7 +55,7 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
>  /*
>  * unlink active urbs.
>  */
> -static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
> +static int deactivate_urbs_old(struct snd_usb_substream *subs, int force, int can_sleep)
>  {
>        struct snd_usb_audio *chip = subs->stream->chip;
>        unsigned int i;
> @@ -112,7 +117,7 @@ static void release_urb_ctx(struct snd_urb_ctx *u)
>  /*
>  *  wait until all urbs are processed.
>  */
> -static int wait_clear_urbs(struct snd_usb_substream *subs)
> +static int wait_clear_urbs_old(struct snd_usb_substream *subs)
>  {
>        unsigned long end_time = jiffies + msecs_to_jiffies(1000);
>        unsigned int i;
> @@ -147,8 +152,8 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
>        int i;
>
>        /* stop urbs (to be sure) */
> -       deactivate_urbs(subs, force, 1);
> -       wait_clear_urbs(subs);
> +       deactivate_urbs_old(subs, force, 1);
> +       wait_clear_urbs_old(subs);
>
>        for (i = 0; i < MAX_URBS; i++)
>                release_urb_ctx(&subs->dataurb[i]);
> @@ -163,7 +168,7 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
>  /*
>  * complete callback from data urb
>  */
> -static void snd_complete_urb(struct urb *urb)
> +static void snd_complete_urb_old(struct urb *urb)
>  {
>        struct snd_urb_ctx *ctx = urb->context;
>        struct snd_usb_substream *subs = ctx->subs;
> @@ -317,7 +322,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
>                u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
>                u->urb->interval = 1 << subs->datainterval;
>                u->urb->context = u;
> -               u->urb->complete = snd_complete_urb;
> +               u->urb->complete = snd_complete_urb_old;
>        }
>
>        if (subs->syncpipe) {
> @@ -855,7 +860,7 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru
>
>  __error:
>        // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
> -       deactivate_urbs(subs, 0, 0);
> +       deactivate_urbs_old(subs, 0, 0);
>        return -EPIPE;
>  }
>
> @@ -916,7 +921,7 @@ 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:
> -               return deactivate_urbs(subs, 0, 0);
> +               return deactivate_urbs_old(subs, 0, 0);
>        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>                subs->ops.prepare = prepare_nodata_playback_urb;
>                return 0;
> @@ -934,7 +939,7 @@ int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int c
>                subs->ops.retire = retire_capture_urb;
>                return start_urbs(subs, substream->runtime);
>        case SNDRV_PCM_TRIGGER_STOP:
> -               return deactivate_urbs(subs, 0, 0);
> +               return deactivate_urbs_old(subs, 0, 0);
>        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>                subs->ops.retire = retire_paused_capture_urb;
>                return 0;
> @@ -950,8 +955,8 @@ 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);
> +       deactivate_urbs_old(subs, 0, 1);
> +       wait_clear_urbs_old(subs);
>
>        /* for playback, submit the URBs now; otherwise, the first hwptr_done
>         * updates for all URBs would happen at the same time when starting */
> @@ -963,3 +968,903 @@ int snd_usb_substream_prepare(struct snd_usb_substream *subs,
>        return 0;
>  }
>
> +int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
> +{
> +       return  ep->sync_master &&
> +               ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA &&
> +               ep->type == SND_USB_ENDPOINT_TYPE_DATA &&
> +               usb_pipeout(ep->pipe);
> +}
> +
> +/* determine the number of frames in the next packet */
> +static int next_packet_size(struct snd_usb_endpoint *ep)
> +{
> +       unsigned long flags;
> +       int ret;
> +
> +       if (ep->fill_max)
> +               return ep->maxframesize;
> +
> +       spin_lock_irqsave(&ep->lock, flags);
> +       ep->phase = (ep->phase & 0xffff)
> +               + (ep->freqm << ep->datainterval);
> +       ret = min(ep->phase >> 16, ep->maxframesize);
> +       spin_unlock_irqrestore(&ep->lock, flags);
> +
> +       return ret;
> +}
> +
> +static void retire_outbound_urb(struct snd_usb_endpoint *ep,
> +                               struct snd_urb_ctx *urb_ctx)
> +{
> +       if (ep->retire_data_urb)
> +               ep->retire_data_urb(ep->data_subs, urb_ctx->urb);
> +}
> +
> +static void retire_inbound_urb(struct snd_usb_endpoint *ep,
> +                              struct snd_urb_ctx *urb_ctx)
> +{
> +       struct urb *urb = urb_ctx->urb;
> +
> +       if (ep->sync_slave)
> +               snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
> +
> +       if (ep->retire_data_urb)
> +               ep->retire_data_urb(ep->data_subs, urb);
> +}
> +
> +static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep,
> +                                      struct snd_urb_ctx *ctx)
> +{
> +       int i;
> +
> +       for (i = 0; i < ctx->packets; ++i)
> +               ctx->packet_size[i] = next_packet_size(ep);
> +}
> +
> +/*
> + * Prepare a PLAYBACK urb for submission to the bus.
> + */
> +static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
> +                                struct snd_urb_ctx *ctx)
> +{
> +       int i;
> +       struct urb *urb = ctx->urb;
> +       unsigned char *cp = urb->transfer_buffer;
> +
> +       urb->dev = ep->chip->dev; /* we need to set this at each time */
> +
> +       switch (ep->type) {
> +       case SND_USB_ENDPOINT_TYPE_DATA:
> +               if (ep->prepare_data_urb) {
> +                       ep->prepare_data_urb(ep->data_subs, urb);
> +               } else {
> +                       /* no data provider, so send silence */
> +                       unsigned int offs = 0;
> +                       for (i = 0; i < ctx->packets; ++i) {
> +                               int counts = ctx->packet_size[i];
> +                               urb->iso_frame_desc[i].offset = offs * ep->stride;
> +                               urb->iso_frame_desc[i].length = counts * ep->stride;
> +                               offs += counts;
> +                       }
> +
> +                       urb->number_of_packets = ctx->packets;
> +                       urb->transfer_buffer_length = offs * ep->stride;
> +                       memset(urb->transfer_buffer, ep->silence_value,
> +                              offs * ep->stride);
> +               }
> +               break;
> +
> +       case SND_USB_ENDPOINT_TYPE_SYNC:
> +               if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
> +                       /*
> +                        * fill the length and offset of each urb descriptor.
> +                        * the fixed 12.13 frequency is passed as 16.16 through the pipe.
> +                        */
> +                       urb->iso_frame_desc[0].length = 4;
> +                       urb->iso_frame_desc[0].offset = 0;
> +                       cp[0] = ep->freqn;
> +                       cp[1] = ep->freqn >> 8;
> +                       cp[2] = ep->freqn >> 16;
> +                       cp[3] = ep->freqn >> 24;
> +               } else {
> +                       /*
> +                        * fill the length and offset of each urb descriptor.
> +                        * the fixed 10.14 frequency is passed through the pipe.
> +                        */
> +                       urb->iso_frame_desc[0].length = 3;
> +                       urb->iso_frame_desc[0].offset = 0;
> +                       cp[0] = ep->freqn >> 2;
> +                       cp[1] = ep->freqn >> 10;
> +                       cp[2] = ep->freqn >> 18;
> +               }
> +
> +               break;
> +       }
> +}
> +
> +/*
> + * Prepare a CAPTURE or SYNC urb for submission to the bus.
> + */
> +static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
> +                                      struct snd_urb_ctx *urb_ctx)
> +{
> +       int i, offs;
> +       struct urb *urb = urb_ctx->urb;
> +
> +       urb->dev = ep->chip->dev; /* we need to set this at each time */
> +
> +       switch (ep->type) {
> +       case SND_USB_ENDPOINT_TYPE_DATA:
> +               offs = 0;
> +               for (i = 0; i < urb_ctx->packets; i++) {
> +                       urb->iso_frame_desc[i].offset = offs;
> +                       urb->iso_frame_desc[i].length = ep->curpacksize;
> +                       offs += ep->curpacksize;
> +               }
> +
> +               urb->transfer_buffer_length = offs;
> +               urb->number_of_packets = urb_ctx->packets;
> +               break;
> +
> +       case SND_USB_ENDPOINT_TYPE_SYNC:
> +               urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize);
> +               urb->iso_frame_desc[0].offset = 0;
> +               break;
> +       }
> +}
> +
> +static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
> +{
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&ep->lock, flags);
> +
> +       while (test_bit(EP_FLAG_RUNNING, &ep->flags) &&
> +              !list_empty(&ep->ready_playback_urbs) &&
> +              ep->next_packet_read_pos != ep->next_packet_write_pos) {
> +
> +               struct snd_usb_packet_info *packet;
> +               struct snd_urb_ctx *ctx;
> +               struct urb *urb;
> +               int err, i;
> +
> +               packet = ep->next_packet + ep->next_packet_read_pos;
> +               ep->next_packet_read_pos++;
> +               ep->next_packet_read_pos %= MAX_URBS;
> +
> +               /* take URB out of FIFO */
> +               ctx = list_first_entry(&ep->ready_playback_urbs,
> +                                      struct snd_urb_ctx, ready_list);
> +               list_del(&ctx->ready_list);
> +               urb = ctx->urb;
> +
> +               /* copy over the length information */
> +               for (i = 0; i < packet->packets; i++)
> +                       ctx->packet_size[i] = packet->packet_size[i];
> +
> +               prepare_outbound_urb(ep, ctx);
> +
> +               err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
> +               if (err < 0)
> +                       snd_printk(KERN_ERR "Unable to submit urb #%d: %d (urb %p)\n",
> +                                  ctx->index, err, ctx->urb);
> +               else
> +                       set_bit(ctx->index, &ep->active_mask);
> +       }
> +
> +       spin_unlock_irqrestore(&ep->lock, flags);
> +}
> +
> +/*
> + * complete callback for urbs
> + */
> +static void snd_complete_urb(struct urb *urb)
> +{
> +       struct snd_urb_ctx *ctx = urb->context;
> +       struct snd_usb_endpoint *ep = ctx->ep;
> +       unsigned long flags;
> +       int err;
> +
> +       if (unlikely(urb->status == -ENOENT ||          /* unlinked */
> +                    urb->status == -ENODEV ||          /* device removed */
> +                    urb->status == -ECONNRESET ||      /* unlinked */
> +                    urb->status == -ESHUTDOWN))        /* device disabled */
> +               goto exit_clear;
> +
> +       if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
> +               goto exit_clear;
> +
> +       if (usb_pipeout(ep->pipe)) {
> +               retire_outbound_urb(ep, ctx);
> +               /* can be stopped during retire callback */
> +               if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
> +                       goto exit_clear;
> +
> +               if (snd_usb_endpoint_implict_feedback_sink(ep)) {
> +                       clear_bit(ctx->index, &ep->active_mask);
> +                       spin_lock_irqsave(&ep->lock, flags);
> +                       list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
> +                       spin_unlock_irqrestore(&ep->lock, flags);
> +                       queue_pending_output_urbs(ep);
> +                       return;
> +               }
> +
> +               prepare_outbound_urb_sizes(ep, ctx);
> +               prepare_outbound_urb(ep, ctx);
> +       } else {
> +               retire_inbound_urb(ep, ctx);
> +               /* can be stopped during retire callback */
> +               if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
> +                       goto exit_clear;
> +
> +               prepare_inbound_urb(ep, ctx);
> +       }
> +
> +       err = usb_submit_urb(urb, GFP_ATOMIC);
> +       if (err == 0)
> +               return;
> +
> +       snd_printk(KERN_ERR "cannot submit urb (err = %d)\n", err);
> +       //snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
> +
> +exit_clear:
> +       clear_bit(ctx->index, &ep->active_mask);
> +}
> +
> +struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
> +                                             struct usb_host_interface *alts,
> +                                             int ep_num, int direction, int type)
> +{
> +       struct list_head *p;
> +       struct snd_usb_endpoint *ep;
> +       int ret, is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
> +
> +       mutex_lock(&chip->mutex);
> +
> +       list_for_each(p, &chip->ep_list) {
> +               ep = list_entry(p, struct snd_usb_endpoint, list);
> +               if (ep->ep_num == ep_num &&
> +                   ep->iface == alts->desc.bInterfaceNumber &&
> +                   ep->alt_idx == alts->desc.bAlternateSetting) {
> +                       snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n",
> +                                       ep_num, ep->iface, ep->alt_idx, ep);
> +                       goto __exit_unlock;
> +               }
> +       }
> +
> +       snd_printdd(KERN_DEBUG "Creating new %s %s endpoint #%x\n",
> +                  is_playback ? "playback" : "capture",
> +                  type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync",
> +                  ep_num);
> +
> +       /* select the alt setting once so the endpoints become valid */
> +       ret = usb_set_interface(chip->dev, alts->desc.bInterfaceNumber,
> +                               alts->desc.bAlternateSetting);
> +       if (ret < 0) {
> +               snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
> +                                       __func__, ret);
> +               ep = NULL;
> +               goto __exit_unlock;
> +       }
> +
> +       ep = kzalloc(sizeof(*ep), GFP_KERNEL);
> +       if (!ep)
> +               goto __exit_unlock;
> +
> +       ep->chip = chip;
> +       spin_lock_init(&ep->lock);
> +       ep->type = type;
> +       ep->ep_num = ep_num;
> +       ep->iface = alts->desc.bInterfaceNumber;
> +       ep->alt_idx = alts->desc.bAlternateSetting;
> +       INIT_LIST_HEAD(&ep->ready_playback_urbs);
> +       ep_num &= USB_ENDPOINT_NUMBER_MASK;
> +
> +       if (is_playback)
> +               ep->pipe = usb_sndisocpipe(chip->dev, ep_num);
> +       else
> +               ep->pipe = usb_rcvisocpipe(chip->dev, ep_num);
> +
> +       if (type == SND_USB_ENDPOINT_TYPE_SYNC) {
> +               if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
> +                   get_endpoint(alts, 1)->bRefresh >= 1 &&
> +                   get_endpoint(alts, 1)->bRefresh <= 9)
> +                       ep->syncinterval = get_endpoint(alts, 1)->bRefresh;
> +               else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL)
> +                       ep->syncinterval = 1;
> +               else if (get_endpoint(alts, 1)->bInterval >= 1 &&
> +                        get_endpoint(alts, 1)->bInterval <= 16)
> +                       ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
> +               else
> +                       ep->syncinterval = 3;
> +
> +               ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
> +       }
> +
> +       list_add_tail(&ep->list, &chip->ep_list);
> +
> +__exit_unlock:
> +       mutex_unlock(&chip->mutex);
> +
> +       return ep;
> +}
> +
> +/*
> + *  wait until all urbs are processed.
> + */
> +static int wait_clear_urbs(struct snd_usb_endpoint *ep)
> +{
> +       unsigned long end_time = jiffies + msecs_to_jiffies(1000);
> +       unsigned int i;
> +       int alive;
> +
> +       do {
> +               alive = 0;
> +               for (i = 0; i < ep->nurbs; i++)
> +                       if (test_bit(i, &ep->active_mask))
> +                               alive++;
> +
> +               if (!alive)
> +                       break;
> +
> +               schedule_timeout_uninterruptible(1);
> +       } while (time_before(jiffies, end_time));
> +
> +       if (alive)
> +               snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n",
> +                                       alive, ep->ep_num);
> +
> +       return 0;
> +}
> +
> +/*
> + * unlink active urbs.
> + */
> +static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep)
> +{
> +       unsigned long flags;
> +       unsigned int i;
> +       int async;
> +
> +       if (!force && ep->chip->shutdown) /* to be sure... */
> +               return -EBADFD;
> +
> +       async = !can_sleep && ep->chip->async_unlink;
> +
> +       clear_bit(EP_FLAG_RUNNING, &ep->flags);
> +
> +       spin_lock_irqsave(&ep->lock, flags);
> +       INIT_LIST_HEAD(&ep->ready_playback_urbs);
> +       ep->next_packet_read_pos = 0;
> +       ep->next_packet_write_pos = 0;
> +       spin_unlock_irqrestore(&ep->lock, flags);
> +
> +       if (!async && in_interrupt())
> +               return 0;
> +
> +       for (i = 0; i < ep->nurbs; i++) {
> +               if (test_bit(i, &ep->active_mask)) {
> +                       if (!test_and_set_bit(i, &ep->unlink_mask)) {
> +                               struct urb *u = ep->urb[i].urb;
> +                               if (async)
> +                                       usb_unlink_urb(u);
> +                               else
> +                                       usb_kill_urb(u);
> +                       }
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * release an endpoint's urbs
> + */
> +static void release_urbs(struct snd_usb_endpoint *ep, int force)
> +{
> +       int i;
> +
> +       /* route incoming urbs to nirvana */
> +       ep->retire_data_urb = NULL;
> +       ep->prepare_data_urb = NULL;
> +
> +       /* stop urbs */
> +       deactivate_urbs(ep, force, 1);
> +       wait_clear_urbs(ep);
> +
> +       for (i = 0; i < ep->nurbs; i++)
> +               release_urb_ctx(&ep->urb[i]);
> +
> +       if (ep->syncbuf)
> +               usb_free_coherent(ep->chip->dev, SYNC_URBS * 4,
> +                                 ep->syncbuf, ep->sync_dma);
> +
> +       ep->syncbuf = NULL;
> +       ep->nurbs = 0;
> +}
> +
> +static int data_ep_set_params(struct snd_usb_endpoint *ep,
> +                             struct snd_pcm_hw_params *hw_params,
> +                             struct audioformat *fmt,
> +                             struct snd_usb_endpoint *sync_ep)
> +{
> +       unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
> +       int period_bytes = params_period_bytes(hw_params);
> +       int format = params_format(hw_params);
> +       int is_playback = usb_pipeout(ep->pipe);
> +       int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) *
> +                                                       params_channels(hw_params);
> +
> +       ep->datainterval = fmt->datainterval;
> +       ep->stride = frame_bits >> 3;
> +       ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
> +
> +       /* calculate max. frequency */
> +       if (ep->maxpacksize) {
> +               /* whatever fits into a max. size packet */
> +               maxsize = ep->maxpacksize;
> +               ep->freqmax = (maxsize / (frame_bits >> 3))
> +                               << (16 - ep->datainterval);
> +       } else {
> +               /* no max. packet size: just take 25% higher than nominal */
> +               ep->freqmax = ep->freqn + (ep->freqn >> 2);
> +               maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
> +                               >> (16 - ep->datainterval);
> +       }
> +
> +       if (ep->fill_max)
> +               ep->curpacksize = ep->maxpacksize;
> +       else
> +               ep->curpacksize = maxsize;
> +
> +       if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL)
> +               packs_per_ms = 8 >> ep->datainterval;
> +       else
> +               packs_per_ms = 1;
> +
> +       if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
> +               urb_packs = max(ep->chip->nrpacks, 1);
> +               urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
> +       } else {
> +               urb_packs = 1;
> +       }
> +
> +       urb_packs *= packs_per_ms;
> +
> +       if (sync_ep && !snd_usb_endpoint_implict_feedback_sink(ep))
> +               urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
> +
> +       /* decide how many packets to be used */
> +       if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
> +               unsigned int minsize, maxpacks;
> +               /* determine how small a packet can be */
> +               minsize = (ep->freqn >> (16 - ep->datainterval))
> +                         * (frame_bits >> 3);
> +               /* with sync from device, assume it can be 12% lower */
> +               if (sync_ep)
> +                       minsize -= minsize >> 3;
> +               minsize = max(minsize, 1u);
> +               total_packs = (period_bytes + minsize - 1) / minsize;
> +               /* we need at least two URBs for queueing */
> +               if (total_packs < 2) {
> +                       total_packs = 2;
> +               } else {
> +                       /* and we don't want too long a queue either */
> +                       maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
> +                       total_packs = min(total_packs, maxpacks);
> +               }
> +       } else {
> +               while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
> +                       urb_packs >>= 1;
> +               total_packs = MAX_URBS * urb_packs;
> +       }
> +
> +       ep->nurbs = (total_packs + urb_packs - 1) / urb_packs;
> +       if (ep->nurbs > MAX_URBS) {
> +               /* too much... */
> +               ep->nurbs = MAX_URBS;
> +               total_packs = MAX_URBS * urb_packs;
> +       } else if (ep->nurbs < 2) {
> +               /* too little - we need at least two packets
> +                * to ensure contiguous playback/capture
> +                */
> +               ep->nurbs = 2;
> +       }
> +
> +       /* allocate and initialize data urbs */
> +       for (i = 0; i < ep->nurbs; i++) {
> +               struct snd_urb_ctx *u = &ep->urb[i];
> +               u->index = i;
> +               u->ep = ep;
> +               u->packets = (i + 1) * total_packs / ep->nurbs
> +                       - i * total_packs / ep->nurbs;
> +               u->buffer_size = maxsize * u->packets;
> +
> +               if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
> +                       u->packets++; /* for transfer delimiter */
> +               u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
> +               if (!u->urb)
> +                       goto out_of_memory;
> +
> +               u->urb->transfer_buffer =
> +                       usb_alloc_coherent(ep->chip->dev, u->buffer_size,
> +                                          GFP_KERNEL, &u->urb->transfer_dma);
> +               if (!u->urb->transfer_buffer)
> +                       goto out_of_memory;
> +               u->urb->pipe = ep->pipe;
> +               u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
> +               u->urb->interval = 1 << ep->datainterval;
> +               u->urb->context = u;
> +               u->urb->complete = snd_complete_urb;
> +       }
> +
> +       return 0;
> +
> +out_of_memory:
> +       release_urbs(ep, 0);
> +       return -ENOMEM;
> +}
> +
> +static int sync_ep_set_params(struct snd_usb_endpoint *ep,
> +                             struct snd_pcm_hw_params *hw_params,
> +                             struct audioformat *fmt)
> +{
> +       int i;
> +
> +       ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
> +                                        GFP_KERNEL, &ep->sync_dma);
> +       if (!ep->syncbuf)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < SYNC_URBS; i++) {
> +               struct snd_urb_ctx *u = &ep->urb[i];
> +               u->index = i;
> +               u->ep = ep;
> +               u->packets = 1;
> +               u->urb = usb_alloc_urb(1, GFP_KERNEL);
> +               if (!u->urb)
> +                       goto out_of_memory;
> +               u->urb->transfer_buffer = ep->syncbuf + i * 4;
> +               u->urb->transfer_dma = ep->sync_dma + i * 4;
> +               u->urb->transfer_buffer_length = 4;
> +               u->urb->pipe = ep->pipe;
> +               u->urb->transfer_flags = URB_ISO_ASAP |
> +                                        URB_NO_TRANSFER_DMA_MAP;
> +               u->urb->number_of_packets = 1;
> +               u->urb->interval = 1 << ep->syncinterval;
> +               u->urb->context = u;
> +               u->urb->complete = snd_complete_urb;
> +       }
> +
> +       ep->nurbs = SYNC_URBS;
> +
> +       return 0;
> +
> +out_of_memory:
> +       release_urbs(ep, 0);
> +       return -ENOMEM;
> +}
> +
> +int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
> +                               struct snd_pcm_hw_params *hw_params,
> +                               struct audioformat *fmt,
> +                               struct snd_usb_endpoint *sync_ep)
> +{
> +       int err;
> +
> +       if (ep->use_count != 0) {
> +               snd_printk(KERN_WARNING "Unable to change format on ep #%x: already in use\n",
> +                          ep->ep_num);
> +               return -EBUSY;
> +       }
> +
> +       /* release old buffers, if any */
> +       release_urbs(ep, 0);
> +
> +       ep->datainterval = fmt->datainterval;
> +       ep->maxpacksize = fmt->maxpacksize;
> +       ep->fill_max = fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX;
> +
> +       if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
> +               ep->freqn = get_usb_full_speed_rate(params_rate(hw_params));
> +       else
> +               ep->freqn = get_usb_high_speed_rate(params_rate(hw_params));
> +
> +       /* calculate the frequency in 16.16 format */
> +       ep->freqm = ep->freqn;
> +       ep->freqshift = INT_MIN;
> +
> +       ep->phase = 0;
> +
> +       switch (ep->type) {
> +       case  SND_USB_ENDPOINT_TYPE_DATA:
> +               err = data_ep_set_params(ep, hw_params, fmt, sync_ep);
> +               break;
> +       case  SND_USB_ENDPOINT_TYPE_SYNC:
> +               err = sync_ep_set_params(ep, hw_params, fmt);
> +               break;
> +       default:
> +               err = -EINVAL;
> +       }
> +
> +       snd_printdd(KERN_DEBUG "Setting params for ep #%x (type %d, %d urbs), ret=%d\n",
> +                  ep->ep_num, ep->type, ep->nurbs, err);
> +
> +       return err;
> +}
> +
> +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
> +{
> +       int err;
> +       unsigned int i;
> +
> +       if (ep->chip->shutdown)
> +               return -EBADFD;
> +
> +       /* already running? */
> +       if (++ep->use_count != 1)
> +               return 0;
> +
> +       if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
> +               return -EINVAL;
> +
> +       /* just to be sure */
> +       deactivate_urbs(ep, 0, 1);
> +       wait_clear_urbs(ep);
> +
> +       ep->active_mask = 0;
> +       ep->unlink_mask = 0;
> +       ep->phase = 0;
> +
> +       /*
> +        * If this endpoint has a data endpoint as implicit feedback source,
> +        * don't start the urbs here. Instead, mark them all as available,
> +        * wait for the record urbs to arrive and queue from that context.
> +        */
> +
> +       set_bit(EP_FLAG_RUNNING, &ep->flags);
> +
> +       if (snd_usb_endpoint_implict_feedback_sink(ep)) {
> +               for (i = 0; i < ep->nurbs; i++) {
> +                       struct snd_urb_ctx *ctx = ep->urb + i;
> +                       list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
> +               }
> +
> +               return 0;
> +       }
> +
> +       for (i = 0; i < ep->nurbs; i++) {
> +               struct urb *urb = ep->urb[i].urb;
> +
> +               if (snd_BUG_ON(!urb))
> +                       goto __error;
> +
> +               if (usb_pipeout(ep->pipe)) {
> +                       prepare_outbound_urb_sizes(ep, urb->context);
> +                       prepare_outbound_urb(ep, urb->context);
> +               } else {
> +                       prepare_inbound_urb(ep, urb->context);
> +               }
> +
> +               err = usb_submit_urb(urb, GFP_ATOMIC);
> +               if (err < 0) {
> +                       snd_printk(KERN_ERR "cannot submit urb %d, error %d: %s\n",
> +                                  i, err, usb_error_string(err));
> +                       goto __error;
> +               }
> +               set_bit(i, &ep->active_mask);
> +       }
> +
> +       return 0;
> +
> +__error:
> +       clear_bit(EP_FLAG_RUNNING, &ep->flags);
> +       ep->use_count--;
> +       deactivate_urbs(ep, 0, 0);
> +       return -EPIPE;
> +}
> +
> +void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
> +                          int force, int can_sleep, int wait)
> +{
> +       if (!ep)
> +               return;
> +
> +       if (snd_BUG_ON(ep->use_count == 0))
> +               return;
> +
> +       if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
> +               return;
> +
> +       if (--ep->use_count == 0) {
> +               deactivate_urbs(ep, force, can_sleep);
> +               ep->data_subs = NULL;
> +               ep->sync_slave = NULL;
> +               ep->retire_data_urb = NULL;
> +               ep->prepare_data_urb = NULL;
> +
> +               if (wait)
> +                       wait_clear_urbs(ep);
> +       }
> +}
> +
> +int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep)
> +{
> +       if (ep->use_count != 0)
> +               return 0;
> +
> +       if (!ep->chip->shutdown &&
> +           !test_and_set_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
> +               int ret;
> +
> +               ret = usb_set_interface(ep->chip->dev, ep->iface, ep->alt_idx);
> +               if (ret < 0) {
> +                       snd_printk(KERN_ERR "%s() usb_set_interface() failed, ret = %d\n",
> +                                               __func__, ret);
> +                       clear_bit(EP_FLAG_ACTIVATED, &ep->flags);
> +                       return ret;
> +               }
> +
> +               return 0;
> +       }
> +
> +       return -EBUSY;
> +}
> +
> +int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
> +{
> +       if (!ep)
> +               return -EINVAL;
> +
> +       if (ep->use_count != 0)
> +               return 0;
> +
> +       if (!ep->chip->shutdown &&
> +           test_and_clear_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
> +               int ret;
> +
> +               ret = usb_set_interface(ep->chip->dev, ep->iface, 0);
> +               if (ret < 0) {
> +                       snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
> +                                               __func__, ret);
> +                       return ret;
> +               }
> +
> +               return 0;
> +       }
> +
> +       return -EBUSY;
> +}
> +
> +void snd_usb_endpoint_free(struct list_head *head)
> +{
> +       struct snd_usb_endpoint *ep;
> +
> +       ep = list_entry(head, struct snd_usb_endpoint, list);
> +       release_urbs(ep, 1);
> +       kfree(ep);
> +}
> +
> +/*
> + * process after playback sync complete
> + *
> + * Full speed devices report feedback values in 10.14 format as samples per
> + * frame, high speed devices in 16.16 format as samples per microframe.
> + * Because the Audio Class 1 spec was written before USB 2.0, many high speed
> + * devices use a wrong interpretation, some others use an entirely different
> + * format.  Therefore, we cannot predict what format any particular device uses
> + * and must detect it automatically.
> + */
> +void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
> +                            struct snd_usb_endpoint *sender,
> +                            const struct urb *urb)
> +{
> +       int shift;
> +       unsigned int f;
> +       unsigned long flags;
> +
> +       snd_BUG_ON(ep == sender);
> +
> +       if (snd_usb_endpoint_implict_feedback_sink(ep) &&
> +           ep->use_count != 0) {
> +
> +               /* implicit feedback case */
> +               int i, bytes = 0;
> +               struct snd_urb_ctx *in_ctx;
> +               struct snd_usb_packet_info *out_packet;
> +
> +               in_ctx = urb->context;
> +
> +               /* Count overall packet size */
> +               for (i = 0; i < in_ctx->packets; i++)
> +                       if (urb->iso_frame_desc[i].status == 0)
> +                               bytes += 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;
> +
> +               spin_lock_irqsave(&ep->lock, flags);
> +               out_packet = ep->next_packet + ep->next_packet_write_pos;
> +
> +               /*
> +                * Iterate through the inbound packet and prepare the lengths
> +                * for the output packet. The OUT packet we are about to send
> +                * will have the same amount of payload than the IN packet we
> +                * just received.
> +                */
> +
> +               out_packet->packets = in_ctx->packets;
> +               for (i = 0; i < in_ctx->packets; i++) {
> +                       if (urb->iso_frame_desc[i].status == 0)
> +                               out_packet->packet_size[i] =
> +                                       urb->iso_frame_desc[i].actual_length / ep->stride;
> +                       else
> +                               out_packet->packet_size[i] = 0;
> +               }
> +
> +               ep->next_packet_write_pos++;
> +               ep->next_packet_write_pos %= MAX_URBS;
> +               spin_unlock_irqrestore(&ep->lock, flags);
> +               queue_pending_output_urbs(ep);
> +
> +               return;
> +       }
> +
> +       if (urb->iso_frame_desc[0].status != 0 ||
> +           urb->iso_frame_desc[0].actual_length < 3)
> +               return;
> +
> +       f = le32_to_cpup(urb->transfer_buffer);
> +       if (urb->iso_frame_desc[0].actual_length == 3)
> +               f &= 0x00ffffff;
> +       else
> +               f &= 0x0fffffff;
> +
> +       if (f == 0)
> +               return;
> +
> +       if (unlikely(ep->freqshift == INT_MIN)) {
> +               /*
> +                * The first time we see a feedback value, determine its format
> +                * by shifting it left or right until it matches the nominal
> +                * frequency value.  This assumes that the feedback does not
> +                * differ from the nominal value more than +50% or -25%.
> +                */
> +               shift = 0;
> +               while (f < ep->freqn - ep->freqn / 4) {
> +                       f <<= 1;
> +                       shift++;
> +               }
> +               while (f > ep->freqn + ep->freqn / 2) {
> +                       f >>= 1;
> +                       shift--;
> +               }
> +               ep->freqshift = shift;
> +       }
> +       else if (ep->freqshift >= 0)
> +               f <<= ep->freqshift;
> +       else
> +               f >>= -ep->freqshift;
> +
> +       if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) {
> +               /*
> +                * If the frequency looks valid, set it.
> +                * This value is referred to in prepare_playback_urb().
> +                */
> +               spin_lock_irqsave(&ep->lock, flags);
> +               ep->freqm = f;
> +               spin_unlock_irqrestore(&ep->lock, flags);
> +       } else {
> +               /*
> +                * Out of range; maybe the shift value is wrong.
> +                * Reset it so that we autodetect again the next time.
> +                */
> +               ep->freqshift = INT_MIN;
> +       }
> +}
> +
> diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
> index 88eb63a..9f083d7 100644
> --- a/sound/usb/endpoint.h
> +++ b/sound/usb/endpoint.h
> @@ -18,4 +18,30 @@ 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);
>
> +
> +#define SND_USB_ENDPOINT_TYPE_DATA     0
> +#define SND_USB_ENDPOINT_TYPE_SYNC     1
> +
> +struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
> +                                             struct usb_host_interface *alts,
> +                                             int ep_num, int direction, int type);
> +
> +int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
> +                               struct snd_pcm_hw_params *hw_params,
> +                               struct audioformat *fmt,
> +                               struct snd_usb_endpoint *sync_ep);
> +
> +int  snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
> +void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
> +                          int force, int can_sleep, int wait);
> +int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
> +int  snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
> +void snd_usb_endpoint_free(struct list_head *head);
> +
> +int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep);
> +
> +void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
> +                            struct snd_usb_endpoint *sender,
> +                            const struct urb *urb);
> +
>  #endif /* __USBAUDIO_ENDPOINT_H */
> diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
> index a16c21d..b8233eb 100644
> --- a/sound/usb/usbaudio.h
> +++ b/sound/usb/usbaudio.h
> @@ -47,6 +47,7 @@ struct snd_usb_audio {
>        int num_suspended_intf;
>
>        struct list_head pcm_list;      /* list of pcm streams */
> +       struct list_head ep_list;       /* list of audio-related endpoints */
>        int pcm_devs;
>
>        struct list_head midi_list;     /* list of midi interfaces */
> --
> 1.7.5.4
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-21 16:16   ` Aurélien Leblond
@ 2011-12-21 16:34     ` Daniel Mack
  2011-12-21 17:11       ` Aurélien Leblond
  0 siblings, 1 reply; 20+ messages in thread
From: Daniel Mack @ 2011-12-21 16:34 UTC (permalink / raw)
  To: Aurélien Leblond; +Cc: tiwai, alsa-devel, clemens, gdiffey, linuxaudio

On 12/21/2011 05:16 PM, Aurélien Leblond wrote:
> Hello Daniel,
> 
> I'm now running this new patch with the kernel git 3.2 rc6.
> 
> I do get sound through Alsa and Pulse Audio, but Jackd2 crashes at start.
> The same version of jack works on 3.2 rc4 with your previous version
> of the patch.
> 
> Let me know if there is any other info that I should provide you
> outside the dmesg trace below.
> 
> In dmesg, I get the following:
> [   92.185624] BUG: unable to handle kernel NULL pointer dereference
> at 0000000000000008
> [   92.185637] IP: [<ffffffffa05c9768>]
> snd_usb_endpoint_activate+0x18/0x90 [snd_usb_audio]

Can you do a quick fix and add the following two lines right at the
beginning of snd_usb_endpoint_activate() in sound/usb/endpoint.c:

        if (!ep)
                return 0;

? I'll fix this in a nicer way, but that should work.

Thanks,
Daniel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-21 16:34     ` Daniel Mack
@ 2011-12-21 17:11       ` Aurélien Leblond
  2011-12-21 17:18         ` Daniel Mack
  0 siblings, 1 reply; 20+ messages in thread
From: Aurélien Leblond @ 2011-12-21 17:11 UTC (permalink / raw)
  To: Daniel Mack; +Cc: tiwai, alsa-devel, clemens, gdiffey, linuxaudio

>> In dmesg, I get the following:
>> [   92.185624] BUG: unable to handle kernel NULL pointer dereference
>> at 0000000000000008
>> [   92.185637] IP: [<ffffffffa05c9768>]
>> snd_usb_endpoint_activate+0x18/0x90 [snd_usb_audio]
>
> Can you do a quick fix and add the following two lines right at the
> beginning of snd_usb_endpoint_activate() in sound/usb/endpoint.c:
>
>        if (!ep)
>                return 0;
>
> ? I'll fix this in a nicer way, but that should work.

Code changed and compiled.
Unfortunately my whole machine freezes as soon as I start Jack.

What info should I provide to you?

Aurélien

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-21 17:11       ` Aurélien Leblond
@ 2011-12-21 17:18         ` Daniel Mack
  2011-12-22 17:55           ` Aurélien Leblond
  0 siblings, 1 reply; 20+ messages in thread
From: Daniel Mack @ 2011-12-21 17:18 UTC (permalink / raw)
  To: Aurélien Leblond; +Cc: tiwai, alsa-devel, clemens, gdiffey, linuxaudio

On 12/21/2011 06:11 PM, Aurélien Leblond wrote:
>>> In dmesg, I get the following:
>>> [   92.185624] BUG: unable to handle kernel NULL pointer dereference
>>> at 0000000000000008
>>> [   92.185637] IP: [<ffffffffa05c9768>]
>>> snd_usb_endpoint_activate+0x18/0x90 [snd_usb_audio]
>>
>> Can you do a quick fix and add the following two lines right at the
>> beginning of snd_usb_endpoint_activate() in sound/usb/endpoint.c:
>>
>>        if (!ep)
>>                return 0;
>>
>> ? I'll fix this in a nicer way, but that should work.
> 
> Code changed and compiled.
> Unfortunately my whole machine freezes as soon as I start Jack.

*sigh*

Which hardware is connected? Did you try that with both? Did you try
with anything else than jack? I'd need more information about your setup
to get an idea.

Daniel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-21 17:18         ` Daniel Mack
@ 2011-12-22 17:55           ` Aurélien Leblond
  2011-12-22 19:04             ` Daniel Mack
  0 siblings, 1 reply; 20+ messages in thread
From: Aurélien Leblond @ 2011-12-22 17:55 UTC (permalink / raw)
  To: Daniel Mack; +Cc: tiwai, alsa-devel, clemens, gdiffey, linuxaudio

> Which hardware is connected? Did you try that with both? Did you try
> with anything else than jack? I'd need more information about your setup
> to get an idea.

Ok, here is a bit more info: I'm running a 64bit Ubuntu Oneiric.
Kernel is the 3.2 rc6.

With the M-Audio Fast Track Ultra:
- Alsa is working ok. That is, I can read a mp3 with mplayer without
any problem for example.
- As soon as I start Jackd2, my whole system freezes.
- 3.2 rc4 and the previous version of your patch, all was working ok
with the FTU.

With the Edirol UA25-Ex:
- It was working with 3.2 rc and the previous version of your patch.
- With the current version, the soundcard doesn't work at all. The
following trace appears in dmesg:

[   33.724071] BUG: unable to handle kernel NULL pointer dereference
at 0000000000000040
[   33.724078] IP: [<ffffffffa054edae>] start_endpoints+0x5e/0xc0
[snd_usb_audio]
[   33.724090] PGD 120018067 PUD 137aaf067 PMD 0
[   33.724095] Oops: 0002 [#1] PREEMPT SMP
[   33.724100] CPU 0
[   33.724102] Modules linked in: speedstep_lib parport_pc ppdev
binfmt_misc joydev snd_usb_audio arc4 snd_pcm snd_page_alloc snd_hwdep
snd_usbmidi_lib snd_seq_midi snd_rawmidi b43 mac80211 cfg80211
snd_seq_midi_event snd_seq snd_timer fglrx(P) ssb bcma psmouse
snd_seq_device hp_wmi snd i7core_edac soundcore sparse_keymap
serio_raw lp edac_core parport usbhid hid uvesafb ahci libahci wmi
video
[   33.724139]
[   33.724142] Pid: 1803, comm: pulseaudio Tainted: P           O
3.2.0-rc6-music+ #2 Hewlett-Packard HP ENVY 17 Notebook PC/144D
[   33.724148] RIP: 0010:[<ffffffffa054edae>]  [<ffffffffa054edae>]
start_endpoints+0x5e/0xc0 [snd_usb_audio]
[   33.724156] RSP: 0018:ffff88011f587cd8  EFLAGS: 00010246
[   33.724159] RAX: ffff88011f618000 RBX: ffff8801236d0018 RCX: 00000000002a9800
[   33.724162] RDX: 0000000000000000 RSI: 0000000000000286 RDI: 0000000000000000
[   33.724164] RBP: ffff88011f587ce8 R08: ffffea00048026c0 R09: ffffffff81489699
[   33.724167] R10: 0000000000000000 R11: ffff8800365972a0 R12: ffff88013426e800
[   33.724170] R13: 0000000000088802 R14: 0000000000088802 R15: 0000000000000000
[   33.724173] FS:  00007f6a1bbda720(0000) GS:ffff88013fc00000(0000)
knlGS:0000000000000000
[   33.724177] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[   33.724179] CR2: 0000000000000040 CR3: 0000000120011000 CR4: 00000000000006f0
[   33.724182] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[   33.724185] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[   33.724188] Process pulseaudio (pid: 1803, threadinfo
ffff88011f586000, task ffff88011f588000)
[   33.724191] Stack:
[   33.724192]  00007f6a1bbfa000 ffff88013426e800 ffff88011f587cf8
ffffffffa054f445
[   33.724198]  ffff88011f587d18 ffffffffa05313cb 00007f6a1bbfa008
ffffffffa053e180
[   33.724203]  ffff88011f587d58 ffffffffa0530ce6 0000000000000000
0000000000000000
[   33.724208] Call Trace:
[   33.724215]  [<ffffffffa054f445>] snd_usb_pcm_prepare+0xb5/0xd0
[snd_usb_audio]
[   33.724223]  [<ffffffffa05313cb>] snd_pcm_do_prepare+0x1b/0x30 [snd_pcm]
[   33.724229]  [<ffffffffa0530ce6>] snd_pcm_action_single+0x36/0x80 [snd_pcm]
[   33.724236]  [<ffffffffa0530e7e>]
snd_pcm_action_nonatomic+0x7e/0x90 [snd_pcm]
[   33.724242]  [<ffffffffa0533ada>] snd_pcm_common_ioctl1+0x7da/0xcb0 [snd_pcm]
[   33.724249]  [<ffffffffa053427c>]
snd_pcm_playback_ioctl1+0x4c/0x240 [snd_pcm]
[   33.724256]  [<ffffffff81616f00>] ? do_page_fault+0x210/0x5c0
[   33.724262]  [<ffffffffa05344a4>] snd_pcm_playback_ioctl+0x34/0x40 [snd_pcm]
[   33.724267]  [<ffffffff81186fef>] do_vfs_ioctl+0x8f/0x500
[   33.724272]  [<ffffffff81141081>] ? sys_mmap_pgoff+0xe1/0x230
[   33.724276]  [<ffffffff811874f1>] sys_ioctl+0x91/0xa0
[   33.724281]  [<ffffffff8161afc2>] system_call_fastpath+0x16/0x1b
[   33.724284] Code: 21 00 00 01 19 d2 31 c0 85 d2 74 11 48 83 c4 08
5b 5d c3 66 2e 0f 1f 84 00 00 00 00 00 48 8b bb 58 21 00 00 48 8b 83
50 21 00 00 <48> 89 47 40 e8 19 96 ff ff 85 c0 78 35 48 83 c4 08 31 c0
5b 5d
[   33.724335] RIP  [<ffffffffa054edae>] start_endpoints+0x5e/0xc0
[snd_usb_audio]
[   33.724342]  RSP <ffff88011f587cd8>
[   33.724344] CR2: 0000000000000040
[   33.724348] ---[ end trace 759045e1bb073007 ]---

If there is anything else relevant I can send you, let me know.

Regards,
Aurélien

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-22 17:55           ` Aurélien Leblond
@ 2011-12-22 19:04             ` Daniel Mack
  0 siblings, 0 replies; 20+ messages in thread
From: Daniel Mack @ 2011-12-22 19:04 UTC (permalink / raw)
  To: Aurélien Leblond; +Cc: tiwai, alsa-devel, clemens, gdiffey, linuxaudio

On 12/22/2011 06:55 PM, Aurélien Leblond wrote:
>> Which hardware is connected? Did you try that with both? Did you try
>> with anything else than jack? I'd need more information about your setup
>> to get an idea.
> 
> Ok, here is a bit more info: I'm running a 64bit Ubuntu Oneiric.
> Kernel is the 3.2 rc6.
> 
> With the M-Audio Fast Track Ultra:
> - Alsa is working ok. That is, I can read a mp3 with mplayer without
> any problem for example.
> - As soon as I start Jackd2, my whole system freezes.
> - 3.2 rc4 and the previous version of your patch, all was working ok
> with the FTU.
> 
> With the Edirol UA25-Ex:
> - It was working with 3.2 rc and the previous version of your patch.
> - With the current version, the soundcard doesn't work at all. The
> following trace appears in dmesg:
> 
> [   33.724071] BUG: unable to handle kernel NULL pointer dereference

Ok, thanks. I'll breed on this a little and let you know when I have
s.th. to share.

Thanks for the feedback!

Daniel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-20 15:32     ` Clemens Ladisch
@ 2011-12-20 20:16       ` Daniel Mack
  0 siblings, 0 replies; 20+ messages in thread
From: Daniel Mack @ 2011-12-20 20:16 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: Takashi Iwai, blablack, alsa-devel, gdiffey, linuxaudio

On 12/20/2011 04:32 PM, Clemens Ladisch wrote:
> Takashi Iwai wrote:
>> One point I'm not sure about this implementation is the use of
>> tasklet.  The tasklet itself runs again in ep->lock, what's the
>> rationale for it?  In other words, can't it be a straight function
>> call?
> 
> This architecture was copied from ua101.c.

Yes.

> If I remember correctly,
> the original reason for the tasklet was to avoid calling
> snd_pcm_period_elapsed() from inside the lock.  This would not be
> necessary in endpoint.c.

Hmm. It was actually you who told me to use a tasklet and not to submit
urbs from the input urb completion callbacks, as we can run out of
queuable playback buffers otherwise. Now that I think about it again, I
agree that it could be done without a tasklet as well by just keeping
the returned ones in a list and handle them in one go whenever possible.

I'll try this when I spin the next version.


Thanks,
Daniel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-20 15:42   ` Takashi Iwai
@ 2011-12-20 20:08     ` Daniel Mack
  0 siblings, 0 replies; 20+ messages in thread
From: Daniel Mack @ 2011-12-20 20:08 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: blablack, alsa-devel, clemens, gdiffey, linuxaudio

On 12/20/2011 04:42 PM, Takashi Iwai wrote:
> At Tue, 20 Dec 2011 10:48:37 +0100,
> Daniel Mack wrote:
>>
>> @@ -99,7 +104,7 @@ static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sl
>>   */
>>  static void release_urb_ctx(struct snd_urb_ctx *u)
>>  {
>> -	if (u->urb) {
>> +	if (!u || u->urb) {
>>  		if (u->buffer_size)
> 
> This will lead to Oops when u == NULL, no?

Yes, that's right. That said, it seems this hunk can be removed
entirely. I'll fix this in the next version.

>> +/* determine the number of frames in the next packet */
>> +static int next_packet_size(struct snd_usb_endpoint *ep)
>> +{
>> +	unsigned long flags;
>> +
>> +	if (ep->fill_max)
>> +		return ep->maxframesize;
>> +
>> +	spin_lock_irqsave(&ep->lock, flags);
>> +	ep->phase = (ep->phase & 0xffff)
>> +		+ (ep->freqm << ep->datainterval);
>> +	spin_unlock_irqrestore(&ep->lock, flags);
>> +
>> +	return min(ep->phase >> 16, ep->maxframesize);
> 
> If you need a protection here, safer to cover until min(), I guess.

Ok, will do.

I'll wait with a new version until there's feedback from people who
tested the code on their hardware :)

Thanks for the review.

Daniel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-20  9:48 ` [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model Daniel Mack
  2011-12-20 15:08   ` Takashi Iwai
@ 2011-12-20 15:42   ` Takashi Iwai
  2011-12-20 20:08     ` Daniel Mack
  1 sibling, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2011-12-20 15:42 UTC (permalink / raw)
  To: Daniel Mack; +Cc: blablack, alsa-devel, clemens, gdiffey, linuxaudio

At Tue, 20 Dec 2011 10:48:37 +0100,
Daniel Mack wrote:
> 
> @@ -99,7 +104,7 @@ static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sl
>   */
>  static void release_urb_ctx(struct snd_urb_ctx *u)
>  {
> -	if (u->urb) {
> +	if (!u || u->urb) {
>  		if (u->buffer_size)

This will lead to Oops when u == NULL, no?


> +/* determine the number of frames in the next packet */
> +static int next_packet_size(struct snd_usb_endpoint *ep)
> +{
> +	unsigned long flags;
> +
> +	if (ep->fill_max)
> +		return ep->maxframesize;
> +
> +	spin_lock_irqsave(&ep->lock, flags);
> +	ep->phase = (ep->phase & 0xffff)
> +		+ (ep->freqm << ep->datainterval);
> +	spin_unlock_irqrestore(&ep->lock, flags);
> +
> +	return min(ep->phase >> 16, ep->maxframesize);

If you need a protection here, safer to cover until min(), I guess.


Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-20 15:08   ` Takashi Iwai
@ 2011-12-20 15:32     ` Clemens Ladisch
  2011-12-20 20:16       ` Daniel Mack
  0 siblings, 1 reply; 20+ messages in thread
From: Clemens Ladisch @ 2011-12-20 15:32 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: linuxaudio, blablack, alsa-devel, gdiffey, Daniel Mack

Takashi Iwai wrote:
> One point I'm not sure about this implementation is the use of
> tasklet.  The tasklet itself runs again in ep->lock, what's the
> rationale for it?  In other words, can't it be a straight function
> call?

This architecture was copied from ua101.c.  If I remember correctly,
the original reason for the tasklet was to avoid calling
snd_pcm_period_elapsed() from inside the lock.  This would not be
necessary in endpoint.c.


Regards,
Clemens

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-20  9:48 ` [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model Daniel Mack
@ 2011-12-20 15:08   ` Takashi Iwai
  2011-12-20 15:32     ` Clemens Ladisch
  2011-12-20 15:42   ` Takashi Iwai
  1 sibling, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2011-12-20 15:08 UTC (permalink / raw)
  To: Daniel Mack; +Cc: blablack, alsa-devel, clemens, gdiffey, linuxaudio

At Tue, 20 Dec 2011 10:48:37 +0100,
Daniel Mack wrote:
> 
> In order to split changes properly, this patch only adds the new
> implementation but leaves the old one around, so the the driver doesn't
> change its behaviour. The switch to actually use the new code is
> submitted separately.
> 
> Signed-off-by: Daniel Mack <zonque@gmail.com>

One point I'm not sure about this implementation is the use of
tasklet.  The tasklet itself runs again in ep->lock, what's the
rationale for it?  In other words, can't it be a straight function
call?

I would understand if it were a workqueue and running in the
schedulable context.  But, I see no big merit of tasklet here.
(And tasklet is a candidate to be dropped sometime in future.)

Maybe I'm missing something behind it...


thanks,

Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-20  9:48 [PATCH 0/5] snd-usb endpoint rework, take 4 Daniel Mack
@ 2011-12-20  9:48 ` Daniel Mack
  2011-12-20 15:08   ` Takashi Iwai
  2011-12-20 15:42   ` Takashi Iwai
  0 siblings, 2 replies; 20+ messages in thread
From: Daniel Mack @ 2011-12-20  9:48 UTC (permalink / raw)
  To: alsa-devel; +Cc: gdiffey, tiwai, clemens, linuxaudio, Daniel Mack, blablack

In order to split changes properly, this patch only adds the new
implementation but leaves the old one around, so the the driver doesn't
change its behaviour. The switch to actually use the new code is
submitted separately.

Signed-off-by: Daniel Mack <zonque@gmail.com>
---
 sound/usb/card.h     |   59 ++++
 sound/usb/endpoint.c |  935 +++++++++++++++++++++++++++++++++++++++++++++++++-
 sound/usb/endpoint.h |   26 ++
 sound/usb/usbaudio.h |    1 +
 4 files changed, 1009 insertions(+), 12 deletions(-)

diff --git a/sound/usb/card.h b/sound/usb/card.h
index a39edcc..4a993c6 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -29,13 +29,17 @@ struct audioformat {
 };
 
 struct snd_usb_substream;
+struct snd_usb_endpoint;
 
 struct snd_urb_ctx {
 	struct urb *urb;
 	unsigned int buffer_size;	/* size of data buffer, if data URB */
 	struct snd_usb_substream *subs;
+	struct snd_usb_endpoint *ep;
 	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 list_head ready_list;
 };
 
 struct snd_urb_ops {
@@ -45,6 +49,61 @@ struct snd_urb_ops {
 	int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
 };
 
+struct snd_usb_endpoint {
+	struct snd_usb_audio *chip;
+
+	int use_count;
+	int ep_num;		/* the referenced endpoint number */
+	int type;		/* SND_USB_ENDPOINT_TYPE_* */
+	unsigned long flags;
+
+	void (*prepare_data_urb) (struct snd_usb_substream *subs,
+				  struct urb *urb);
+	void (*retire_data_urb) (struct snd_usb_substream *subs,
+				 struct urb *urb);
+
+	struct snd_usb_substream *data_subs;
+	struct snd_usb_endpoint *sync_master;
+	struct snd_usb_endpoint *sync_slave;
+
+	struct snd_urb_ctx urb[MAX_URBS];
+
+	struct snd_usb_packet_info {
+		uint32_t packet_size[MAX_PACKS_HS];
+		int packets;
+	} next_packet[MAX_URBS];
+	int next_packet_read_pos, next_packet_write_pos;
+	struct list_head ready_playback_urbs;
+	struct tasklet_struct outbound_tasklet;
+
+	unsigned int nurbs;		/* # urbs */
+	unsigned long active_mask;	/* bitmask of active urbs */
+	unsigned long unlink_mask;	/* bitmask of unlinked urbs */
+	char *syncbuf;			/* sync buffer for all sync URBs */
+	dma_addr_t sync_dma;		/* DMA address of syncbuf */
+
+	unsigned int pipe;		/* the data i/o pipe */
+	unsigned int freqn;		/* nominal sampling rate in fs/fps in Q16.16 format */
+	unsigned int freqm;		/* momentary sampling rate in fs/fps in Q16.16 format */
+	int	   freqshift;		/* how much to shift the feedback value to get Q16.16 */
+	unsigned int freqmax;		/* maximum sampling rate, used for buffer management */
+	unsigned int phase;		/* phase accumulator */
+	unsigned int maxpacksize;	/* max packet size in bytes */
+	unsigned int maxframesize;      /* max packet size in frames */
+	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
+	unsigned int curframesize;      /* current packet size in frames (for capture) */
+	unsigned int syncmaxsize;	/* sync endpoint packet size */
+	unsigned int fill_max:1;	/* fill max packet size always */
+	unsigned int datainterval;      /* log_2 of data packet interval */
+	unsigned int syncinterval;	/* P for adaptive mode, 0 otherwise */
+	unsigned char silence_value;
+	unsigned int stride;
+	int iface, alt_idx;
+
+	spinlock_t lock;
+	struct list_head list;
+};
+
 struct snd_usb_substream {
 	struct snd_usb_stream *stream;
 	struct usb_device *dev;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 81c6ede..2ed6a7c 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -19,9 +19,11 @@
 #include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/slab.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 
 #include "usbaudio.h"
 #include "helper.h"
@@ -29,6 +31,9 @@
 #include "endpoint.h"
 #include "pcm.h"
 
+#define EP_FLAG_ACTIVATED	0
+#define EP_FLAG_RUNNING		1
+
 /*
  * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
  * this will overflow at approx 524 kHz
@@ -50,7 +55,7 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
 /*
  * unlink active urbs.
  */
-static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
+static int deactivate_urbs_old(struct snd_usb_substream *subs, int force, int can_sleep)
 {
 	struct snd_usb_audio *chip = subs->stream->chip;
 	unsigned int i;
@@ -99,7 +104,7 @@ static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sl
  */
 static void release_urb_ctx(struct snd_urb_ctx *u)
 {
-	if (u->urb) {
+	if (!u || u->urb) {
 		if (u->buffer_size)
 			usb_free_coherent(u->subs->dev, u->buffer_size,
 					u->urb->transfer_buffer,
@@ -112,7 +117,7 @@ static void release_urb_ctx(struct snd_urb_ctx *u)
 /*
  *  wait until all urbs are processed.
  */
-static int wait_clear_urbs(struct snd_usb_substream *subs)
+static int wait_clear_urbs_old(struct snd_usb_substream *subs)
 {
 	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
 	unsigned int i;
@@ -147,8 +152,8 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
 	int i;
 
 	/* stop urbs (to be sure) */
-	deactivate_urbs(subs, force, 1);
-	wait_clear_urbs(subs);
+	deactivate_urbs_old(subs, force, 1);
+	wait_clear_urbs_old(subs);
 
 	for (i = 0; i < MAX_URBS; i++)
 		release_urb_ctx(&subs->dataurb[i]);
@@ -163,7 +168,7 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
 /*
  * complete callback from data urb
  */
-static void snd_complete_urb(struct urb *urb)
+static void snd_complete_urb_old(struct urb *urb)
 {
 	struct snd_urb_ctx *ctx = urb->context;
 	struct snd_usb_substream *subs = ctx->subs;
@@ -317,7 +322,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
 		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
 		u->urb->interval = 1 << subs->datainterval;
 		u->urb->context = u;
-		u->urb->complete = snd_complete_urb;
+		u->urb->complete = snd_complete_urb_old;
 	}
 
 	if (subs->syncpipe) {
@@ -855,7 +860,7 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru
 
  __error:
 	// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
-	deactivate_urbs(subs, 0, 0);
+	deactivate_urbs_old(subs, 0, 0);
 	return -EPIPE;
 }
 
@@ -916,7 +921,7 @@ 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:
-		return deactivate_urbs(subs, 0, 0);
+		return deactivate_urbs_old(subs, 0, 0);
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		subs->ops.prepare = prepare_nodata_playback_urb;
 		return 0;
@@ -934,7 +939,7 @@ int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int c
 		subs->ops.retire = retire_capture_urb;
 		return start_urbs(subs, substream->runtime);
 	case SNDRV_PCM_TRIGGER_STOP:
-		return deactivate_urbs(subs, 0, 0);
+		return deactivate_urbs_old(subs, 0, 0);
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		subs->ops.retire = retire_paused_capture_urb;
 		return 0;
@@ -950,8 +955,8 @@ 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);
+	deactivate_urbs_old(subs, 0, 1);
+	wait_clear_urbs_old(subs);
 
 	/* for playback, submit the URBs now; otherwise, the first hwptr_done
 	 * updates for all URBs would happen at the same time when starting */
@@ -963,3 +968,909 @@ int snd_usb_substream_prepare(struct snd_usb_substream *subs,
 	return 0;
 }
 
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
+{
+	return  ep->sync_master &&
+		ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA &&
+		ep->type == SND_USB_ENDPOINT_TYPE_DATA &&
+		usb_pipeout(ep->pipe);
+}
+
+/* determine the number of frames in the next packet */
+static int next_packet_size(struct snd_usb_endpoint *ep)
+{
+	unsigned long flags;
+
+	if (ep->fill_max)
+		return ep->maxframesize;
+
+	spin_lock_irqsave(&ep->lock, flags);
+	ep->phase = (ep->phase & 0xffff)
+		+ (ep->freqm << ep->datainterval);
+	spin_unlock_irqrestore(&ep->lock, flags);
+
+	return min(ep->phase >> 16, ep->maxframesize);
+}
+
+static void retire_outbound_urb(struct snd_usb_endpoint *ep,
+				struct snd_urb_ctx *urb_ctx)
+{
+	if (ep->retire_data_urb)
+		ep->retire_data_urb(ep->data_subs, urb_ctx->urb);
+}
+
+static void retire_inbound_urb(struct snd_usb_endpoint *ep,
+			       struct snd_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+
+	if (ep->sync_slave)
+		snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
+
+	if (ep->retire_data_urb)
+		ep->retire_data_urb(ep->data_subs, urb);
+}
+
+static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep,
+				       struct snd_urb_ctx *ctx)
+{
+	int i;
+
+	for (i = 0; i < ctx->packets; ++i)
+		ctx->packet_size[i] = next_packet_size(ep);
+}
+
+/*
+ * Prepare a PLAYBACK urb for submission to the bus.
+ */
+static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
+				 struct snd_urb_ctx *ctx)
+{
+	int i;
+	struct urb *urb = ctx->urb;
+	unsigned char *cp = urb->transfer_buffer;
+
+	urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+	switch (ep->type) {
+	case SND_USB_ENDPOINT_TYPE_DATA:
+		if (ep->prepare_data_urb) {
+			ep->prepare_data_urb(ep->data_subs, urb);
+		} else {
+			/* no data provider, so send silence */
+			unsigned int offs = 0;
+			for (i = 0; i < ctx->packets; ++i) {
+				int counts = ctx->packet_size[i];
+				urb->iso_frame_desc[i].offset = offs * ep->stride;
+				urb->iso_frame_desc[i].length = counts * ep->stride;
+				offs += counts;
+			}
+
+			urb->number_of_packets = ctx->packets;
+			urb->transfer_buffer_length = offs * ep->stride;
+			memset(urb->transfer_buffer, ep->silence_value,
+			       offs * ep->stride);
+		}
+		break;
+
+	case SND_USB_ENDPOINT_TYPE_SYNC:
+		if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
+			/*
+			 * fill the length and offset of each urb descriptor.
+			 * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+			 */
+			urb->iso_frame_desc[0].length = 4;
+			urb->iso_frame_desc[0].offset = 0;
+			cp[0] = ep->freqn;
+			cp[1] = ep->freqn >> 8;
+			cp[2] = ep->freqn >> 16;
+			cp[3] = ep->freqn >> 24;
+		} else {
+			/*
+			 * fill the length and offset of each urb descriptor.
+			 * the fixed 10.14 frequency is passed through the pipe.
+			 */
+			urb->iso_frame_desc[0].length = 3;
+			urb->iso_frame_desc[0].offset = 0;
+			cp[0] = ep->freqn >> 2;
+			cp[1] = ep->freqn >> 10;
+			cp[2] = ep->freqn >> 18;
+		}
+
+		break;
+	}
+}
+
+/*
+ * Prepare a CAPTURE or SYNC urb for submission to the bus.
+ */
+static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
+				       struct snd_urb_ctx *urb_ctx)
+{
+	int i, offs;
+	struct urb *urb = urb_ctx->urb;
+
+	urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+	switch (ep->type) {
+	case SND_USB_ENDPOINT_TYPE_DATA:
+		offs = 0;
+		for (i = 0; i < urb_ctx->packets; i++) {
+			urb->iso_frame_desc[i].offset = offs;
+			urb->iso_frame_desc[i].length = ep->curpacksize;
+			offs += ep->curpacksize;
+		}
+
+		urb->transfer_buffer_length = offs;
+		urb->number_of_packets = urb_ctx->packets;
+		break;
+
+	case SND_USB_ENDPOINT_TYPE_SYNC:
+		urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize);
+		urb->iso_frame_desc[0].offset = 0;
+		break;
+	}
+}
+
+/*
+ * complete callback for urbs
+ */
+static void snd_complete_urb(struct urb *urb)
+{
+	struct snd_urb_ctx *ctx = urb->context;
+	struct snd_usb_endpoint *ep = ctx->ep;
+	unsigned long flags;
+	int err;
+
+	if (unlikely(urb->status == -ENOENT ||		/* unlinked */
+		     urb->status == -ENODEV ||		/* device removed */
+		     urb->status == -ECONNRESET ||	/* unlinked */
+		     urb->status == -ESHUTDOWN))	/* device disabled */
+		goto exit_clear;
+
+	if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+		goto exit_clear;
+
+	if (usb_pipeout(ep->pipe)) {
+		retire_outbound_urb(ep, ctx);
+		/* can be stopped during retire callback */
+		if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+			goto exit_clear;
+
+		if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+			clear_bit(ctx->index, &ep->active_mask);
+			spin_lock_irqsave(&ep->lock, flags);
+			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+			tasklet_schedule(&ep->outbound_tasklet);
+			spin_unlock_irqrestore(&ep->lock, flags);
+			return;
+		}
+
+		prepare_outbound_urb_sizes(ep, ctx);
+		prepare_outbound_urb(ep, ctx);
+	} else {
+		retire_inbound_urb(ep, ctx);
+		/* can be stopped during retire callback */
+		if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+			goto exit_clear;
+
+		prepare_inbound_urb(ep, ctx);
+	}
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err == 0)
+		return;
+
+	snd_printk(KERN_ERR "cannot submit urb (err = %d)\n", err);
+	//snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+exit_clear:
+	clear_bit(ctx->index, &ep->active_mask);
+}
+
+static void snd_usb_tasklet_handler(unsigned long data)
+{
+	struct snd_usb_endpoint *ep = (void *) data;
+	unsigned long flags;
+
+	if (!test_bit(EP_FLAG_RUNNING, &ep->flags))
+		return;
+
+	spin_lock_irqsave(&ep->lock, flags);
+
+	while (test_bit(EP_FLAG_RUNNING, &ep->flags) &&
+	       !list_empty(&ep->ready_playback_urbs) &&
+	       ep->next_packet_read_pos != ep->next_packet_write_pos) {
+
+		struct snd_usb_packet_info *packet;
+		struct snd_urb_ctx *ctx;
+		struct urb *urb;
+		int err, i;
+
+		packet = ep->next_packet + ep->next_packet_read_pos;
+		ep->next_packet_read_pos++;
+		ep->next_packet_read_pos %= MAX_URBS;
+
+		/* take URB out of FIFO */
+		ctx = list_first_entry(&ep->ready_playback_urbs,
+				       struct snd_urb_ctx, ready_list);
+		list_del(&ctx->ready_list);
+		urb = ctx->urb;
+
+		/* copy over the length information */
+		for (i = 0; i < packet->packets; i++)
+			ctx->packet_size[i] = packet->packet_size[i];
+
+		prepare_outbound_urb(ep, ctx);
+
+		err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
+		if (err < 0)
+			snd_printk(KERN_ERR "Unable to submit urb #%d: %d (urb %p)\n",
+				   ctx->index, err, ctx->urb);
+		else
+			set_bit(ctx->index, &ep->active_mask);
+	}
+
+	spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+					      struct usb_host_interface *alts,
+					      int ep_num, int direction, int type)
+{
+	struct list_head *p;
+	struct snd_usb_endpoint *ep;
+	int ret, is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
+
+	mutex_lock(&chip->mutex);
+
+	list_for_each(p, &chip->ep_list) {
+		ep = list_entry(p, struct snd_usb_endpoint, list);
+		if (ep->ep_num == ep_num &&
+		    ep->iface == alts->desc.bInterfaceNumber &&
+		    ep->alt_idx == alts->desc.bAlternateSetting) {
+			snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n",
+					ep_num, ep->iface, ep->alt_idx, ep);
+			goto __exit_unlock;
+		}
+	}
+
+	snd_printdd(KERN_DEBUG "Creating new %s %s endpoint #%x\n",
+		   is_playback ? "playback" : "capture",
+		   type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync",
+		   ep_num);
+
+	/* select the alt setting once so the endpoints become valid */
+	ret = usb_set_interface(chip->dev, alts->desc.bInterfaceNumber,
+				alts->desc.bAlternateSetting);
+	if (ret < 0) {
+		snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+					__func__, ret);
+		ep = NULL;
+		goto __exit_unlock;
+	}
+
+	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+	if (!ep)
+		goto __exit_unlock;
+
+	ep->chip = chip;
+	spin_lock_init(&ep->lock);
+	ep->type = type;
+	ep->ep_num = ep_num;
+	ep->iface = alts->desc.bInterfaceNumber;
+	ep->alt_idx = alts->desc.bAlternateSetting;
+	INIT_LIST_HEAD(&ep->ready_playback_urbs);
+	tasklet_init(&ep->outbound_tasklet, snd_usb_tasklet_handler,
+			(unsigned long) ep);
+
+	ep_num &= USB_ENDPOINT_NUMBER_MASK;
+
+	if (is_playback)
+		ep->pipe = usb_sndisocpipe(chip->dev, ep_num);
+	else
+		ep->pipe = usb_rcvisocpipe(chip->dev, ep_num);
+
+	if (type == SND_USB_ENDPOINT_TYPE_SYNC) {
+		if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+		    get_endpoint(alts, 1)->bRefresh >= 1 &&
+		    get_endpoint(alts, 1)->bRefresh <= 9)
+			ep->syncinterval = get_endpoint(alts, 1)->bRefresh;
+		else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL)
+			ep->syncinterval = 1;
+		else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+			 get_endpoint(alts, 1)->bInterval <= 16)
+			ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+		else
+			ep->syncinterval = 3;
+
+		ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
+	}
+
+	list_add_tail(&ep->list, &chip->ep_list);
+
+__exit_unlock:
+	mutex_unlock(&chip->mutex);
+
+	return ep;
+}
+
+/*
+ *  wait until all urbs are processed.
+ */
+static int wait_clear_urbs(struct snd_usb_endpoint *ep)
+{
+	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
+	unsigned int i;
+	int alive;
+
+	do {
+		alive = 0;
+		for (i = 0; i < ep->nurbs; i++)
+			if (test_bit(i, &ep->active_mask))
+				alive++;
+
+		if (!alive)
+			break;
+
+		schedule_timeout_uninterruptible(1);
+	} while (time_before(jiffies, end_time));
+
+	if (alive)
+		snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n",
+					alive, ep->ep_num);
+
+	return 0;
+}
+
+/*
+ * unlink active urbs.
+ */
+static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep)
+{
+	unsigned long flags;
+	unsigned int i;
+	int async;
+
+	if (!force && ep->chip->shutdown) /* to be sure... */
+		return -EBADFD;
+
+	async = !can_sleep && ep->chip->async_unlink;
+
+	clear_bit(EP_FLAG_RUNNING, &ep->flags);
+	tasklet_kill(&ep->outbound_tasklet);
+
+	spin_lock_irqsave(&ep->lock, flags);
+	INIT_LIST_HEAD(&ep->ready_playback_urbs);
+	ep->next_packet_read_pos = 0;
+	ep->next_packet_write_pos = 0;
+	spin_unlock_irqrestore(&ep->lock, flags);
+
+	if (!async && in_interrupt())
+		return 0;
+
+	for (i = 0; i < ep->nurbs; i++) {
+		if (test_bit(i, &ep->active_mask)) {
+			if (!test_and_set_bit(i, &ep->unlink_mask)) {
+				struct urb *u = ep->urb[i].urb;
+				if (async)
+					usb_unlink_urb(u);
+				else
+					usb_kill_urb(u);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * release an endpoint's urbs
+ */
+static void release_urbs(struct snd_usb_endpoint *ep, int force)
+{
+	int i;
+
+	/* route incoming urbs to nirvana */
+	ep->retire_data_urb = NULL;
+	ep->prepare_data_urb = NULL;
+
+	/* stop urbs */
+	deactivate_urbs(ep, force, 1);
+	wait_clear_urbs(ep);
+
+	for (i = 0; i < ep->nurbs; i++)
+		release_urb_ctx(&ep->urb[i]);
+
+	if (ep->syncbuf)
+		usb_free_coherent(ep->chip->dev, SYNC_URBS * 4,
+				  ep->syncbuf, ep->sync_dma);
+
+	ep->syncbuf = NULL;
+	ep->nurbs = 0;
+}
+
+static int data_ep_set_params(struct snd_usb_endpoint *ep,
+			      struct snd_pcm_hw_params *hw_params,
+			      struct audioformat *fmt,
+			      struct snd_usb_endpoint *sync_ep)
+{
+	unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
+	int period_bytes = params_period_bytes(hw_params);
+	int format = params_format(hw_params);
+	int is_playback = usb_pipeout(ep->pipe);
+	int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) *
+							params_channels(hw_params);
+
+	ep->datainterval = fmt->datainterval;
+	ep->stride = frame_bits >> 3;
+	ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
+
+	/* calculate max. frequency */
+	if (ep->maxpacksize) {
+		/* whatever fits into a max. size packet */
+		maxsize = ep->maxpacksize;
+		ep->freqmax = (maxsize / (frame_bits >> 3))
+				<< (16 - ep->datainterval);
+	} else {
+		/* no max. packet size: just take 25% higher than nominal */
+		ep->freqmax = ep->freqn + (ep->freqn >> 2);
+		maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
+				>> (16 - ep->datainterval);
+	}
+
+	if (ep->fill_max)
+		ep->curpacksize = ep->maxpacksize;
+	else
+		ep->curpacksize = maxsize;
+
+	if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL)
+		packs_per_ms = 8 >> ep->datainterval;
+	else
+		packs_per_ms = 1;
+
+	if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+		urb_packs = max(ep->chip->nrpacks, 1);
+		urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
+	} else {
+		urb_packs = 1;
+	}
+
+	urb_packs *= packs_per_ms;
+
+	if (sync_ep && !snd_usb_endpoint_implict_feedback_sink(ep))
+		urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
+
+	/* decide how many packets to be used */
+	if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+		unsigned int minsize, maxpacks;
+		/* determine how small a packet can be */
+		minsize = (ep->freqn >> (16 - ep->datainterval))
+			  * (frame_bits >> 3);
+		/* with sync from device, assume it can be 12% lower */
+		if (sync_ep)
+			minsize -= minsize >> 3;
+		minsize = max(minsize, 1u);
+		total_packs = (period_bytes + minsize - 1) / minsize;
+		/* we need at least two URBs for queueing */
+		if (total_packs < 2) {
+			total_packs = 2;
+		} else {
+			/* and we don't want too long a queue either */
+			maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
+			total_packs = min(total_packs, maxpacks);
+		}
+	} else {
+		while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+			urb_packs >>= 1;
+		total_packs = MAX_URBS * urb_packs;
+	}
+
+	ep->nurbs = (total_packs + urb_packs - 1) / urb_packs;
+	if (ep->nurbs > MAX_URBS) {
+		/* too much... */
+		ep->nurbs = MAX_URBS;
+		total_packs = MAX_URBS * urb_packs;
+	} else if (ep->nurbs < 2) {
+		/* too little - we need at least two packets
+		 * to ensure contiguous playback/capture
+		 */
+		ep->nurbs = 2;
+	}
+
+	/* allocate and initialize data urbs */
+	for (i = 0; i < ep->nurbs; i++) {
+		struct snd_urb_ctx *u = &ep->urb[i];
+		u->index = i;
+		u->ep = ep;
+		u->packets = (i + 1) * total_packs / ep->nurbs
+			- i * total_packs / ep->nurbs;
+		u->buffer_size = maxsize * u->packets;
+
+		if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
+			u->packets++; /* for transfer delimiter */
+		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+		if (!u->urb)
+			goto out_of_memory;
+
+		u->urb->transfer_buffer =
+			usb_alloc_coherent(ep->chip->dev, u->buffer_size,
+					   GFP_KERNEL, &u->urb->transfer_dma);
+		if (!u->urb->transfer_buffer)
+			goto out_of_memory;
+		u->urb->pipe = ep->pipe;
+		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+		u->urb->interval = 1 << ep->datainterval;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+	}
+
+	return 0;
+
+out_of_memory:
+	release_urbs(ep, 0);
+	return -ENOMEM;
+}
+
+static int sync_ep_set_params(struct snd_usb_endpoint *ep,
+			      struct snd_pcm_hw_params *hw_params,
+			      struct audioformat *fmt)
+{
+	int i;
+
+	ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
+					 GFP_KERNEL, &ep->sync_dma);
+	if (!ep->syncbuf)
+		return -ENOMEM;
+
+	for (i = 0; i < SYNC_URBS; i++) {
+		struct snd_urb_ctx *u = &ep->urb[i];
+		u->index = i;
+		u->ep = ep;
+		u->packets = 1;
+		u->urb = usb_alloc_urb(1, GFP_KERNEL);
+		if (!u->urb)
+			goto out_of_memory;
+		u->urb->transfer_buffer = ep->syncbuf + i * 4;
+		u->urb->transfer_dma = ep->sync_dma + i * 4;
+		u->urb->transfer_buffer_length = 4;
+		u->urb->pipe = ep->pipe;
+		u->urb->transfer_flags = URB_ISO_ASAP |
+					 URB_NO_TRANSFER_DMA_MAP;
+		u->urb->number_of_packets = 1;
+		u->urb->interval = 1 << ep->syncinterval;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+	}
+
+	ep->nurbs = SYNC_URBS;
+
+	return 0;
+
+out_of_memory:
+	release_urbs(ep, 0);
+	return -ENOMEM;
+}
+
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+				struct snd_pcm_hw_params *hw_params,
+				struct audioformat *fmt,
+				struct snd_usb_endpoint *sync_ep)
+{
+	int err;
+
+	if (ep->use_count != 0) {
+		snd_printk(KERN_WARNING "Unable to change format on ep #%x: already in use\n",
+			   ep->ep_num);
+		return -EBUSY;
+	}
+
+	/* release old buffers, if any */
+	release_urbs(ep, 0);
+
+	ep->datainterval = fmt->datainterval;
+	ep->maxpacksize = fmt->maxpacksize;
+	ep->fill_max = fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX;
+
+	if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
+		ep->freqn = get_usb_full_speed_rate(params_rate(hw_params));
+	else
+		ep->freqn = get_usb_high_speed_rate(params_rate(hw_params));
+
+	/* calculate the frequency in 16.16 format */
+	ep->freqm = ep->freqn;
+	ep->freqshift = INT_MIN;
+
+	ep->phase = 0;
+
+	switch (ep->type) {
+	case  SND_USB_ENDPOINT_TYPE_DATA:
+		err = data_ep_set_params(ep, hw_params, fmt, sync_ep);
+		break;
+	case  SND_USB_ENDPOINT_TYPE_SYNC:
+		err = sync_ep_set_params(ep, hw_params, fmt);
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	snd_printdd(KERN_DEBUG "Setting params for ep #%x (type %d, %d urbs), ret=%d\n",
+		   ep->ep_num, ep->type, ep->nurbs, err);
+
+	return err;
+}
+
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
+{
+	int err;
+	unsigned int i;
+
+	if (ep->chip->shutdown)
+		return -EBADFD;
+
+	/* already running? */
+	if (++ep->use_count != 1)
+		return 0;
+
+	if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
+		return -EINVAL;
+
+	/* just to be sure */
+	deactivate_urbs(ep, 0, 1);
+	wait_clear_urbs(ep);
+
+	ep->active_mask = 0;
+	ep->unlink_mask = 0;
+	ep->phase = 0;
+
+	/*
+	 * If this endpoint has a data endpoint as implicit feedback source,
+	 * don't start the urbs here. Instead, mark them all as available,
+	 * wait for the record urbs to arrive and queue from that context.
+	 */
+
+	set_bit(EP_FLAG_RUNNING, &ep->flags);
+
+	if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+		for (i = 0; i < ep->nurbs; i++) {
+			struct snd_urb_ctx *ctx = ep->urb + i;
+			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+		}
+
+		return 0;
+	}
+
+	for (i = 0; i < ep->nurbs; i++) {
+		struct urb *urb = ep->urb[i].urb;
+
+		if (snd_BUG_ON(!urb))
+			goto __error;
+
+		if (usb_pipeout(ep->pipe)) {
+			prepare_outbound_urb_sizes(ep, urb->context);
+			prepare_outbound_urb(ep, urb->context);
+		} else {
+			prepare_inbound_urb(ep, urb->context);
+		}
+
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0) {
+			snd_printk(KERN_ERR "cannot submit urb %d, error %d: %s\n",
+				   i, err, usb_error_string(err));
+			goto __error;
+		}
+		set_bit(i, &ep->active_mask);
+	}
+
+	return 0;
+
+__error:
+	clear_bit(EP_FLAG_RUNNING, &ep->flags);
+	ep->use_count--;
+	deactivate_urbs(ep, 0, 0);
+	return -EPIPE;
+}
+
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
+			   int force, int can_sleep, int wait)
+{
+	if (!ep)
+		return;
+
+	if (snd_BUG_ON(ep->use_count == 0))
+		return;
+
+	if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
+		return;
+
+	if (--ep->use_count == 0) {
+		deactivate_urbs(ep, force, can_sleep);
+		ep->data_subs = NULL;
+		ep->sync_slave = NULL;
+		ep->retire_data_urb = NULL;
+		ep->prepare_data_urb = NULL;
+
+		if (wait)
+			wait_clear_urbs(ep);
+	}
+}
+
+int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep)
+{
+	if (ep->use_count != 0)
+		return 0;
+
+	if (!ep->chip->shutdown &&
+	    !test_and_set_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
+		int ret;
+
+		ret = usb_set_interface(ep->chip->dev, ep->iface, ep->alt_idx);
+		if (ret < 0) {
+			snd_printk(KERN_ERR "%s() usb_set_interface() failed, ret = %d\n",
+						__func__, ret);
+			clear_bit(EP_FLAG_ACTIVATED, &ep->flags);
+			return ret;
+		}
+
+		return 0;
+	}
+
+	return -EBUSY;
+}
+
+int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
+{
+	if (!ep)
+		return -EINVAL;
+
+	if (ep->use_count != 0)
+		return 0;
+
+	if (!ep->chip->shutdown &&
+	    test_and_clear_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
+		int ret;
+
+		ret = usb_set_interface(ep->chip->dev, ep->iface, 0);
+		if (ret < 0) {
+			snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+						__func__, ret);
+			return ret;
+		}
+
+		return 0;
+	}
+
+	return -EBUSY;
+}
+
+void snd_usb_endpoint_free(struct list_head *head)
+{
+	struct snd_usb_endpoint *ep;
+
+	ep = list_entry(head, struct snd_usb_endpoint, list);
+	release_urbs(ep, 1);
+	kfree(ep);
+}
+
+/*
+ * process after playback sync complete
+ *
+ * Full speed devices report feedback values in 10.14 format as samples per
+ * frame, high speed devices in 16.16 format as samples per microframe.
+ * Because the Audio Class 1 spec was written before USB 2.0, many high speed
+ * devices use a wrong interpretation, some others use an entirely different
+ * format.  Therefore, we cannot predict what format any particular device uses
+ * and must detect it automatically.
+ */
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+			     struct snd_usb_endpoint *sender,
+			     const struct urb *urb)
+{
+	int shift;
+	unsigned int f;
+	unsigned long flags;
+
+	snd_BUG_ON(ep == sender);
+
+	if (snd_usb_endpoint_implict_feedback_sink(ep) &&
+	    ep->use_count != 0) {
+
+		/* implicit feedback case */
+		int i, bytes = 0;
+		struct snd_urb_ctx *in_ctx;
+		struct snd_usb_packet_info *out_packet;
+
+		in_ctx = urb->context;
+
+		/* Count overall packet size */
+		for (i = 0; i < in_ctx->packets; i++)
+			if (urb->iso_frame_desc[i].status == 0)
+				bytes += 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;
+
+		spin_lock_irqsave(&ep->lock, flags);
+		out_packet = ep->next_packet + ep->next_packet_write_pos;
+
+		/*
+		 * Iterate through the inbound packet and prepare the lengths
+		 * for the output packet. The OUT packet we are about to send
+		 * will have the same amount of payload than the IN packet we
+		 * just received.
+		 */
+
+		out_packet->packets = in_ctx->packets;
+		for (i = 0; i < in_ctx->packets; i++) {
+			if (urb->iso_frame_desc[i].status == 0)
+				out_packet->packet_size[i] =
+					urb->iso_frame_desc[i].actual_length / ep->stride;
+			else
+				out_packet->packet_size[i] = 0;
+		}
+
+		ep->next_packet_write_pos++;
+		ep->next_packet_write_pos %= MAX_URBS;
+		tasklet_schedule(&ep->outbound_tasklet);
+		spin_unlock_irqrestore(&ep->lock, flags);
+
+		return;
+	}
+
+	if (urb->iso_frame_desc[0].status != 0 ||
+	    urb->iso_frame_desc[0].actual_length < 3)
+		return;
+
+	f = le32_to_cpup(urb->transfer_buffer);
+	if (urb->iso_frame_desc[0].actual_length == 3)
+		f &= 0x00ffffff;
+	else
+		f &= 0x0fffffff;
+
+	if (f == 0)
+		return;
+
+	if (unlikely(ep->freqshift == INT_MIN)) {
+		/*
+		 * The first time we see a feedback value, determine its format
+		 * by shifting it left or right until it matches the nominal
+		 * frequency value.  This assumes that the feedback does not
+		 * differ from the nominal value more than +50% or -25%.
+		 */
+		shift = 0;
+		while (f < ep->freqn - ep->freqn / 4) {
+			f <<= 1;
+			shift++;
+		}
+		while (f > ep->freqn + ep->freqn / 2) {
+			f >>= 1;
+			shift--;
+		}
+		ep->freqshift = shift;
+	}
+	else if (ep->freqshift >= 0)
+		f <<= ep->freqshift;
+	else
+		f >>= -ep->freqshift;
+
+	if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) {
+		/*
+		 * If the frequency looks valid, set it.
+		 * This value is referred to in prepare_playback_urb().
+		 */
+		spin_lock_irqsave(&ep->lock, flags);
+		ep->freqm = f;
+		spin_unlock_irqrestore(&ep->lock, flags);
+	} else {
+		/*
+		 * Out of range; maybe the shift value is wrong.
+		 * Reset it so that we autodetect again the next time.
+		 */
+		ep->freqshift = INT_MIN;
+	}
+}
+
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 88eb63a..9f083d7 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -18,4 +18,30 @@ 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);
 
+
+#define SND_USB_ENDPOINT_TYPE_DATA     0
+#define SND_USB_ENDPOINT_TYPE_SYNC     1
+
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+					      struct usb_host_interface *alts,
+					      int ep_num, int direction, int type);
+
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+				struct snd_pcm_hw_params *hw_params,
+				struct audioformat *fmt,
+				struct snd_usb_endpoint *sync_ep);
+
+int  snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
+			   int force, int can_sleep, int wait);
+int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
+int  snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_free(struct list_head *head);
+
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep);
+
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+			     struct snd_usb_endpoint *sender,
+			     const struct urb *urb);
+
 #endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index a16c21d..b8233eb 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -47,6 +47,7 @@ struct snd_usb_audio {
 	int num_suspended_intf;
 
 	struct list_head pcm_list;	/* list of pcm streams */
+	struct list_head ep_list;	/* list of audio-related endpoints */
 	int pcm_devs;
 
 	struct list_head midi_list;	/* list of midi interfaces */
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-12-18 23:02 [PATCH 0/5] snd-usb endpoint logic rework, version 3 Daniel Mack
@ 2011-12-18 23:02 ` Daniel Mack
  0 siblings, 0 replies; 20+ messages in thread
From: Daniel Mack @ 2011-12-18 23:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: gdiffey, tiwai, clemens, linuxaudio, Daniel Mack, blablack

In order to split changes properly, this patch only adds the new
implementation but leaves the old one around, so the the driver doesn't
change its behaviour. The switch to actually use the new code is
submitted separately.

Signed-off-by: Daniel Mack <zonque@gmail.com>
---
 sound/usb/card.h     |   60 ++++
 sound/usb/endpoint.c |  904 +++++++++++++++++++++++++++++++++++++++++++++++++-
 sound/usb/endpoint.h |   25 ++
 sound/usb/usbaudio.h |    1 +
 4 files changed, 979 insertions(+), 11 deletions(-)

diff --git a/sound/usb/card.h b/sound/usb/card.h
index a39edcc..219a8cd 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -29,13 +29,17 @@ struct audioformat {
 };
 
 struct snd_usb_substream;
+struct snd_usb_endpoint;
 
 struct snd_urb_ctx {
 	struct urb *urb;
 	unsigned int buffer_size;	/* size of data buffer, if data URB */
 	struct snd_usb_substream *subs;
+	struct snd_usb_endpoint *ep;
 	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 list_head ready_list;
 };
 
 struct snd_urb_ops {
@@ -45,6 +49,62 @@ struct snd_urb_ops {
 	int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
 };
 
+struct snd_usb_endpoint {
+	struct snd_usb_audio *chip;
+
+	int use_count;
+	int ep_num;     /* the referenced endpoint number */
+	int type;	/* SND_USB_ENDPOINT_TYPE_* */
+	int activated;
+
+	void (* prepare_data_urb) (struct snd_usb_substream *subs,
+				   struct urb *urb);
+	void (* retire_data_urb) (struct snd_usb_substream *subs,
+				  struct urb *urb);
+
+	struct snd_usb_substream *data_subs;
+	struct snd_usb_endpoint *sync_master;
+	struct snd_usb_endpoint *sync_slave;
+
+	struct snd_urb_ctx urb[MAX_URBS];
+	
+
+	struct snd_usb_packet_info {
+		uint32_t packet_size[MAX_PACKS_HS];
+		int packets;
+	} next_packet[MAX_URBS];
+	int next_packet_read_pos, next_packet_write_pos;
+	struct list_head ready_playback_urbs;
+	struct tasklet_struct outbound_tasklet;
+
+	unsigned int nurbs;			/* # urbs */
+	unsigned long active_mask;		/* bitmask of active urbs */
+	unsigned long unlink_mask;		/* bitmask of unlinked urbs */
+	char *syncbuf;			     /* sync buffer for all sync URBs */
+	dma_addr_t sync_dma;		      /* DMA address of syncbuf */
+
+	unsigned int pipe;      /* the data i/o pipe */
+	unsigned int freqn;     /* nominal sampling rate in fs/fps in Q16.16 format */
+	unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */
+	int	   freqshift;  /* how much to shift the feedback value to get Q16.16 */
+	unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
+	unsigned int phase;      /* phase accumulator */
+	unsigned int maxpacksize;	/* max packet size in bytes */
+	unsigned int maxframesize;      /* max packet size in frames */
+	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
+	unsigned int curframesize;      /* current packet size in frames (for capture) */
+	unsigned int syncmaxsize;	/* sync endpoint packet size */
+	unsigned int fill_max: 1;	/* fill max packet size always */
+	unsigned int datainterval;      /* log_2 of data packet interval */
+	unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
+	unsigned char silence_value;
+	unsigned int stride;
+	int iface, alt_idx;
+
+	spinlock_t lock;
+	struct list_head list;
+};
+
 struct snd_usb_substream {
 	struct snd_usb_stream *stream;
 	struct usb_device *dev;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 81c6ede..633cb63 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -19,9 +19,11 @@
 #include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/slab.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 
 #include "usbaudio.h"
 #include "helper.h"
@@ -50,7 +52,7 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
 /*
  * unlink active urbs.
  */
-static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
+static int deactivate_urbs_old(struct snd_usb_substream *subs, int force, int can_sleep)
 {
 	struct snd_usb_audio *chip = subs->stream->chip;
 	unsigned int i;
@@ -112,7 +114,7 @@ static void release_urb_ctx(struct snd_urb_ctx *u)
 /*
  *  wait until all urbs are processed.
  */
-static int wait_clear_urbs(struct snd_usb_substream *subs)
+static int wait_clear_urbs_old(struct snd_usb_substream *subs)
 {
 	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
 	unsigned int i;
@@ -147,8 +149,8 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
 	int i;
 
 	/* stop urbs (to be sure) */
-	deactivate_urbs(subs, force, 1);
-	wait_clear_urbs(subs);
+	deactivate_urbs_old(subs, force, 1);
+	wait_clear_urbs_old(subs);
 
 	for (i = 0; i < MAX_URBS; i++)
 		release_urb_ctx(&subs->dataurb[i]);
@@ -163,7 +165,7 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
 /*
  * complete callback from data urb
  */
-static void snd_complete_urb(struct urb *urb)
+static void snd_complete_urb_old(struct urb *urb)
 {
 	struct snd_urb_ctx *ctx = urb->context;
 	struct snd_usb_substream *subs = ctx->subs;
@@ -317,7 +319,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
 		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
 		u->urb->interval = 1 << subs->datainterval;
 		u->urb->context = u;
-		u->urb->complete = snd_complete_urb;
+		u->urb->complete = snd_complete_urb_old;
 	}
 
 	if (subs->syncpipe) {
@@ -855,7 +857,7 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru
 
  __error:
 	// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
-	deactivate_urbs(subs, 0, 0);
+	deactivate_urbs_old(subs, 0, 0);
 	return -EPIPE;
 }
 
@@ -916,7 +918,7 @@ 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:
-		return deactivate_urbs(subs, 0, 0);
+		return deactivate_urbs_old(subs, 0, 0);
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		subs->ops.prepare = prepare_nodata_playback_urb;
 		return 0;
@@ -934,7 +936,7 @@ int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int c
 		subs->ops.retire = retire_capture_urb;
 		return start_urbs(subs, substream->runtime);
 	case SNDRV_PCM_TRIGGER_STOP:
-		return deactivate_urbs(subs, 0, 0);
+		return deactivate_urbs_old(subs, 0, 0);
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		subs->ops.retire = retire_paused_capture_urb;
 		return 0;
@@ -950,8 +952,8 @@ 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);
+	deactivate_urbs_old(subs, 0, 1);
+	wait_clear_urbs_old(subs);
 
 	/* for playback, submit the URBs now; otherwise, the first hwptr_done
 	 * updates for all URBs would happen at the same time when starting */
@@ -963,3 +965,883 @@ int snd_usb_substream_prepare(struct snd_usb_substream *subs,
 	return 0;
 }
 
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
+{
+	return  ep->sync_master &&
+		ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA &&
+		ep->type == SND_USB_ENDPOINT_TYPE_DATA &&
+		usb_pipeout(ep->pipe);
+}
+
+/* determine the number of frames in the next packet */
+static int next_packet_size(struct snd_usb_endpoint *ep)
+{
+	unsigned long flags;
+
+	if (ep->fill_max)
+		return ep->maxframesize;
+
+	spin_lock_irqsave(&ep->lock, flags);
+	ep->phase = (ep->phase & 0xffff)
+		+ (ep->freqm << ep->datainterval);
+	spin_unlock_irqrestore(&ep->lock, flags);
+
+	return min(ep->phase >> 16, ep->maxframesize);
+}
+
+static void retire_outbound_urb(struct snd_usb_endpoint *ep,
+				struct snd_urb_ctx *urb_ctx)
+{
+	if (ep->retire_data_urb)
+		ep->retire_data_urb(ep->data_subs, urb_ctx->urb);
+}
+
+static void retire_inbound_urb(struct snd_usb_endpoint *ep,
+			       struct snd_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+
+	if (ep->sync_slave)
+		snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
+
+	if (ep->retire_data_urb)
+		ep->retire_data_urb(ep->data_subs, urb);
+}
+
+static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep,
+				       struct snd_urb_ctx *ctx)
+{
+	int i;
+
+	for (i = 0; i < ctx->packets; ++i)
+		ctx->packet_size[i] = next_packet_size(ep);
+}
+
+/*
+ * Prepare a PLAYBACK urb for submission to the bus.
+ */
+static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
+				 struct snd_urb_ctx *ctx)
+{
+	int i;
+	struct urb *urb = ctx->urb;
+	unsigned char *cp = urb->transfer_buffer;
+
+	urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+	switch (ep->type) {
+	case SND_USB_ENDPOINT_TYPE_DATA:
+		if (ep->prepare_data_urb) {
+			ep->prepare_data_urb(ep->data_subs, urb);
+		} else {
+			/* no data provider, so send silence */
+			unsigned int offs = 0;
+			for (i = 0; i < ctx->packets; ++i) {
+				int counts = ctx->packet_size[i];
+				urb->iso_frame_desc[i].offset = offs * ep->stride;
+				urb->iso_frame_desc[i].length = counts * ep->stride;
+				offs += counts;
+			}
+
+			urb->number_of_packets = ctx->packets;
+			urb->transfer_buffer_length = offs * ep->stride;
+			memset(urb->transfer_buffer, ep->silence_value,
+			       offs * ep->stride);
+		}
+		break;
+
+	case SND_USB_ENDPOINT_TYPE_SYNC:
+		if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
+			/*
+			 * fill the length and offset of each urb descriptor.
+			 * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+			 */
+			urb->iso_frame_desc[0].length = 4;
+			urb->iso_frame_desc[0].offset = 0;
+			cp[0] = ep->freqn;
+			cp[1] = ep->freqn >> 8;
+			cp[2] = ep->freqn >> 16;
+			cp[3] = ep->freqn >> 24;
+		} else {
+			/*
+			 * fill the length and offset of each urb descriptor.
+			 * the fixed 10.14 frequency is passed through the pipe.
+			 */
+			urb->iso_frame_desc[0].length = 3;
+			urb->iso_frame_desc[0].offset = 0;
+			cp[0] = ep->freqn >> 2;
+			cp[1] = ep->freqn >> 10;
+			cp[2] = ep->freqn >> 18;
+		}
+
+		break;
+	}
+}
+
+/*
+ * Prepare a CAPTURE or SYNC urb for submission to the bus.
+ */
+static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
+				       struct snd_urb_ctx *urb_ctx)
+{
+	int i, offs;
+	struct urb *urb = urb_ctx->urb;
+
+	urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+	switch (ep->type) {
+	case SND_USB_ENDPOINT_TYPE_DATA:
+		offs = 0;
+		for (i = 0; i < urb_ctx->packets; i++) {
+			urb->iso_frame_desc[i].offset = offs;
+			urb->iso_frame_desc[i].length = ep->curpacksize;
+			offs += ep->curpacksize;
+		}
+
+		urb->transfer_buffer_length = offs;
+		urb->number_of_packets = urb_ctx->packets;
+		break;
+
+	case SND_USB_ENDPOINT_TYPE_SYNC:
+		urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize);
+		urb->iso_frame_desc[0].offset = 0;
+		break;
+	}
+}
+
+/*
+ * complete callback for urbs
+ */
+static void snd_complete_urb(struct urb *urb)
+{
+	struct snd_urb_ctx *ctx = urb->context;
+	struct snd_usb_endpoint *ep = ctx->ep;
+	unsigned long flags;
+	int err;
+
+	if (ep->use_count == 0)
+		goto exit_clear;
+
+	if (usb_pipeout(ep->pipe)) {
+		retire_outbound_urb(ep, ctx);
+		if (ep->use_count == 0) /* can be stopped during retire callback */
+			goto exit_clear;
+
+		if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+			clear_bit(ctx->index, &ep->active_mask);
+			spin_lock_irqsave(&ep->lock, flags);
+			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+			spin_unlock_irqrestore(&ep->lock, flags);
+			tasklet_schedule(&ep->outbound_tasklet);
+			return;
+		}
+
+		prepare_outbound_urb_sizes(ep, ctx);
+		prepare_outbound_urb(ep, ctx);
+	} else {
+		retire_inbound_urb(ep, ctx);
+		if (ep->use_count == 0) /* can be stopped during retire callback */
+			goto exit_clear;
+
+		prepare_inbound_urb(ep, ctx);
+	}
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err == 0)
+		return;
+
+	snd_printk(KERN_ERR "cannot submit urb (err = %d)\n", err);
+	//snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+exit_clear:
+	clear_bit(ctx->index, &ep->active_mask);
+}
+
+static void snd_usb_tasklet_handler(unsigned long data)
+{
+	struct snd_usb_endpoint *ep = (void *) data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ep->lock, flags);
+
+	while (!list_empty(&ep->ready_playback_urbs) &&
+	       ep->next_packet_read_pos != ep->next_packet_write_pos) {
+		struct snd_usb_packet_info *packet;
+		struct snd_urb_ctx *ctx;
+		struct urb *urb;
+		int err, i;
+
+		packet = ep->next_packet + ep->next_packet_read_pos;
+		ep->next_packet_read_pos++;
+		ep->next_packet_read_pos %= MAX_URBS;
+
+		/* take URB out of FIFO */
+		ctx = list_first_entry(&ep->ready_playback_urbs,
+				       struct snd_urb_ctx, ready_list);
+		list_del(&ctx->ready_list);
+		urb = ctx->urb;
+
+		/* copy over the length information */
+		for (i = 0; i < packet->packets; i++)
+			ctx->packet_size[i] = packet->packet_size[i];
+
+		prepare_outbound_urb(ep, ctx);
+
+		err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
+		if (err < 0) {
+			snd_printk(KERN_ERR "Unable to submit urb #%d: %d (urb %p)\n",
+				   ctx->index, err, ctx->urb);
+		} else {
+			set_bit(ctx->index, &ep->active_mask);
+		}
+	}
+
+	spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+					      struct usb_host_interface *alts,
+					      int ep_num, int direction, int type)
+{
+	struct list_head *p;
+	struct snd_usb_endpoint *ep;
+	int ret, is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
+
+	mutex_lock(&chip->mutex);
+
+	list_for_each(p, &chip->ep_list) {
+		ep = list_entry(p, struct snd_usb_endpoint, list);
+		if (ep->ep_num == ep_num &&
+		    ep->iface == alts->desc.bInterfaceNumber &&
+		    ep->alt_idx == alts->desc.bAlternateSetting) {
+			snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n",
+					ep_num, ep->iface, ep->alt_idx, ep);
+			goto __exit_unlock;
+		}
+	}
+
+	snd_printdd(KERN_DEBUG "Creating new %s %s endpoint #%x\n",
+		   is_playback ? "playback" : "capture",
+		   type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync",
+		   ep_num);
+
+	/* select the alt setting once so the endpoints become valid */
+	ret = usb_set_interface(chip->dev, alts->desc.bInterfaceNumber,
+				alts->desc.bAlternateSetting);
+	if (ret < 0) {
+		snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+					__func__, ret);
+		ep = NULL;
+		goto __exit_unlock;
+	}
+
+	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+	if (!ep)
+		goto __exit_unlock;
+
+	spin_lock_init(&ep->lock);
+
+	ep->chip = chip;
+	ep->type = type;
+	ep->ep_num = ep_num;
+	ep->iface = alts->desc.bInterfaceNumber;
+	ep->alt_idx = alts->desc.bAlternateSetting;
+	INIT_LIST_HEAD(&ep->ready_playback_urbs);
+	ep->next_packet_read_pos = 0;
+	ep->next_packet_write_pos = 0;
+	tasklet_init(&ep->outbound_tasklet, snd_usb_tasklet_handler,
+			(unsigned long) ep);
+
+	ep_num &= USB_ENDPOINT_NUMBER_MASK;
+
+	if (is_playback)
+		ep->pipe = usb_sndisocpipe(chip->dev, ep_num);
+	else
+		ep->pipe = usb_rcvisocpipe(chip->dev, ep_num);
+
+	if (type == SND_USB_ENDPOINT_TYPE_SYNC) {
+		if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+		    get_endpoint(alts, 1)->bRefresh >= 1 &&
+		    get_endpoint(alts, 1)->bRefresh <= 9)
+			ep->syncinterval = get_endpoint(alts, 1)->bRefresh;
+		else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL)
+			ep->syncinterval = 1;
+		else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+			 get_endpoint(alts, 1)->bInterval <= 16)
+			ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+		else
+			ep->syncinterval = 3;
+
+		ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
+	}
+
+	list_add_tail(&ep->list, &chip->ep_list);
+
+__exit_unlock:
+	mutex_unlock(&chip->mutex);
+
+	return ep;
+}
+
+/*
+ *  wait until all urbs are processed.
+ */
+static int wait_clear_urbs(struct snd_usb_endpoint *ep)
+{
+	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
+	unsigned int i;
+	int alive;
+
+	do {
+		alive = 0;
+		for (i = 0; i < ep->nurbs; i++)
+			if (test_bit(i, &ep->active_mask))
+				alive++;
+
+		if (! alive)
+			break;
+
+		schedule_timeout_uninterruptible(1);
+	} while (time_before(jiffies, end_time));
+
+	if (alive)
+		snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n",
+					alive, ep->ep_num);
+
+	return 0;
+}
+
+/*
+ * unlink active urbs.
+ */
+static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep)
+{
+	unsigned int i;
+	int async;
+
+	if (!force && ep->chip->shutdown) /* to be sure... */
+		return -EBADFD;
+
+	async = !can_sleep && ep->chip->async_unlink;
+
+	if (!async && in_interrupt())
+		return 0;
+
+	for (i = 0; i < ep->nurbs; i++) {
+		if (test_bit(i, &ep->active_mask)) {
+			if (!test_and_set_bit(i, &ep->unlink_mask)) {
+				struct urb *u = ep->urb[i].urb;
+				if (async)
+					usb_unlink_urb(u);
+				else
+					usb_kill_urb(u);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * release an endpoint's urbs
+ */
+static void release_urbs(struct snd_usb_endpoint *ep, int force)
+{
+	int i;
+
+	/* route incoming urbs to nirvana */
+	ep->retire_data_urb = NULL;
+	ep->prepare_data_urb = NULL;
+
+	/* stop urbs */
+	deactivate_urbs(ep, force, 1);
+	wait_clear_urbs(ep);
+
+	for (i = 0; i < ep->nurbs; i++)
+		release_urb_ctx(&ep->urb[i]);
+
+	usb_free_coherent(ep->chip->dev, SYNC_URBS * 4,
+			  ep->syncbuf, ep->sync_dma);
+
+	ep->syncbuf = NULL;
+	ep->nurbs = 0;
+}
+
+static int data_ep_set_params(struct snd_usb_endpoint *ep,
+			      struct snd_pcm_hw_params *hw_params,
+			      struct audioformat *fmt,
+			      struct snd_usb_endpoint *sync_ep)
+{
+	unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
+	int period_bytes = params_period_bytes(hw_params);
+	int format = params_format(hw_params);
+	int is_playback = usb_pipeout(ep->pipe);
+	int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) *
+							params_channels(hw_params);
+
+	ep->datainterval = fmt->datainterval;
+	ep->stride = frame_bits >> 3;
+	ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
+
+	/* calculate max. frequency */
+	if (ep->maxpacksize) {
+		/* whatever fits into a max. size packet */
+		maxsize = ep->maxpacksize;
+		ep->freqmax = (maxsize / (frame_bits >> 3))
+				<< (16 - ep->datainterval);
+	} else {
+		/* no max. packet size: just take 25% higher than nominal */
+		ep->freqmax = ep->freqn + (ep->freqn >> 2);
+		maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
+				>> (16 - ep->datainterval);
+	}
+
+	if (ep->fill_max)
+		ep->curpacksize = ep->maxpacksize;
+	else
+		ep->curpacksize = maxsize;
+
+	if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL)
+		packs_per_ms = 8 >> ep->datainterval;
+	else
+		packs_per_ms = 1;
+
+	if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+		urb_packs = max(ep->chip->nrpacks, 1);
+		urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
+	} else {
+		urb_packs = 1;
+	}
+
+	urb_packs *= packs_per_ms;
+
+	if (sync_ep && !snd_usb_endpoint_implict_feedback_sink(ep))
+		urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
+
+	/* decide how many packets to be used */
+	if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+		unsigned int minsize, maxpacks;
+		/* determine how small a packet can be */
+		minsize = (ep->freqn >> (16 - ep->datainterval))
+			  * (frame_bits >> 3);
+		/* with sync from device, assume it can be 12% lower */
+		if (sync_ep)
+			minsize -= minsize >> 3;
+		minsize = max(minsize, 1u);
+		total_packs = (period_bytes + minsize - 1) / minsize;
+		/* we need at least two URBs for queueing */
+		if (total_packs < 2) {
+			total_packs = 2;
+		} else {
+			/* and we don't want too long a queue either */
+			maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
+			total_packs = min(total_packs, maxpacks);
+		}
+	} else {
+		while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+			urb_packs >>= 1;
+		total_packs = MAX_URBS * urb_packs;
+	}
+
+	ep->nurbs = (total_packs + urb_packs - 1) / urb_packs;
+	if (ep->nurbs > MAX_URBS) {
+		/* too much... */
+		ep->nurbs = MAX_URBS;
+		total_packs = MAX_URBS * urb_packs;
+	} else if (ep->nurbs < 2) {
+		/* too little - we need at least two packets
+		 * to ensure contiguous playback/capture
+		 */
+		ep->nurbs = 2;
+	}
+
+	/* allocate and initialize data urbs */
+	for (i = 0; i < ep->nurbs; i++) {
+		struct snd_urb_ctx *u = &ep->urb[i];
+		u->index = i;
+		u->ep = ep;
+		u->packets = (i + 1) * total_packs / ep->nurbs
+			- i * total_packs / ep->nurbs;
+		u->buffer_size = maxsize * u->packets;
+
+		if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
+			u->packets++; /* for transfer delimiter */
+		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+		if (!u->urb)
+			goto out_of_memory;
+
+		u->urb->transfer_buffer =
+			usb_alloc_coherent(ep->chip->dev, u->buffer_size,
+					   GFP_KERNEL, &u->urb->transfer_dma);
+		if (!u->urb->transfer_buffer)
+			goto out_of_memory;
+		u->urb->pipe = ep->pipe;
+		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+		u->urb->interval = 1 << ep->datainterval;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+	}
+
+	return 0;
+
+out_of_memory:
+	release_urbs(ep, 0);
+	return -ENOMEM;
+}
+
+static int sync_ep_set_params(struct snd_usb_endpoint *ep,
+			      struct snd_pcm_hw_params *hw_params,
+			      struct audioformat *fmt)
+{
+	int i;
+
+	ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
+					 GFP_KERNEL, &ep->sync_dma);
+	if (!ep->syncbuf)
+		goto out_of_memory;
+
+	for (i = 0; i < SYNC_URBS; i++) {
+		struct snd_urb_ctx *u = &ep->urb[i];
+		u->index = i;
+		u->ep = ep;
+		u->packets = 1;
+		u->urb = usb_alloc_urb(1, GFP_KERNEL);
+		if (!u->urb)
+			goto out_of_memory;
+		u->urb->transfer_buffer = ep->syncbuf + i * 4;
+		u->urb->transfer_dma = ep->sync_dma + i * 4;
+		u->urb->transfer_buffer_length = 4;
+		u->urb->pipe = ep->pipe;
+		u->urb->transfer_flags = URB_ISO_ASAP |
+					 URB_NO_TRANSFER_DMA_MAP;
+		u->urb->number_of_packets = 1;
+		u->urb->interval = 1 << ep->syncinterval;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+	}
+
+	ep->nurbs = SYNC_URBS;
+
+	return 0;
+
+out_of_memory:
+	release_urbs(ep, 0);
+	return -ENOMEM;
+}
+
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+				struct snd_pcm_hw_params *hw_params,
+				struct audioformat *fmt,
+				struct snd_usb_endpoint *sync_ep)
+{
+	int err;
+
+	if (ep->use_count > 0) {
+		snd_printk(KERN_WARNING "Unable to change format on ep #%x: already in use\n",
+			   ep->ep_num);
+		return -EBUSY;
+	}
+
+	ep->datainterval = fmt->datainterval;
+	ep->maxpacksize = fmt->maxpacksize;
+	ep->fill_max = fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX;
+
+	if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
+		ep->freqn = get_usb_full_speed_rate(params_rate(hw_params));
+	else
+		ep->freqn = get_usb_high_speed_rate(params_rate(hw_params));
+
+	/* calculate the frequency in 16.16 format */
+	ep->freqm = ep->freqn;
+	ep->freqshift = INT_MIN;
+
+	ep->phase = 0;
+
+	switch (ep->type) {
+	case  SND_USB_ENDPOINT_TYPE_DATA:
+		err = data_ep_set_params(ep, hw_params, fmt, sync_ep);
+		break;
+	case  SND_USB_ENDPOINT_TYPE_SYNC:
+		err = sync_ep_set_params(ep, hw_params, fmt);
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	snd_printdd(KERN_DEBUG "Setting params for ep #%x (type %d, %d urbs), ret=%d\n",
+		   ep->ep_num, ep->type, ep->nurbs, err);
+
+	return err;
+}
+
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
+{
+	int err;
+	unsigned int i;
+	unsigned long flags;
+
+	if (ep->chip->shutdown)
+		return -EBADFD;
+
+	/* already running? */
+	if (++ep->use_count != 1)
+		return 0;
+
+	if (snd_BUG_ON(!ep->activated))
+		return -EINVAL;
+
+	ep->phase = 0;
+	ep->active_mask = 0;
+	ep->unlink_mask = 0;
+	tasklet_kill(&ep->outbound_tasklet);
+
+	spin_lock_irqsave(&ep->lock, flags);
+	INIT_LIST_HEAD(&ep->ready_playback_urbs);
+	ep->next_packet_read_pos = 0;
+	ep->next_packet_write_pos = 0;
+	spin_unlock_irqrestore(&ep->lock, flags);
+
+	/*
+	 * If this endpoint has a data endpoint as implicit feedback source,
+	 * don't start the urbs here. Instead, mark them all as available,
+	 * wait for the record urbs to arrive and queue from that context.
+	 */
+
+	if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+		spin_lock_irqsave(&ep->lock, flags);
+		for (i = 0; i < ep->nurbs; i++) {
+			struct snd_urb_ctx *ctx = ep->urb + i;
+			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+		}
+		spin_unlock_irqrestore(&ep->lock, flags);
+
+		return 0;
+	}
+
+	for (i = 0; i < ep->nurbs; i++) {
+		struct urb *urb = ep->urb[i].urb;
+
+		if (snd_BUG_ON(!urb))
+			return -EINVAL;
+
+		if (usb_pipeout(ep->pipe)) {
+			prepare_outbound_urb_sizes(ep, urb->context);
+			prepare_outbound_urb(ep, urb->context);
+		} else {
+			prepare_inbound_urb(ep, urb->context);
+		}
+
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0) {
+			snd_printk(KERN_ERR "cannot submit urb %d, error %d: %s\n",
+				   i, err, usb_error_string(err));
+			goto __error;
+		}
+		set_bit(i, &ep->active_mask);
+	}
+
+	return 0;
+
+__error:
+	ep->use_count--;
+	deactivate_urbs(ep, 0, 0);
+	return -EPIPE;
+}
+
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, int force, int can_sleep)
+{
+	unsigned long flags;
+
+	if (!ep)
+		return;
+
+	if (snd_BUG_ON(ep->use_count == 0))
+		return;
+
+	if (snd_BUG_ON(!ep->activated))
+		return;
+
+	if (--ep->use_count == 0) {
+		tasklet_kill(&ep->outbound_tasklet);
+
+		spin_lock_irqsave(&ep->lock, flags);
+		INIT_LIST_HEAD(&ep->ready_playback_urbs);
+		ep->next_packet_read_pos = 0;
+		ep->next_packet_write_pos = 0;
+		spin_unlock_irqrestore(&ep->lock, flags);
+
+		ep->data_subs = NULL;
+		ep->sync_master = NULL;
+		ep->sync_slave = NULL;
+		ep->retire_data_urb = NULL;
+		ep->prepare_data_urb = NULL;
+		ep->sync_master = NULL;
+		deactivate_urbs(ep, force, can_sleep);
+	}
+}
+
+int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep)
+{
+	if (ep && ep->use_count == 0 && !ep->chip->shutdown && !ep->activated) {
+		int ret;
+
+		ret = usb_set_interface(ep->chip->dev, ep->iface, ep->alt_idx);
+		if (ret < 0) {
+			snd_printk(KERN_ERR "%s() usb_set_interface() failed, ret = %d\n",
+						__func__, ret);
+			return ret;
+		}
+
+		ep->activated = 1;
+	}
+
+	return 0;
+}
+
+int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
+{
+	if (ep && ep->use_count == 0 && !ep->chip->shutdown && ep->activated) {
+		int ret;
+
+		ret = usb_set_interface(ep->chip->dev, ep->iface, 0);
+		if (ret < 0) {
+			snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+						__func__, ret);
+			return ret;
+		}
+
+		ep->activated = 0;
+	}
+
+	return 0;
+}
+
+void snd_usb_endpoint_free(struct list_head *head)
+{
+	struct snd_usb_endpoint *ep;
+
+	ep = list_entry(head, struct snd_usb_endpoint, list);
+	deactivate_urbs(ep, 1, 1);
+	release_urbs(ep, 1);
+}
+
+/*
+ * process after playback sync complete
+ *
+ * Full speed devices report feedback values in 10.14 format as samples per
+ * frame, high speed devices in 16.16 format as samples per microframe.
+ * Because the Audio Class 1 spec was written before USB 2.0, many high speed
+ * devices use a wrong interpretation, some others use an entirely different
+ * format.  Therefore, we cannot predict what format any particular device uses
+ * and must detect it automatically.
+ */
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+			     struct snd_usb_endpoint *sender,
+			     const struct urb *urb)
+{
+	int shift;
+	unsigned int f;
+	unsigned long flags;
+
+	snd_BUG_ON(ep == sender);
+
+	if (snd_usb_endpoint_implict_feedback_sink(ep) && ep->use_count > 0) {
+		/* implicit feedback case */
+		int i, bytes = 0;
+		struct snd_urb_ctx *in_ctx;
+		struct snd_usb_packet_info *out_packet;
+
+		in_ctx = urb->context;
+
+		/* Count overall packet size */
+		for (i = 0; i < in_ctx->packets; i++)
+			if (urb->iso_frame_desc[i].status == 0)
+				bytes += 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;
+
+		spin_lock_irqsave(&ep->lock, flags);
+		out_packet = ep->next_packet + ep->next_packet_write_pos;
+
+		/*
+		 * Iterate through the inbound packet and prepare the lengths
+		 * for the output packet. The OUT packet we are about to send
+		 * will have the same amount of payload than the IN packet we
+		 * just received.
+		 */
+
+		out_packet->packets = in_ctx->packets;
+		for (i = 0; i < in_ctx->packets; i++) {
+			if (urb->iso_frame_desc[i].status == 0)
+				out_packet->packet_size[i] =
+					urb->iso_frame_desc[i].actual_length / ep->stride;
+			else
+				out_packet->packet_size[i] = 0;
+		}
+
+		ep->next_packet_write_pos++;
+		ep->next_packet_write_pos %= MAX_URBS;
+		spin_unlock_irqrestore(&ep->lock, flags);
+		tasklet_schedule(&ep->outbound_tasklet);
+
+		return;
+	}
+
+	if (urb->iso_frame_desc[0].status != 0 ||
+	    urb->iso_frame_desc[0].actual_length < 3)
+		return;
+
+	f = le32_to_cpup(urb->transfer_buffer);
+	if (urb->iso_frame_desc[0].actual_length == 3)
+		f &= 0x00ffffff;
+	else
+		f &= 0x0fffffff;
+
+	if (f == 0)
+		return;
+
+	if (unlikely(ep->freqshift == INT_MIN)) {
+		/*
+		 * The first time we see a feedback value, determine its format
+		 * by shifting it left or right until it matches the nominal
+		 * frequency value.  This assumes that the feedback does not
+		 * differ from the nominal value more than +50% or -25%.
+		 */
+		shift = 0;
+		while (f < ep->freqn - ep->freqn / 4) {
+			f <<= 1;
+			shift++;
+		}
+		while (f > ep->freqn + ep->freqn / 2) {
+			f >>= 1;
+			shift--;
+		}
+		ep->freqshift = shift;
+	}
+	else if (ep->freqshift >= 0)
+		f <<= ep->freqshift;
+	else
+		f >>= -ep->freqshift;
+
+	if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) {
+		/*
+		 * If the frequency looks valid, set it.
+		 * This value is referred to in prepare_playback_urb().
+		 */
+		spin_lock_irqsave(&ep->lock, flags);
+		ep->freqm = f;
+		spin_unlock_irqrestore(&ep->lock, flags);
+	} else {
+		/*
+		 * Out of range; maybe the shift value is wrong.
+		 * Reset it so that we autodetect again the next time.
+		 */
+		ep->freqshift = INT_MIN;
+	}
+}
+
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 88eb63a..84aaec7 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -18,4 +18,29 @@ 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);
 
+
+#define SND_USB_ENDPOINT_TYPE_DATA     0
+#define SND_USB_ENDPOINT_TYPE_SYNC     1
+
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+					      struct usb_host_interface *alts,
+					      int ep_num, int direction, int type);
+
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+				struct snd_pcm_hw_params *hw_params,
+				struct audioformat *fmt,
+				struct snd_usb_endpoint *sync_ep);
+
+int  snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, int force, int can_sleep);
+int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
+int  snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_free(struct list_head *head);
+
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep);
+
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+			     struct snd_usb_endpoint *sender,
+			     const struct urb *urb);
+
 #endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index a16c21d..b8233eb 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -47,6 +47,7 @@ struct snd_usb_audio {
 	int num_suspended_intf;
 
 	struct list_head pcm_list;	/* list of pcm streams */
+	struct list_head ep_list;	/* list of audio-related endpoints */
 	int pcm_devs;
 
 	struct list_head midi_list;	/* list of midi interfaces */
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model
  2011-11-18 20:05 [PATCH 0/5] RFC v2 for snd-usb endpoint logic rework Daniel Mack
@ 2011-11-18 20:05 ` Daniel Mack
  0 siblings, 0 replies; 20+ messages in thread
From: Daniel Mack @ 2011-11-18 20:05 UTC (permalink / raw)
  To: alsa-devel; +Cc: gdiffey, tiwai, clemens, linuxaudio, Daniel Mack, blablack

In order to split changes properly, this patch only adds the new
implementation but leaves the old one around, so the rest of the driver
does not behave differently. The switch to actually use the new thing is
submitted separately.
---
 sound/usb/card.h     |   60 ++++
 sound/usb/endpoint.c |  911 +++++++++++++++++++++++++++++++++++++++++++++++++-
 sound/usb/endpoint.h |   25 ++
 sound/usb/usbaudio.h |    1 +
 4 files changed, 986 insertions(+), 11 deletions(-)

diff --git a/sound/usb/card.h b/sound/usb/card.h
index a39edcc..219a8cd 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -29,13 +29,17 @@ struct audioformat {
 };
 
 struct snd_usb_substream;
+struct snd_usb_endpoint;
 
 struct snd_urb_ctx {
 	struct urb *urb;
 	unsigned int buffer_size;	/* size of data buffer, if data URB */
 	struct snd_usb_substream *subs;
+	struct snd_usb_endpoint *ep;
 	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 list_head ready_list;
 };
 
 struct snd_urb_ops {
@@ -45,6 +49,62 @@ struct snd_urb_ops {
 	int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
 };
 
+struct snd_usb_endpoint {
+	struct snd_usb_audio *chip;
+
+	int use_count;
+	int ep_num;     /* the referenced endpoint number */
+	int type;	/* SND_USB_ENDPOINT_TYPE_* */
+	int activated;
+
+	void (* prepare_data_urb) (struct snd_usb_substream *subs,
+				   struct urb *urb);
+	void (* retire_data_urb) (struct snd_usb_substream *subs,
+				  struct urb *urb);
+
+	struct snd_usb_substream *data_subs;
+	struct snd_usb_endpoint *sync_master;
+	struct snd_usb_endpoint *sync_slave;
+
+	struct snd_urb_ctx urb[MAX_URBS];
+	
+
+	struct snd_usb_packet_info {
+		uint32_t packet_size[MAX_PACKS_HS];
+		int packets;
+	} next_packet[MAX_URBS];
+	int next_packet_read_pos, next_packet_write_pos;
+	struct list_head ready_playback_urbs;
+	struct tasklet_struct outbound_tasklet;
+
+	unsigned int nurbs;			/* # urbs */
+	unsigned long active_mask;		/* bitmask of active urbs */
+	unsigned long unlink_mask;		/* bitmask of unlinked urbs */
+	char *syncbuf;			     /* sync buffer for all sync URBs */
+	dma_addr_t sync_dma;		      /* DMA address of syncbuf */
+
+	unsigned int pipe;      /* the data i/o pipe */
+	unsigned int freqn;     /* nominal sampling rate in fs/fps in Q16.16 format */
+	unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */
+	int	   freqshift;  /* how much to shift the feedback value to get Q16.16 */
+	unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
+	unsigned int phase;      /* phase accumulator */
+	unsigned int maxpacksize;	/* max packet size in bytes */
+	unsigned int maxframesize;      /* max packet size in frames */
+	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
+	unsigned int curframesize;      /* current packet size in frames (for capture) */
+	unsigned int syncmaxsize;	/* sync endpoint packet size */
+	unsigned int fill_max: 1;	/* fill max packet size always */
+	unsigned int datainterval;      /* log_2 of data packet interval */
+	unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
+	unsigned char silence_value;
+	unsigned int stride;
+	int iface, alt_idx;
+
+	spinlock_t lock;
+	struct list_head list;
+};
+
 struct snd_usb_substream {
 	struct snd_usb_stream *stream;
 	struct usb_device *dev;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 81c6ede..ebf4008 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -19,9 +19,11 @@
 #include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/slab.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 
 #include "usbaudio.h"
 #include "helper.h"
@@ -50,7 +52,7 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
 /*
  * unlink active urbs.
  */
-static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
+static int deactivate_urbs_old(struct snd_usb_substream *subs, int force, int can_sleep)
 {
 	struct snd_usb_audio *chip = subs->stream->chip;
 	unsigned int i;
@@ -112,7 +114,7 @@ static void release_urb_ctx(struct snd_urb_ctx *u)
 /*
  *  wait until all urbs are processed.
  */
-static int wait_clear_urbs(struct snd_usb_substream *subs)
+static int wait_clear_urbs_old(struct snd_usb_substream *subs)
 {
 	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
 	unsigned int i;
@@ -147,8 +149,8 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
 	int i;
 
 	/* stop urbs (to be sure) */
-	deactivate_urbs(subs, force, 1);
-	wait_clear_urbs(subs);
+	deactivate_urbs_old(subs, force, 1);
+	wait_clear_urbs_old(subs);
 
 	for (i = 0; i < MAX_URBS; i++)
 		release_urb_ctx(&subs->dataurb[i]);
@@ -163,7 +165,7 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
 /*
  * complete callback from data urb
  */
-static void snd_complete_urb(struct urb *urb)
+static void snd_complete_urb_old(struct urb *urb)
 {
 	struct snd_urb_ctx *ctx = urb->context;
 	struct snd_usb_substream *subs = ctx->subs;
@@ -317,7 +319,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
 		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
 		u->urb->interval = 1 << subs->datainterval;
 		u->urb->context = u;
-		u->urb->complete = snd_complete_urb;
+		u->urb->complete = snd_complete_urb_old;
 	}
 
 	if (subs->syncpipe) {
@@ -855,7 +857,7 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru
 
  __error:
 	// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
-	deactivate_urbs(subs, 0, 0);
+	deactivate_urbs_old(subs, 0, 0);
 	return -EPIPE;
 }
 
@@ -916,7 +918,7 @@ 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:
-		return deactivate_urbs(subs, 0, 0);
+		return deactivate_urbs_old(subs, 0, 0);
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		subs->ops.prepare = prepare_nodata_playback_urb;
 		return 0;
@@ -934,7 +936,7 @@ int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int c
 		subs->ops.retire = retire_capture_urb;
 		return start_urbs(subs, substream->runtime);
 	case SNDRV_PCM_TRIGGER_STOP:
-		return deactivate_urbs(subs, 0, 0);
+		return deactivate_urbs_old(subs, 0, 0);
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		subs->ops.retire = retire_paused_capture_urb;
 		return 0;
@@ -950,8 +952,8 @@ 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);
+	deactivate_urbs_old(subs, 0, 1);
+	wait_clear_urbs_old(subs);
 
 	/* for playback, submit the URBs now; otherwise, the first hwptr_done
 	 * updates for all URBs would happen at the same time when starting */
@@ -963,3 +965,890 @@ int snd_usb_substream_prepare(struct snd_usb_substream *subs,
 	return 0;
 }
 
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
+{
+	return  ep->sync_master &&
+		ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA &&
+		ep->type == SND_USB_ENDPOINT_TYPE_DATA &&
+		usb_pipeout(ep->pipe);
+}
+
+/* determine the number of frames in the next packet */
+static int next_packet_size(struct snd_usb_endpoint *ep)
+{
+	unsigned long flags;
+
+	if (ep->fill_max)
+		return ep->maxframesize;
+
+	spin_lock_irqsave(&ep->lock, flags);
+	ep->phase = (ep->phase & 0xffff)
+		+ (ep->freqm << ep->datainterval);
+	spin_unlock_irqrestore(&ep->lock, flags);
+
+	return min(ep->phase >> 16, ep->maxframesize);
+}
+
+static void retire_outbound_urb(struct snd_usb_endpoint *ep,
+				struct snd_urb_ctx *urb_ctx)
+{
+	if (ep->retire_data_urb)
+		ep->retire_data_urb(ep->data_subs, urb_ctx->urb);
+}
+
+static void retire_inbound_urb(struct snd_usb_endpoint *ep,
+			       struct snd_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+
+	if (ep->sync_slave)
+		snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
+
+	if (ep->retire_data_urb)
+		ep->retire_data_urb(ep->data_subs, urb);
+}
+
+static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep,
+				       struct snd_urb_ctx *ctx)
+{
+	int i;
+
+	for (i = 0; i < ctx->packets; ++i)
+		ctx->packet_size[i] = next_packet_size(ep);
+}
+
+/*
+ * Prepare a PLAYBACK urb for submission to the bus.
+ */
+static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
+				 struct snd_urb_ctx *ctx)
+{
+	int i;
+	struct urb *urb = ctx->urb;
+	unsigned char *cp = urb->transfer_buffer;
+
+	urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+	switch (ep->type) {
+	case SND_USB_ENDPOINT_TYPE_DATA:
+		if (ep->prepare_data_urb) {
+			ep->prepare_data_urb(ep->data_subs, urb);
+		} else {
+			/* no data provider, so send silence */
+			unsigned int offs = 0;
+			for (i = 0; i < ctx->packets; ++i) {
+				int counts = ctx->packet_size[i];
+				urb->iso_frame_desc[i].offset = offs * ep->stride;
+				urb->iso_frame_desc[i].length = counts * ep->stride;
+				offs += counts;
+			}
+
+			urb->number_of_packets = ctx->packets;
+			urb->transfer_buffer_length = offs * ep->stride;
+			memset(urb->transfer_buffer, ep->silence_value,
+			       offs * ep->stride);
+		}
+		break;
+
+	case SND_USB_ENDPOINT_TYPE_SYNC:
+		if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
+			/*
+			 * fill the length and offset of each urb descriptor.
+			 * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+			 */
+			urb->iso_frame_desc[0].length = 4;
+			urb->iso_frame_desc[0].offset = 0;
+			cp[0] = ep->freqn;
+			cp[1] = ep->freqn >> 8;
+			cp[2] = ep->freqn >> 16;
+			cp[3] = ep->freqn >> 24;
+		} else {
+			/*
+			 * fill the length and offset of each urb descriptor.
+			 * the fixed 10.14 frequency is passed through the pipe.
+			 */
+			urb->iso_frame_desc[0].length = 3;
+			urb->iso_frame_desc[0].offset = 0;
+			cp[0] = ep->freqn >> 2;
+			cp[1] = ep->freqn >> 10;
+			cp[2] = ep->freqn >> 18;
+		}
+
+		break;
+	}
+}
+
+/*
+ * Prepare a CAPTURE or SYNC urb for submission to the bus.
+ */
+static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
+				       struct snd_urb_ctx *urb_ctx)
+{
+	int i, offs;
+	struct urb *urb = urb_ctx->urb;
+
+	urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+	switch (ep->type) {
+	case SND_USB_ENDPOINT_TYPE_DATA:
+		offs = 0;
+		for (i = 0; i < urb_ctx->packets; i++) {
+			urb->iso_frame_desc[i].offset = offs;
+			urb->iso_frame_desc[i].length = ep->curpacksize;
+			offs += ep->curpacksize;
+		}
+
+		urb->transfer_buffer_length = offs;
+		urb->number_of_packets = urb_ctx->packets;
+		break;
+
+	case SND_USB_ENDPOINT_TYPE_SYNC:
+		urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize);
+		urb->iso_frame_desc[0].offset = 0;
+		break;
+	}
+}
+
+/*
+ * complete callback for urbs
+ */
+static void snd_complete_urb(struct urb *urb)
+{
+	struct snd_urb_ctx *ctx = urb->context;
+	struct snd_usb_endpoint *ep = ctx->ep;
+	unsigned long flags;
+	int err;
+
+	if (ep->use_count == 0)
+		goto exit_clear;
+
+	if (usb_pipeout(ep->pipe)) {
+		retire_outbound_urb(ep, ctx);
+		if (ep->use_count == 0) /* can be stopped during retire callback */
+			goto exit_clear;
+
+		if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+			clear_bit(ctx->index, &ep->active_mask);
+			spin_lock_irqsave(&ep->lock, flags);
+			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+			tasklet_schedule(&ep->outbound_tasklet);
+			spin_unlock_irqrestore(&ep->lock, flags);
+			return;
+		}
+
+		prepare_outbound_urb_sizes(ep, ctx);
+		prepare_outbound_urb(ep, ctx);
+	} else {
+		retire_inbound_urb(ep, ctx);
+		if (ep->use_count == 0) /* can be stopped during retire callback */
+			goto exit_clear;
+
+		prepare_inbound_urb(ep, ctx);
+	}
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err == 0)
+		return;
+
+	snd_printk(KERN_ERR "cannot submit urb (err = %d)\n", err);
+	//snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+exit_clear:
+	clear_bit(ctx->index, &ep->active_mask);
+}
+
+static void snd_usb_tasklet_handler(unsigned long data)
+{
+	struct snd_usb_endpoint *ep = (void *) data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ep->lock, flags);
+	while (!list_empty(&ep->ready_playback_urbs) &&
+	       ep->next_packet_read_pos != ep->next_packet_write_pos) {
+		struct snd_usb_packet_info *packet;
+		struct snd_urb_ctx *ctx;
+		struct urb *urb;
+		int err, i;
+
+		packet = ep->next_packet + ep->next_packet_read_pos;
+		ep->next_packet_read_pos++;
+		ep->next_packet_read_pos %= MAX_URBS;
+
+		/* take URB out of FIFO */
+		ctx = list_first_entry(&ep->ready_playback_urbs,
+				       struct snd_urb_ctx, ready_list);
+		list_del(&ctx->ready_list);
+		urb = ctx->urb;
+
+		/* copy over the length information */
+		for (i = 0; i < packet->packets; i++)
+			ctx->packet_size[i] = packet->packet_size[i];
+
+		prepare_outbound_urb(ep, ctx);
+
+		err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
+		if (err < 0) {
+			snd_printk(KERN_ERR "Unable to submit urb #%d: %d (urb %p)\n",
+				   ctx->index, err, ctx->urb);
+		} else {
+			set_bit(ctx->index, &ep->active_mask);
+		}
+	}
+
+	spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+					      struct usb_host_interface *alts,
+					      int ep_num, int direction, int type)
+{
+	struct list_head *p;
+	struct snd_usb_endpoint *ep;
+	int ret, is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
+
+	mutex_lock(&chip->mutex);
+
+	list_for_each(p, &chip->ep_list) {
+		ep = list_entry(p, struct snd_usb_endpoint, list);
+		if (ep->ep_num == ep_num &&
+		    ep->iface == alts->desc.bInterfaceNumber &&
+		    ep->alt_idx == alts->desc.bAlternateSetting) {
+			snd_printk("Re-using EP %x in iface %d,%d @%p\n",
+					ep_num, ep->iface, ep->alt_idx, ep);
+			goto __exit_unlock;
+		}
+	}
+
+	snd_printk(KERN_INFO "Creating new %s %s endpoint #%x\n",
+		   is_playback ? "playback" : "capture",
+		   type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync",
+		   ep_num);
+
+	/* select the alt setting once so the endpoints become valid */
+	snd_printk("%s() calling usb_set_interface()\n", __func__);
+	ret = usb_set_interface(chip->dev, alts->desc.bInterfaceNumber,
+				alts->desc.bAlternateSetting);
+
+	if (ret < 0) {
+		snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+					__func__, ret);
+		ep = NULL;
+		goto __exit_unlock;
+	}
+
+	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+	if (!ep)
+		goto __exit_unlock;
+
+	spin_lock_init(&ep->lock);
+
+	ep->chip = chip;
+	ep->type = type;
+	ep->ep_num = ep_num;
+	ep->iface = alts->desc.bInterfaceNumber;
+	ep->alt_idx = alts->desc.bAlternateSetting;
+	INIT_LIST_HEAD(&ep->ready_playback_urbs);
+	ep->next_packet_read_pos = 0;
+	ep->next_packet_write_pos = 0;
+	tasklet_init(&ep->outbound_tasklet, snd_usb_tasklet_handler,
+			(unsigned long) ep);
+
+	ep_num &= USB_ENDPOINT_NUMBER_MASK;
+
+	if (is_playback)
+		ep->pipe = usb_sndisocpipe(chip->dev, ep_num);
+	else
+		ep->pipe = usb_rcvisocpipe(chip->dev, ep_num);
+
+	if (type == SND_USB_ENDPOINT_TYPE_SYNC) {
+		if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+		    get_endpoint(alts, 1)->bRefresh >= 1 &&
+		    get_endpoint(alts, 1)->bRefresh <= 9)
+			ep->syncinterval = get_endpoint(alts, 1)->bRefresh;
+		else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL)
+			ep->syncinterval = 1;
+		else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+			 get_endpoint(alts, 1)->bInterval <= 16)
+			ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+		else
+			ep->syncinterval = 3;
+
+		ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
+	}
+
+	list_add_tail(&ep->list, &chip->ep_list);
+
+__exit_unlock:
+	mutex_unlock(&chip->mutex);
+
+	return ep;
+}
+
+/*
+ *  wait until all urbs are processed.
+ */
+static int wait_clear_urbs(struct snd_usb_endpoint *ep)
+{
+	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
+	unsigned int i;
+	int alive;
+
+	do {
+		alive = 0;
+		for (i = 0; i < ep->nurbs; i++)
+			if (test_bit(i, &ep->active_mask))
+				alive++;
+
+		if (! alive)
+			break;
+
+		schedule_timeout_uninterruptible(1);
+	} while (time_before(jiffies, end_time));
+
+	if (alive)
+		snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n",
+					alive, ep->ep_num);
+
+	return 0;
+}
+
+/*
+ * unlink active urbs.
+ */
+static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep)
+{
+	unsigned int i;
+	int async;
+
+	if (!force && ep->chip->shutdown) /* to be sure... */
+		return -EBADFD;
+
+	async = !can_sleep && ep->chip->async_unlink;
+
+	if (!async && in_interrupt())
+		return 0;
+
+	for (i = 0; i < ep->nurbs; i++) {
+		if (test_bit(i, &ep->active_mask)) {
+			if (!test_and_set_bit(i, &ep->unlink_mask)) {
+				struct urb *u = ep->urb[i].urb;
+				if (async)
+					usb_unlink_urb(u);
+				else
+					usb_kill_urb(u);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * release an endpoint's urbs
+ */
+static void release_urbs(struct snd_usb_endpoint *ep, int force)
+{
+	int i;
+
+	/* route incoming urbs to nirvana */
+	ep->retire_data_urb = NULL;
+	ep->prepare_data_urb = NULL;
+
+	/* stop urbs */
+	deactivate_urbs(ep, force, 1);
+	wait_clear_urbs(ep);
+
+	for (i = 0; i < ep->nurbs; i++)
+		release_urb_ctx(&ep->urb[i]);
+
+	usb_free_coherent(ep->chip->dev, SYNC_URBS * 4,
+			  ep->syncbuf, ep->sync_dma);
+
+	ep->syncbuf = NULL;
+	ep->nurbs = 0;
+}
+
+static int data_ep_set_params(struct snd_usb_endpoint *ep,
+			      struct snd_pcm_hw_params *hw_params,
+			      struct audioformat *fmt,
+			      struct snd_usb_endpoint *sync_ep)
+{
+	unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
+	int period_bytes = params_period_bytes(hw_params);
+	int format = params_format(hw_params);
+	int is_playback = usb_pipeout(ep->pipe);
+	int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) *
+							params_channels(hw_params);
+
+	ep->datainterval = fmt->datainterval;
+	ep->stride = frame_bits >> 3;
+	ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
+
+	/* calculate max. frequency */
+	if (ep->maxpacksize) {
+		/* whatever fits into a max. size packet */
+		maxsize = ep->maxpacksize;
+		ep->freqmax = (maxsize / (frame_bits >> 3))
+				<< (16 - ep->datainterval);
+	} else {
+		/* no max. packet size: just take 25% higher than nominal */
+		ep->freqmax = ep->freqn + (ep->freqn >> 2);
+		maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
+				>> (16 - ep->datainterval);
+	}
+
+	if (ep->fill_max)
+		ep->curpacksize = ep->maxpacksize;
+	else
+		ep->curpacksize = maxsize;
+
+	if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL)
+		packs_per_ms = 8 >> ep->datainterval;
+	else
+		packs_per_ms = 1;
+
+	if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+		urb_packs = max(ep->chip->nrpacks, 1);
+		urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
+	} else {
+		urb_packs = 1;
+	}
+
+	urb_packs *= packs_per_ms;
+
+	if (sync_ep && !snd_usb_endpoint_implict_feedback_sink(ep))
+		urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
+
+	/* decide how many packets to be used */
+	if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+		unsigned int minsize, maxpacks;
+		/* determine how small a packet can be */
+		minsize = (ep->freqn >> (16 - ep->datainterval))
+			  * (frame_bits >> 3);
+		/* with sync from device, assume it can be 12% lower */
+		if (sync_ep)
+			minsize -= minsize >> 3;
+		minsize = max(minsize, 1u);
+		total_packs = (period_bytes + minsize - 1) / minsize;
+		/* we need at least two URBs for queueing */
+		if (total_packs < 2) {
+			total_packs = 2;
+		} else {
+			/* and we don't want too long a queue either */
+			maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
+			total_packs = min(total_packs, maxpacks);
+		}
+	} else {
+		while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+			urb_packs >>= 1;
+		total_packs = MAX_URBS * urb_packs;
+	}
+
+	snd_printk("is_playback? %d impf %d, total_packs %d urb_packs %d\n",
+		is_playback, snd_usb_endpoint_implict_feedback_sink(ep), total_packs, urb_packs);
+
+	ep->nurbs = (total_packs + urb_packs - 1) / urb_packs;
+	if (ep->nurbs > MAX_URBS) {
+		/* too much... */
+		ep->nurbs = MAX_URBS;
+		total_packs = MAX_URBS * urb_packs;
+	} else if (ep->nurbs < 2) {
+		/* too little - we need at least two packets
+		 * to ensure contiguous playback/capture
+		 */
+		ep->nurbs = 2;
+	}
+
+	/* allocate and initialize data urbs */
+	for (i = 0; i < ep->nurbs; i++) {
+		struct snd_urb_ctx *u = &ep->urb[i];
+		u->index = i;
+		u->ep = ep;
+		u->packets = (i + 1) * total_packs / ep->nurbs
+			- i * total_packs / ep->nurbs;
+		u->buffer_size = maxsize * u->packets;
+		snd_printk(" ep %p:: buffer_size %d maxsize %d packets %d\n", ep, u->buffer_size, maxsize, u->packets);
+		if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
+			u->packets++; /* for transfer delimiter */
+		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+		if (!u->urb)
+			goto out_of_memory;
+
+		u->urb->transfer_buffer =
+			usb_alloc_coherent(ep->chip->dev, u->buffer_size,
+					   GFP_KERNEL, &u->urb->transfer_dma);
+		if (!u->urb->transfer_buffer)
+			goto out_of_memory;
+		u->urb->pipe = ep->pipe;
+		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+		u->urb->interval = 1 << ep->datainterval;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+	}
+
+	return 0;
+
+out_of_memory:
+	release_urbs(ep, 0);
+	return -ENOMEM;
+}
+
+static int sync_ep_set_params(struct snd_usb_endpoint *ep,
+			      struct snd_pcm_hw_params *hw_params,
+			      struct audioformat *fmt)
+{
+	int i;
+
+	ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
+					 GFP_KERNEL, &ep->sync_dma);
+	if (!ep->syncbuf)
+		goto out_of_memory;
+
+	for (i = 0; i < SYNC_URBS; i++) {
+		struct snd_urb_ctx *u = &ep->urb[i];
+		u->index = i;
+		u->ep = ep;
+		u->packets = 1;
+		u->urb = usb_alloc_urb(1, GFP_KERNEL);
+		if (!u->urb)
+			goto out_of_memory;
+		u->urb->transfer_buffer = ep->syncbuf + i * 4;
+		u->urb->transfer_dma = ep->sync_dma + i * 4;
+		u->urb->transfer_buffer_length = 4;
+		u->urb->pipe = ep->pipe;
+		u->urb->transfer_flags = URB_ISO_ASAP |
+					 URB_NO_TRANSFER_DMA_MAP;
+		u->urb->number_of_packets = 1;
+		u->urb->interval = 1 << ep->syncinterval;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+	}
+
+	ep->nurbs = SYNC_URBS;
+
+	return 0;
+
+out_of_memory:
+	release_urbs(ep, 0);
+	return -ENOMEM;
+}
+
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+				struct snd_pcm_hw_params *hw_params,
+				struct audioformat *fmt,
+				struct snd_usb_endpoint *sync_ep)
+{
+	int err;
+
+	if (ep->use_count > 0) {
+		snd_printk(KERN_WARNING "Unable to change format on ep #%x: already in use\n",
+			   ep->ep_num);
+		return -EBUSY;
+	}
+
+	ep->datainterval = fmt->datainterval;
+	ep->maxpacksize = fmt->maxpacksize;
+	ep->fill_max = fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX;
+
+	if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
+		ep->freqn = get_usb_full_speed_rate(params_rate(hw_params));
+	else
+		ep->freqn = get_usb_high_speed_rate(params_rate(hw_params));
+
+	/* calculate the frequency in 16.16 format */
+	ep->freqm = ep->freqn;
+	ep->freqshift = INT_MIN;
+
+	ep->phase = 0;
+
+	switch (ep->type) {
+	case  SND_USB_ENDPOINT_TYPE_DATA:
+		err = data_ep_set_params(ep, hw_params, fmt, sync_ep);
+		break;
+	case  SND_USB_ENDPOINT_TYPE_SYNC:
+		err = sync_ep_set_params(ep, hw_params, fmt);
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	snd_printk("Setting params for ep #%x (type %d, %d urbs) -> %d\n",
+		   ep->ep_num, ep->type, ep->nurbs, err);
+
+	return err;
+}
+
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
+{
+	int err;
+	unsigned int i;
+	unsigned long flags;
+
+	if (ep->chip->shutdown)
+		return -EBADFD;
+
+	/* already running? */
+	if (++ep->use_count != 1)
+		return 0;
+
+	if (snd_BUG_ON(!ep->activated))
+		return -EINVAL;
+
+	ep->phase = 0;
+	ep->active_mask = 0;
+	ep->unlink_mask = 0;
+	spin_lock(&ep->lock);
+	INIT_LIST_HEAD(&ep->ready_playback_urbs);
+	ep->next_packet_read_pos = 0;
+	ep->next_packet_write_pos = 0;
+	spin_unlock(&ep->lock);
+	tasklet_kill(&ep->outbound_tasklet);
+
+	/*
+	 * If this endpoint has a data endpoint as implicit feedback source,
+	 * don't start the urbs here. Instead, mark them all as available,
+	 * wait for the record urbs to arrive and queue from that context.
+	 */
+	printk(KERN_ERR "%s(): pipein? %d, impl? %d\n", __func__, usb_pipein(ep->pipe), snd_usb_endpoint_implict_feedback_sink(ep));
+
+	if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+		spin_lock_irqsave(&ep->lock, flags);
+		for (i = 0; i < ep->nurbs; i++) {
+			struct snd_urb_ctx *ctx = ep->urb + i;
+			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+		}
+		spin_unlock_irqrestore(&ep->lock, flags);
+
+		return 0;
+	}
+
+	for (i = 0; i < ep->nurbs; i++) {
+		struct urb *urb = ep->urb[i].urb;
+
+		if (snd_BUG_ON(!urb))
+			return -EINVAL;
+
+		if (usb_pipeout(ep->pipe)) {
+			prepare_outbound_urb_sizes(ep, urb->context);
+			prepare_outbound_urb(ep, urb->context);
+		} else {
+			prepare_inbound_urb(ep, urb->context);
+		}
+
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		snd_printk(KERN_ERR "Submmiting urb %d/%d (in? %d buf %p len %d) ret (%d)\n",
+				i, ep->nurbs, usb_pipein(ep->pipe),
+				ep->urb[i].urb->transfer_buffer,
+				ep->urb[i].urb->transfer_buffer_length,
+				err);
+		if (err < 0) {
+			snd_printk(KERN_ERR "cannot submit urb %d, error %d: %s\n",
+				   i, err, usb_error_string(err));
+			goto __error;
+		}
+		set_bit(i, &ep->active_mask);
+	}
+
+	return 0;
+
+__error:
+	ep->use_count--;
+	deactivate_urbs(ep, 0, 0);
+	return -EPIPE;
+}
+
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, int force, int can_sleep)
+{
+	if (!ep)
+		return;
+
+	if (snd_BUG_ON(ep->use_count == 0))
+		return;
+
+	if (snd_BUG_ON(!ep->activated))
+		return;
+
+	if (--ep->use_count == 0) {
+		tasklet_kill(&ep->outbound_tasklet);
+		spin_lock(&ep->lock);
+		INIT_LIST_HEAD(&ep->ready_playback_urbs);
+		ep->next_packet_read_pos = 0;
+		ep->next_packet_write_pos = 0;
+		spin_unlock(&ep->lock);
+		ep->data_subs = NULL;
+		ep->sync_master = NULL;
+		ep->sync_slave = NULL;
+		ep->retire_data_urb = NULL;
+		ep->prepare_data_urb = NULL;
+		ep->sync_master = NULL;
+		deactivate_urbs(ep, force, can_sleep);
+	}
+}
+
+int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep)
+{
+	if (ep && ep->use_count == 0 && !ep->chip->shutdown && !ep->activated) {
+		int ret;
+
+		snd_printk("%s() calling usb_set_interface(%d,%d)\n", __func__, ep->iface, ep->alt_idx);
+		ret = usb_set_interface(ep->chip->dev, ep->iface, ep->alt_idx);
+		if (ret < 0) {
+			snd_printk(KERN_ERR "%s() usb_set_interface() failed, ret = %d\n",
+						__func__, ret);
+			return ret;
+		}
+
+		ep->activated = 1;
+	}
+
+	return 0;
+}
+
+int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
+{
+	if (ep && ep->use_count == 0 && !ep->chip->shutdown && ep->activated) {
+		int ret;
+
+		snd_printk("%s() calling usb_set_interface(%d,0)\n", __func__, ep->iface);
+		ret = usb_set_interface(ep->chip->dev, ep->iface, 0);
+		if (ret < 0) {
+			snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+						__func__, ret);
+			return ret;
+		}
+
+		ep->activated = 0;
+	}
+
+	return 0;
+}
+
+void snd_usb_endpoint_free(struct list_head *head)
+{
+	struct snd_usb_endpoint *ep;
+
+	ep = list_entry(head, struct snd_usb_endpoint, list);
+	deactivate_urbs(ep, 1, 1);
+	release_urbs(ep, 1);
+}
+
+/*
+ * process after playback sync complete
+ *
+ * Full speed devices report feedback values in 10.14 format as samples per
+ * frame, high speed devices in 16.16 format as samples per microframe.
+ * Because the Audio Class 1 spec was written before USB 2.0, many high speed
+ * devices use a wrong interpretation, some others use an entirely different
+ * format.  Therefore, we cannot predict what format any particular device uses
+ * and must detect it automatically.
+ */
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+			     struct snd_usb_endpoint *sender,
+			     const struct urb *urb)
+{
+	int shift;
+	unsigned int f;
+	unsigned long flags;
+
+	snd_BUG_ON(ep == sender);
+
+	if (snd_usb_endpoint_implict_feedback_sink(ep) && ep->use_count > 0) {
+		/* implicit feedback case */
+		int i, bytes = 0;
+		struct snd_urb_ctx *in_ctx;
+		struct snd_usb_packet_info *out_packet;
+
+		in_ctx = urb->context;
+
+		/* Count overall packet size */
+		for (i = 0; i < in_ctx->packets; i++)
+			if (urb->iso_frame_desc[i].status == 0)
+				bytes += 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;
+
+		spin_lock_irqsave(&ep->lock, flags);
+		out_packet = ep->next_packet + ep->next_packet_write_pos;
+
+		/*
+		 * Iterate through the inbound packet and prepare the lengths
+		 * for the output packet. The OUT packet we are about to send
+		 * will have the same amount of payload than the IN packet we
+		 * just received.
+		 */
+
+		out_packet->packets = in_ctx->packets;
+		for (i = 0; i < in_ctx->packets; i++) {
+			if (urb->iso_frame_desc[i].status == 0)
+				out_packet->packet_size[i] =
+					urb->iso_frame_desc[i].actual_length / ep->stride;
+			else
+				out_packet->packet_size[i] = 0;
+		}
+
+		ep->next_packet_write_pos++;
+		ep->next_packet_write_pos %= MAX_URBS;
+		tasklet_schedule(&ep->outbound_tasklet);
+		spin_unlock_irqrestore(&ep->lock, flags);
+
+		return;
+	}
+
+	if (urb->iso_frame_desc[0].status != 0 ||
+	    urb->iso_frame_desc[0].actual_length < 3)
+		return;
+
+	f = le32_to_cpup(urb->transfer_buffer);
+	if (urb->iso_frame_desc[0].actual_length == 3)
+		f &= 0x00ffffff;
+	else
+		f &= 0x0fffffff;
+
+	if (f == 0)
+		return;
+
+	if (unlikely(ep->freqshift == INT_MIN)) {
+		/*
+		 * The first time we see a feedback value, determine its format
+		 * by shifting it left or right until it matches the nominal
+		 * frequency value.  This assumes that the feedback does not
+		 * differ from the nominal value more than +50% or -25%.
+		 */
+		shift = 0;
+		while (f < ep->freqn - ep->freqn / 4) {
+			f <<= 1;
+			shift++;
+		}
+		while (f > ep->freqn + ep->freqn / 2) {
+			f >>= 1;
+			shift--;
+		}
+		ep->freqshift = shift;
+	}
+	else if (ep->freqshift >= 0)
+		f <<= ep->freqshift;
+	else
+		f >>= -ep->freqshift;
+
+	if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) {
+		/*
+		 * If the frequency looks valid, set it.
+		 * This value is referred to in prepare_playback_urb().
+		 */
+		spin_lock_irqsave(&ep->lock, flags);
+		ep->freqm = f;
+		spin_unlock_irqrestore(&ep->lock, flags);
+	} else {
+		/*
+		 * Out of range; maybe the shift value is wrong.
+		 * Reset it so that we autodetect again the next time.
+		 */
+		ep->freqshift = INT_MIN;
+	}
+}
+
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 88eb63a..84aaec7 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -18,4 +18,29 @@ 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);
 
+
+#define SND_USB_ENDPOINT_TYPE_DATA     0
+#define SND_USB_ENDPOINT_TYPE_SYNC     1
+
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+					      struct usb_host_interface *alts,
+					      int ep_num, int direction, int type);
+
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+				struct snd_pcm_hw_params *hw_params,
+				struct audioformat *fmt,
+				struct snd_usb_endpoint *sync_ep);
+
+int  snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, int force, int can_sleep);
+int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
+int  snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_free(struct list_head *head);
+
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep);
+
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+			     struct snd_usb_endpoint *sender,
+			     const struct urb *urb);
+
 #endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index a16c21d..b8233eb 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -47,6 +47,7 @@ struct snd_usb_audio {
 	int num_suspended_intf;
 
 	struct list_head pcm_list;	/* list of pcm streams */
+	struct list_head ep_list;	/* list of audio-related endpoints */
 	int pcm_devs;
 
 	struct list_head midi_list;	/* list of midi interfaces */
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2011-12-22 19:04 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-20 23:34 [PATCH 0/5] snd-usb endpoint rework, take 5 Daniel Mack
2011-12-20 23:34 ` [PATCH 1/5] ALSA: snd-usb: add snd_usb_audio-wide mutex Daniel Mack
2011-12-20 23:34 ` [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model Daniel Mack
2011-12-21 16:16   ` Aurélien Leblond
2011-12-21 16:34     ` Daniel Mack
2011-12-21 17:11       ` Aurélien Leblond
2011-12-21 17:18         ` Daniel Mack
2011-12-22 17:55           ` Aurélien Leblond
2011-12-22 19:04             ` Daniel Mack
2011-12-20 23:34 ` [PATCH 3/5] ALSA: snd-usb: switch over to new endpoint streaming logic Daniel Mack
2011-12-20 23:34 ` [PATCH 4/5] ALSA: snd-usb: remove old " Daniel Mack
2011-12-20 23:34 ` [PATCH 5/5] ALSA: snd-usb: add support for implicit feedback Daniel Mack
  -- strict thread matches above, loose matches on Subject: below --
2011-12-20  9:48 [PATCH 0/5] snd-usb endpoint rework, take 4 Daniel Mack
2011-12-20  9:48 ` [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model Daniel Mack
2011-12-20 15:08   ` Takashi Iwai
2011-12-20 15:32     ` Clemens Ladisch
2011-12-20 20:16       ` Daniel Mack
2011-12-20 15:42   ` Takashi Iwai
2011-12-20 20:08     ` Daniel Mack
2011-12-18 23:02 [PATCH 0/5] snd-usb endpoint logic rework, version 3 Daniel Mack
2011-12-18 23:02 ` [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model Daniel Mack
2011-11-18 20:05 [PATCH 0/5] RFC v2 for snd-usb endpoint logic rework Daniel Mack
2011-11-18 20:05 ` [PATCH 2/5] ALSA: snd-usb: implement new endpoint streaming model Daniel Mack

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.