summaryrefslogtreecommitdiff
path: root/sound/usb/endpoint.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/endpoint.c')
-rw-r--r--sound/usb/endpoint.c21
1 files changed, 14 insertions, 7 deletions
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 0835943d7b0e..879d0451536e 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -449,7 +449,9 @@ static void push_back_to_ready_list(struct snd_usb_endpoint *ep,
struct snd_urb_ctx *ctx)
{
guard(spinlock_irqsave)(&ep->lock);
- list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+ /* ctx may still be linked: a stale completion racing a stop/restart. */
+ if (list_empty(&ctx->ready_list))
+ list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
}
/*
@@ -492,9 +494,10 @@ int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
/* copy over the length information */
if (implicit_fb) {
- ctx->packets = packet->packets;
+ ctx->packets = min_t(int, packet->packets,
+ ep->max_urb_packs);
memcpy(ctx->packet_size, packet->packet_size,
- packet->packets * sizeof(packet->packet_size[0]));
+ ctx->packets * sizeof(packet->packet_size[0]));
}
/* call the data handler to fill in playback data */
@@ -1036,6 +1039,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep)
*/
static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending)
{
+ struct snd_urb_ctx *ctx, *n;
unsigned int i;
if (!force && atomic_read(&ep->running))
@@ -1045,7 +1049,9 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending)
return 0;
scoped_guard(spinlock_irqsave, &ep->lock) {
- INIT_LIST_HEAD(&ep->ready_playback_urbs);
+ /* Unlink each ctx; INIT_LIST_HEAD() alone would leave them looking linked. */
+ list_for_each_entry_safe(ctx, n, &ep->ready_playback_urbs, ready_list)
+ list_del_init(&ctx->ready_list);
ep->next_packet_head = 0;
ep->next_packet_queued = 0;
}
@@ -1242,15 +1248,16 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods);
}
+ if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
+ urb_packs++; /* for transfer delimiter */
+ ep->max_urb_packs = urb_packs;
+
/* 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 = urb_packs;
-
- if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
- u->packets++; /* for transfer delimiter */
u->buffer_size = maxsize * u->packets;
u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
if (!u->urb)