summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/usb/card.h5
-rw-r--r--sound/usb/endpoint.c83
2 files changed, 63 insertions, 25 deletions
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 66a249ae138f..cde492e9581a 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -80,8 +80,9 @@ struct snd_usb_endpoint {
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 next_packet_head; /* ring buffer offset to read */
+ unsigned int next_packet_queued; /* queued items in the ring buffer */
+ struct list_head ready_playback_urbs; /* playback URB FIFO for implicit fb */
unsigned int nurbs; /* # urbs */
unsigned long active_mask; /* bitmask of active urbs */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index eee74313603e..b8f06a75fc2a 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -328,6 +328,39 @@ static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
}
}
+/* notify an error as XRUN to the assigned PCM data substream */
+static void notify_xrun(struct snd_usb_endpoint *ep)
+{
+ struct snd_usb_substream *data_subs;
+
+ data_subs = READ_ONCE(ep->data_subs);
+ if (data_subs && data_subs->pcm_substream)
+ snd_pcm_stop_xrun(data_subs->pcm_substream);
+}
+
+static struct snd_usb_packet_info *
+next_packet_fifo_enqueue(struct snd_usb_endpoint *ep)
+{
+ struct snd_usb_packet_info *p;
+
+ p = ep->next_packet + (ep->next_packet_head + ep->next_packet_queued) %
+ ARRAY_SIZE(ep->next_packet);
+ ep->next_packet_queued++;
+ return p;
+}
+
+static struct snd_usb_packet_info *
+next_packet_fifo_dequeue(struct snd_usb_endpoint *ep)
+{
+ struct snd_usb_packet_info *p;
+
+ p = ep->next_packet + ep->next_packet_head;
+ ep->next_packet_head++;
+ ep->next_packet_head %= ARRAY_SIZE(ep->next_packet);
+ ep->next_packet_queued--;
+ return p;
+}
+
/*
* Send output urbs that have been prepared previously. URBs are dequeued
* from ep->ready_playback_urbs and in case there aren't any available
@@ -352,17 +385,14 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
int err, i;
spin_lock_irqsave(&ep->lock, flags);
- if (ep->next_packet_read_pos != ep->next_packet_write_pos) {
- packet = ep->next_packet + ep->next_packet_read_pos;
- ep->next_packet_read_pos++;
- ep->next_packet_read_pos %= MAX_URBS;
-
+ if (ep->next_packet_queued > 0 &&
+ !list_empty(&ep->ready_playback_urbs)) {
/* take URB out of FIFO */
- if (!list_empty(&ep->ready_playback_urbs)) {
- ctx = list_first_entry(&ep->ready_playback_urbs,
+ ctx = list_first_entry(&ep->ready_playback_urbs,
struct snd_urb_ctx, ready_list);
- list_del_init(&ctx->ready_list);
- }
+ list_del_init(&ctx->ready_list);
+
+ packet = next_packet_fifo_dequeue(ep);
}
spin_unlock_irqrestore(&ep->lock, flags);
@@ -377,12 +407,15 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
prepare_outbound_urb(ep, ctx);
err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
- if (err < 0)
+ if (err < 0) {
usb_audio_err(ep->chip,
"Unable to submit urb #%d: %d at %s\n",
ctx->index, err, __func__);
- else
- set_bit(ctx->index, &ep->active_mask);
+ notify_xrun(ep);
+ return;
+ }
+
+ set_bit(ctx->index, &ep->active_mask);
}
}
@@ -393,7 +426,6 @@ static void snd_complete_urb(struct urb *urb)
{
struct snd_urb_ctx *ctx = urb->context;
struct snd_usb_endpoint *ep = ctx->ep;
- struct snd_usb_substream *data_subs;
unsigned long flags;
int err;
@@ -418,10 +450,10 @@ static void snd_complete_urb(struct urb *urb)
if (snd_usb_endpoint_implicit_feedback_sink(ep)) {
spin_lock_irqsave(&ep->lock, flags);
list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+ clear_bit(ctx->index, &ep->active_mask);
spin_unlock_irqrestore(&ep->lock, flags);
queue_pending_output_urbs(ep);
-
- goto exit_clear;
+ return;
}
prepare_outbound_urb(ep, ctx);
@@ -442,9 +474,7 @@ static void snd_complete_urb(struct urb *urb)
return;
usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err);
- data_subs = READ_ONCE(ep->data_subs);
- if (data_subs && data_subs->pcm_substream)
- snd_pcm_stop_xrun(data_subs->pcm_substream);
+ notify_xrun(ep);
exit_clear:
clear_bit(ctx->index, &ep->active_mask);
@@ -789,8 +819,8 @@ static int deactivate_urbs(struct snd_usb_endpoint *ep, bool force)
clear_bit(EP_FLAG_RUNNING, &ep->flags);
INIT_LIST_HEAD(&ep->ready_playback_urbs);
- ep->next_packet_read_pos = 0;
- ep->next_packet_write_pos = 0;
+ ep->next_packet_head = 0;
+ ep->next_packet_queued = 0;
for (i = 0; i < ep->nurbs; i++) {
if (test_bit(i, &ep->active_mask)) {
@@ -1402,7 +1432,16 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
return;
spin_lock_irqsave(&ep->lock, flags);
- out_packet = ep->next_packet + ep->next_packet_write_pos;
+ if (ep->next_packet_queued >= ARRAY_SIZE(ep->next_packet)) {
+ spin_unlock_irqrestore(&ep->lock, flags);
+ usb_audio_err(ep->chip,
+ "next package FIFO overflow EP 0x%x\n",
+ ep->ep_num);
+ notify_xrun(ep);
+ return;
+ }
+
+ out_packet = next_packet_fifo_enqueue(ep);
/*
* Iterate through the inbound packet and prepare the lengths
@@ -1423,8 +1462,6 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
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);