From 015618b902ae8e28705b7af9b4668615fea48ddd Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 29 Aug 2012 13:17:05 +0200 Subject: ALSA: snd-usb: Fix URB cancellation at stream start Commit e9ba389c5 ("ALSA: usb-audio: Fix scheduling-while-atomic bug in PCM capture stream") fixed a scheduling-while-atomic bug that happened when snd_usb_endpoint_start was called from the trigger callback, which is an atmic context. However, the patch breaks the idea of the endpoints reference counting, which is the reason why the driver has been refactored lately. Revert that commit and let snd_usb_endpoint_start() take care of the URB cancellation again. As this function is called from both atomic and non-atomic context, add a flag to denote whether the function may sleep. Signed-off-by: Daniel Mack Cc: stable@kernel.org [3.5+] Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 11 +++++++++-- sound/usb/endpoint.h | 2 +- sound/usb/pcm.c | 13 +++++-------- 3 files changed, 15 insertions(+), 11 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index c41181202688..b896c5559524 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -799,7 +799,9 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, /** * snd_usb_endpoint_start: start an snd_usb_endpoint * - * @ep: the endpoint to start + * @ep: the endpoint to start + * @can_sleep: flag indicating whether the operation is executed in + * non-atomic context * * A call to this function will increment the use count of the endpoint. * In case it is not already running, the URBs for this endpoint will be @@ -809,7 +811,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, * * Returns an error if the URB submission failed, 0 in all other cases. */ -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep) { int err; unsigned int i; @@ -821,6 +823,11 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) if (++ep->use_count != 1) return 0; + /* just to be sure */ + deactivate_urbs(ep, 0, can_sleep); + if (can_sleep) + wait_clear_urbs(ep); + ep->active_mask = 0; ep->unlink_mask = 0; ep->phase = 0; diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index ee2723fb174f..a8e60c1408e5 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -13,7 +13,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep); -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep); 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); diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 62ec808ed792..1546577ae458 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -212,7 +212,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, } } -static int start_endpoints(struct snd_usb_substream *subs) +static int start_endpoints(struct snd_usb_substream *subs, int can_sleep) { int err; @@ -225,7 +225,7 @@ static int start_endpoints(struct snd_usb_substream *subs) snd_printdd(KERN_DEBUG "Starting data EP @%p\n", ep); ep->data_subs = subs; - err = snd_usb_endpoint_start(ep); + err = snd_usb_endpoint_start(ep, can_sleep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags); return err; @@ -239,7 +239,7 @@ static int start_endpoints(struct snd_usb_substream *subs) snd_printdd(KERN_DEBUG "Starting sync EP @%p\n", ep); ep->sync_slave = subs->data_endpoint; - err = snd_usb_endpoint_start(ep); + err = snd_usb_endpoint_start(ep, can_sleep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); return err; @@ -544,13 +544,10 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) subs->last_frame_number = 0; runtime->delay = 0; - /* clear the pending deactivation on the target EPs */ - deactivate_endpoints(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) - return start_endpoints(subs); + return start_endpoints(subs, 1); return 0; } @@ -1175,7 +1172,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream switch (cmd) { case SNDRV_PCM_TRIGGER_START: - err = start_endpoints(subs); + err = start_endpoints(subs, 0); if (err < 0) return err; -- cgit v1.2.3 From 03d2f44e967b3c2cf79a6dfb904c8880616c7f83 Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Thu, 30 Aug 2012 17:11:17 -0400 Subject: ALSA: snd-usb: use list_for_each_safe for endpoint resources snd_usb_endpoint_free() frees the structure that contains its argument. Signed-off-by: Pavel Roskin Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai --- sound/usb/card.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/card.c b/sound/usb/card.c index d5b5c3388e28..4a469f0cb6d4 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -553,7 +553,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, struct snd_usb_audio *chip) { struct snd_card *card; - struct list_head *p; + struct list_head *p, *n; if (chip == (void *)-1L) return; @@ -570,7 +570,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, snd_usb_stream_disconnect(p); } /* release the endpoint resources */ - list_for_each(p, &chip->ep_list) { + list_for_each_safe(p, n, &chip->ep_list) { snd_usb_endpoint_free(p); } /* release the midi resources */ -- cgit v1.2.3 From fbcfbf5f673847657ccd98afb4d8e13af7fdc372 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 30 Aug 2012 18:52:29 +0200 Subject: ALSA: snd-usb: restore delay information Parts of commit 294c4fb8 ("ALSA: usb: refine delay information with USB frame counter") were unfortunately lost during the refactoring of the snd-usb driver in 3.5. This patch adds them back, restoring the correct delay information behaviour. Signed-off-by: Daniel Mack Cc: Pierre-Louis Bossart Cc: stable@kernel.org [3.5+] Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 1546577ae458..5ceb8f1d63fb 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1091,7 +1091,16 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, 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) @@ -1109,12 +1118,26 @@ static void retire_playback_urb(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; int stride = runtime->frame_bits >> 3; int processed = urb->transfer_buffer_length / stride; + int est_delay; spin_lock_irqsave(&subs->lock, flags); - if (processed > runtime->delay) - runtime->delay = 0; + 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 - runtime->delay -= processed; + 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); } -- cgit v1.2.3 From 245baf983cc39524cce39c24d01b276e6e653c9e Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 30 Aug 2012 18:52:30 +0200 Subject: ALSA: snd-usb: fix calls to next_packet_size In order to support devices with implicit feedback streaming models, packet sizes are now stored with each individual urb, and the PCM handling code which fills the buffers purely relies on the size fields now. However, calling snd_usb_audio_next_packet_size() for all possible packets in an URB at once, prior to letting the PCM code do its job does in fact not lead to the same behaviour than what the old code did: The PCM code will break its loop once a period boundary is reached, consequently using up less packets that it really could. As snd_usb_audio_next_packet_size() implements a feedback mechanism to the endpoints phase accumulator, the number of calls to that function matters, and when called too often, the data rate runs out of bounds. Fix this by making the next_packet function public, and call it from the PCM code as before if the packet data sizes are not defined. Signed-off-by: Daniel Mack Cc: stable@kernel.org [v3.5+] Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 13 +------------ sound/usb/endpoint.h | 1 + sound/usb/pcm.c | 7 ++++++- 3 files changed, 8 insertions(+), 13 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index b896c5559524..d6e2bb49c59c 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -141,7 +141,7 @@ int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep) * * For implicit feedback, next_packet_size() is unused. */ -static int next_packet_size(struct snd_usb_endpoint *ep) +int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) { unsigned long flags; int ret; @@ -177,15 +177,6 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep, 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. */ @@ -370,7 +361,6 @@ static void snd_complete_urb(struct urb *urb) goto exit_clear; } - prepare_outbound_urb_sizes(ep, ctx); prepare_outbound_urb(ep, ctx); } else { retire_inbound_urb(ep, ctx); @@ -857,7 +847,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep) 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); diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index a8e60c1408e5..cbbbdf226d66 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -21,6 +21,7 @@ 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); +int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep); void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, struct snd_usb_endpoint *sender, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 5ceb8f1d63fb..e80b6687f43a 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1029,6 +1029,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, struct urb *urb) { struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; + struct snd_usb_endpoint *ep = subs->data_endpoint; struct snd_urb_ctx *ctx = urb->context; unsigned int counts, frames, bytes; int i, stride, period_elapsed = 0; @@ -1040,7 +1041,11 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, urb->number_of_packets = 0; spin_lock_irqsave(&subs->lock, flags); for (i = 0; i < ctx->packets; i++) { - counts = ctx->packet_size[i]; + if (ctx->packet_size[i]) + counts = ctx->packet_size[i]; + else + counts = snd_usb_endpoint_next_packet_size(ep); + /* set up descriptor */ urb->iso_frame_desc[i].offset = frames * stride; urb->iso_frame_desc[i].length = counts * stride; -- cgit v1.2.3 From 2e4a263ca80a203ac6109f5932722a716c265395 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 30 Aug 2012 18:52:31 +0200 Subject: ALSA: snd-usb: fix cross-interface streaming devices Commit 68e67f40b ("ALSA: snd-usb: move calls to usb_set_interface") saved us some unnecessary calls to snd_usb_set_interface() but ignored the fact that there is at least one device out there which operates on two endpoint in different interfaces simultaniously. Take care for this by catching the case where data and sync endpoints are located on different interfaces and calling snd_usb_set_interface() between the start of the two endpoints. Signed-off-by: Daniel Mack Reported-by: Robert M. Albrecht Cc: stable@kernel.org [v3.5+] Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index e80b6687f43a..fd5e982fc98c 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -236,6 +236,21 @@ static int start_endpoints(struct snd_usb_substream *subs, int can_sleep) !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { struct snd_usb_endpoint *ep = subs->sync_endpoint; + if (subs->data_endpoint->iface != subs->sync_endpoint->iface || + subs->data_endpoint->alt_idx != subs->sync_endpoint->alt_idx) { + err = usb_set_interface(subs->dev, + subs->sync_endpoint->iface, + subs->sync_endpoint->alt_idx); + if (err < 0) { + snd_printk(KERN_ERR + "%d:%d:%d: cannot set interface (%d)\n", + subs->dev->devnum, + subs->sync_endpoint->iface, + subs->sync_endpoint->alt_idx, err); + return -EIO; + } + } + snd_printdd(KERN_DEBUG "Starting sync EP @%p\n", ep); ep->sync_slave = subs->data_endpoint; -- cgit v1.2.3 From 1213a205f9ed27d97de3d5bed28fb085ef4853e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Sep 2012 14:58:00 +0200 Subject: ALSA: usb-audio: Fix bogus error messages for delay accounting The recent fix for the missing fine delayed time adjustment gives strange error messages at each start of the playback stream, such as delay: estimated 0, actual 352 delay: estimated 353, actual 705 These come from the sanity check in retire_playback_urb(). Before the stream is activated via start_endpoints(), a few silent packets have been already sent. And at this point the delay account is still in the state as if the new packets are just queued, so the driver gets confused and spews the bogus error messages. For fixing the issue, we just need to check whether the received packet is valid, whether it's zero sized or not. Reported-by: Markus Trippelsdorf Cc: [v3.5+] Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index fd5e982fc98c..f782ce19bf5a 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1140,6 +1140,12 @@ static void retire_playback_urb(struct snd_usb_substream *subs, int processed = urb->transfer_buffer_length / stride; int est_delay; + /* ignore the delay accounting when procssed=0 is given, i.e. + * silent payloads are procssed before handling the actual data + */ + if (!processed) + return; + spin_lock_irqsave(&subs->lock, flags); est_delay = snd_usb_pcm_delay(subs, runtime->rate); /* update delay with exact number of samples played */ -- cgit v1.2.3