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.c230
1 files changed, 113 insertions, 117 deletions
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index a29f28eb7d0c..6fbcb117555c 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -160,25 +160,22 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep)
* This won't be used for implicit feedback which takes the packet size
* returned from the sync source
*/
-static int slave_next_packet_size(struct snd_usb_endpoint *ep,
- unsigned int avail)
+static int synced_next_packet_size(struct snd_usb_endpoint *ep,
+ unsigned int avail)
{
- unsigned long flags;
unsigned int phase;
int ret;
if (ep->fill_max)
return ep->maxframesize;
- spin_lock_irqsave(&ep->lock, flags);
+ guard(spinlock_irqsave)(&ep->lock);
phase = (ep->phase & 0xffff) + (ep->freqm << ep->datainterval);
ret = min(phase >> 16, ep->maxframesize);
if (avail && ret >= avail)
ret = -EAGAIN;
else
ep->phase = phase;
- spin_unlock_irqrestore(&ep->lock, flags);
-
return ret;
}
@@ -224,13 +221,14 @@ int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
packet = ctx->packet_size[idx];
if (packet) {
+ packet = min(packet, ep->maxframesize);
if (avail && packet >= avail)
return -EAGAIN;
return packet;
}
if (ep->sync_source)
- return slave_next_packet_size(ep, avail);
+ return synced_next_packet_size(ep, avail);
else
return next_packet_size(ep, avail);
}
@@ -278,8 +276,8 @@ static inline bool has_tx_length_quirk(struct snd_usb_audio *chip)
return chip->quirk_flags & QUIRK_FLAG_TX_LENGTH;
}
-static void prepare_silent_urb(struct snd_usb_endpoint *ep,
- struct snd_urb_ctx *ctx)
+static int prepare_silent_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *ctx)
{
struct urb *urb = ctx->urb;
unsigned int offs = 0;
@@ -292,28 +290,34 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
extra = sizeof(packet_length);
for (i = 0; i < ctx->packets; ++i) {
- unsigned int offset;
- unsigned int length;
- int counts;
-
- counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0);
- length = counts * ep->stride; /* number of silent bytes */
- offset = offs * ep->stride + extra * i;
- urb->iso_frame_desc[i].offset = offset;
+ int length;
+
+ length = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0);
+ if (length < 0)
+ return length;
+ length *= ep->stride; /* number of silent bytes */
+ if (offs + length + extra > ctx->buffer_size)
+ break;
+ urb->iso_frame_desc[i].offset = offs;
urb->iso_frame_desc[i].length = length + extra;
if (extra) {
packet_length = cpu_to_le32(length);
- memcpy(urb->transfer_buffer + offset,
+ memcpy(urb->transfer_buffer + offs,
&packet_length, sizeof(packet_length));
+ offs += extra;
}
- memset(urb->transfer_buffer + offset + extra,
+ memset(urb->transfer_buffer + offs,
ep->silence_value, length);
- offs += counts;
+ offs += length;
}
- urb->number_of_packets = ctx->packets;
- urb->transfer_buffer_length = offs * ep->stride + ctx->packets * extra;
+ if (!offs)
+ return -EPIPE;
+
+ urb->number_of_packets = i;
+ urb->transfer_buffer_length = offs;
ctx->queued = 0;
+ return 0;
}
/*
@@ -335,8 +339,7 @@ static int prepare_outbound_urb(struct snd_usb_endpoint *ep,
if (data_subs && ep->prepare_data_urb)
return ep->prepare_data_urb(data_subs, urb, in_stream_lock);
/* no data provider, so send silence */
- prepare_silent_urb(ep, ctx);
- break;
+ return prepare_silent_urb(ep, ctx);
case SND_USB_ENDPOINT_TYPE_SYNC:
if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
@@ -400,18 +403,21 @@ static int 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)
+static bool notify_xrun(struct snd_usb_endpoint *ep)
{
struct snd_usb_substream *data_subs;
struct snd_pcm_substream *psubs;
data_subs = READ_ONCE(ep->data_subs);
if (!data_subs)
- return;
+ return false;
psubs = data_subs->pcm_substream;
if (psubs && psubs->runtime &&
- psubs->runtime->state == SNDRV_PCM_STATE_RUNNING)
+ psubs->runtime->state == SNDRV_PCM_STATE_RUNNING) {
snd_pcm_stop_xrun(psubs);
+ return true;
+ }
+ return false;
}
static struct snd_usb_packet_info *
@@ -440,11 +446,8 @@ next_packet_fifo_dequeue(struct snd_usb_endpoint *ep)
static void push_back_to_ready_list(struct snd_usb_endpoint *ep,
struct snd_urb_ctx *ctx)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ep->lock, flags);
+ guard(spinlock_irqsave)(&ep->lock);
list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
- spin_unlock_irqrestore(&ep->lock, flags);
}
/*
@@ -466,31 +469,30 @@ int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
bool implicit_fb = snd_usb_endpoint_implicit_feedback_sink(ep);
while (ep_state_running(ep)) {
-
- unsigned long flags;
struct snd_usb_packet_info *packet;
struct snd_urb_ctx *ctx = NULL;
- int err, i;
-
- spin_lock_irqsave(&ep->lock, flags);
- if ((!implicit_fb || ep->next_packet_queued > 0) &&
- !list_empty(&ep->ready_playback_urbs)) {
- /* take URB out of FIFO */
- ctx = list_first_entry(&ep->ready_playback_urbs,
- struct snd_urb_ctx, ready_list);
- list_del_init(&ctx->ready_list);
- if (implicit_fb)
- packet = next_packet_fifo_dequeue(ep);
+ int err;
+
+ scoped_guard(spinlock_irqsave, &ep->lock) {
+ if ((!implicit_fb || ep->next_packet_queued > 0) &&
+ !list_empty(&ep->ready_playback_urbs)) {
+ /* take URB out of FIFO */
+ ctx = list_first_entry(&ep->ready_playback_urbs,
+ struct snd_urb_ctx, ready_list);
+ list_del_init(&ctx->ready_list);
+ if (implicit_fb)
+ packet = next_packet_fifo_dequeue(ep);
+ }
}
- spin_unlock_irqrestore(&ep->lock, flags);
if (ctx == NULL)
break;
/* copy over the length information */
if (implicit_fb) {
- for (i = 0; i < packet->packets; i++)
- ctx->packet_size[i] = packet->packet_size[i];
+ ctx->packets = packet->packets;
+ memcpy(ctx->packet_size, packet->packet_size,
+ packet->packets * sizeof(packet->packet_size[0]));
}
/* call the data handler to fill in playback data */
@@ -596,8 +598,9 @@ static void snd_complete_urb(struct urb *urb)
return;
if (!atomic_read(&ep->chip->shutdown)) {
- usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err);
- notify_xrun(ep);
+ if (notify_xrun(ep))
+ usb_audio_err(ep->chip,
+ "cannot submit urb (err = %d)\n", err);
}
exit_clear:
@@ -619,7 +622,7 @@ iface_ref_find(struct snd_usb_audio *chip, int iface)
if (ip->iface == iface)
return ip;
- ip = kzalloc(sizeof(*ip), GFP_KERNEL);
+ ip = kzalloc_obj(*ip);
if (!ip)
return NULL;
ip->iface = iface;
@@ -637,7 +640,7 @@ clock_ref_find(struct snd_usb_audio *chip, int clock)
if (ref->clock == clock)
return ref;
- ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ ref = kzalloc_obj(*ref);
if (!ref)
return NULL;
ref->clock = clock;
@@ -696,7 +699,7 @@ int snd_usb_add_endpoint(struct snd_usb_audio *chip, int ep_num, int type)
usb_audio_dbg(chip, "Creating new %s endpoint #%x\n",
ep_type_name(type),
ep_num);
- ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ ep = kzalloc_obj(*ep);
if (!ep)
return -ENOMEM;
@@ -768,12 +771,8 @@ bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip,
const struct audioformat *fp,
const struct snd_pcm_hw_params *params)
{
- bool ret;
-
- mutex_lock(&chip->mutex);
- ret = endpoint_compatible(ep, fp, params);
- mutex_unlock(&chip->mutex);
- return ret;
+ guard(mutex)(&chip->mutex);
+ return endpoint_compatible(ep, fp, params);
}
/*
@@ -799,11 +798,11 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep;
int ep_num = is_sync_ep ? fp->sync_ep : fp->endpoint;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
ep = snd_usb_get_endpoint(chip, ep_num);
if (!ep) {
usb_audio_err(chip, "Cannot find EP 0x%x to open\n", ep_num);
- goto unlock;
+ return NULL;
}
if (!ep->opened) {
@@ -820,17 +819,13 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep_num, ep->iface, ep->altsetting, ep->ep_idx);
ep->iface_ref = iface_ref_find(chip, ep->iface);
- if (!ep->iface_ref) {
- ep = NULL;
- goto unlock;
- }
+ if (!ep->iface_ref)
+ return NULL;
if (fp->protocol != UAC_VERSION_1) {
ep->clock_ref = clock_ref_find(chip, fp->clock);
- if (!ep->clock_ref) {
- ep = NULL;
- goto unlock;
- }
+ if (!ep->clock_ref)
+ return NULL;
ep->clock_ref->opened++;
}
@@ -859,16 +854,13 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep->implicit_fb_sync);
} else {
- if (WARN_ON(!ep->iface_ref)) {
- ep = NULL;
- goto unlock;
- }
+ if (WARN_ON(!ep->iface_ref))
+ return NULL;
if (!endpoint_compatible(ep, fp, params)) {
usb_audio_err(chip, "Incompatible EP setup for 0x%x\n",
ep_num);
- ep = NULL;
- goto unlock;
+ return NULL;
}
usb_audio_dbg(chip, "Reopened EP 0x%x (count %d)\n",
@@ -879,9 +871,6 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep->iface_ref->need_setup = true;
ep->opened++;
-
- unlock:
- mutex_unlock(&chip->mutex);
return ep;
}
@@ -926,6 +915,8 @@ static int endpoint_set_interface(struct snd_usb_audio *chip,
{
int altset = set ? ep->altsetting : 0;
int err;
+ int retries = 0;
+ const int max_retries = 5;
if (ep->iface_ref->altset == altset)
return 0;
@@ -935,8 +926,13 @@ static int endpoint_set_interface(struct snd_usb_audio *chip,
usb_audio_dbg(chip, "Setting usb interface %d:%d for EP 0x%x\n",
ep->iface, altset, ep->ep_num);
+retry:
err = usb_set_interface(chip->dev, ep->iface, altset);
if (err < 0) {
+ if (err == -EPROTO && ++retries <= max_retries) {
+ msleep(5 * (1 << (retries - 1)));
+ goto retry;
+ }
usb_audio_err_ratelimited(
chip, "%d:%d: usb_set_interface failed (%d)\n",
ep->iface, altset, err);
@@ -957,7 +953,7 @@ static int endpoint_set_interface(struct snd_usb_audio *chip,
void snd_usb_endpoint_close(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep)
{
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
usb_audio_dbg(chip, "Closing EP 0x%x (count %d)\n",
ep->ep_num, ep->opened);
@@ -978,7 +974,6 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
ep->clock_ref = NULL;
usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num);
}
- mutex_unlock(&chip->mutex);
}
/* Prepare for suspening EP, called from the main suspend handler */
@@ -1040,7 +1035,6 @@ 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)
{
unsigned int i;
- unsigned long flags;
if (!force && atomic_read(&ep->running))
return -EBUSY;
@@ -1048,11 +1042,11 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending)
if (!ep_state_update(ep, EP_STATE_RUNNING, EP_STATE_STOPPING))
return 0;
- spin_lock_irqsave(&ep->lock, flags);
- INIT_LIST_HEAD(&ep->ready_playback_urbs);
- ep->next_packet_head = 0;
- ep->next_packet_queued = 0;
- spin_unlock_irqrestore(&ep->lock, flags);
+ scoped_guard(spinlock_irqsave, &ep->lock) {
+ INIT_LIST_HEAD(&ep->ready_playback_urbs);
+ ep->next_packet_head = 0;
+ ep->next_packet_queued = 0;
+ }
if (keep_pending)
return 0;
@@ -1353,16 +1347,16 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep)
{
const struct audioformat *fmt = ep->cur_audiofmt;
- int err = 0;
+ int err;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
if (!ep->need_setup)
- goto unlock;
+ return 0;
/* release old buffers, if any */
err = release_urbs(ep, false);
if (err < 0)
- goto unlock;
+ return err;
ep->datainterval = fmt->datainterval;
ep->maxpacksize = fmt->maxpacksize;
@@ -1379,6 +1373,11 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
ep->sample_rem = ep->cur_rate % ep->pps;
ep->packsize[0] = ep->cur_rate / ep->pps;
ep->packsize[1] = (ep->cur_rate + (ep->pps - 1)) / ep->pps;
+ if (ep->packsize[1] > ep->maxpacksize) {
+ usb_audio_dbg(chip, "Too small maxpacksize %u for rate %u / pps %u\n",
+ ep->maxpacksize, ep->cur_rate, ep->pps);
+ return -EINVAL;
+ }
/* calculate the frequency in 16.16 format */
ep->freqm = ep->freqn;
@@ -1400,20 +1399,21 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
usb_audio_dbg(chip, "Set up %d URBS, ret=%d\n", ep->nurbs, err);
if (err < 0)
- goto unlock;
+ return err;
/* some unit conversions in runtime */
ep->maxframesize = ep->maxpacksize / ep->cur_frame_bytes;
ep->curframesize = ep->curpacksize / ep->cur_frame_bytes;
+ ep->packsize[0] = min(ep->packsize[0], ep->maxframesize);
+ ep->packsize[1] = min(ep->packsize[1], ep->maxframesize);
+
err = update_clock_ref_rate(chip, ep);
if (err >= 0) {
ep->need_setup = false;
err = 0;
}
- unlock:
- mutex_unlock(&chip->mutex);
return err;
}
@@ -1460,11 +1460,11 @@ int snd_usb_endpoint_prepare(struct snd_usb_audio *chip,
bool iface_first;
int err = 0;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
if (WARN_ON(!ep->iface_ref))
- goto unlock;
+ return 0;
if (!ep->need_prepare)
- goto unlock;
+ return 0;
/* If the interface has been already set up, just set EP parameters */
if (!ep->iface_ref->need_setup) {
@@ -1474,7 +1474,7 @@ int snd_usb_endpoint_prepare(struct snd_usb_audio *chip,
if (ep->cur_audiofmt->protocol == UAC_VERSION_1) {
err = init_sample_rate(chip, ep);
if (err < 0)
- goto unlock;
+ return err;
}
goto done;
}
@@ -1492,38 +1492,35 @@ int snd_usb_endpoint_prepare(struct snd_usb_audio *chip,
if (iface_first) {
err = endpoint_set_interface(chip, ep, true);
if (err < 0)
- goto unlock;
+ return err;
}
- err = snd_usb_init_pitch(chip, ep->cur_audiofmt);
+ err = snd_usb_select_mode_quirk(chip, ep->cur_audiofmt);
if (err < 0)
- goto unlock;
+ return err;
- err = init_sample_rate(chip, ep);
+ err = snd_usb_init_pitch(chip, ep->cur_audiofmt);
if (err < 0)
- goto unlock;
+ return err;
- err = snd_usb_select_mode_quirk(chip, ep->cur_audiofmt);
+ err = init_sample_rate(chip, ep);
if (err < 0)
- goto unlock;
+ return err;
/* for UAC2/3, enable the interface altset here at last */
if (!iface_first) {
err = endpoint_set_interface(chip, ep, true);
if (err < 0)
- goto unlock;
+ return err;
}
ep->iface_ref->need_setup = false;
done:
ep->need_prepare = false;
- err = 1;
-
-unlock:
- mutex_unlock(&chip->mutex);
- return err;
+ return 1;
}
+EXPORT_SYMBOL_GPL(snd_usb_endpoint_prepare);
/* get the current rate set to the given clock by any endpoint */
int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock)
@@ -1533,14 +1530,13 @@ int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock)
if (!clock)
return 0;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
list_for_each_entry(ref, &chip->clock_ref_list, list) {
if (ref->clock == clock) {
rate = ref->rate;
break;
}
}
- mutex_unlock(&chip->mutex);
return rate;
}
@@ -1791,10 +1787,11 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
spin_lock_irqsave(&ep->lock, flags);
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);
+ if (notify_xrun(ep)) {
+ usb_audio_err(ep->chip,
+ "next packet FIFO overflow EP 0x%x\n",
+ ep->ep_num);
+ }
return;
}
@@ -1890,9 +1887,8 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
* If the frequency looks valid, set it.
* This value is referred to in prepare_playback_urb().
*/
- spin_lock_irqsave(&ep->lock, flags);
+ guard(spinlock_irqsave)(&ep->lock);
ep->freqm = f;
- spin_unlock_irqrestore(&ep->lock, flags);
} else {
/*
* Out of range; maybe the shift value is wrong.