ALSA: usb-audio: Check available frames for the next packet size
This is yet more preparation for the upcoming changes. Extend snd_usb_endpoint_next_packet_size() to check the available frames and return -EAGAIN if the next packet size is equal or exceeds the given size. This will be needed for avoiding XRUN during the low latency operation. As of this patch, avail=0 is passed, i.e. the check is skipped and no behavior change. Link: https://lore.kernel.org/r/20210929080844.11583-7-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
bceee75387
commit
d215f63d49
@ -148,18 +148,23 @@ 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)
|
||||
static int slave_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);
|
||||
ep->phase = (ep->phase & 0xffff)
|
||||
+ (ep->freqm << ep->datainterval);
|
||||
ret = min(ep->phase >> 16, ep->maxframesize);
|
||||
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;
|
||||
@ -169,20 +174,25 @@ static int slave_next_packet_size(struct snd_usb_endpoint *ep)
|
||||
* Return the number of samples to be sent in the next packet
|
||||
* for adaptive and synchronous endpoints
|
||||
*/
|
||||
static int next_packet_size(struct snd_usb_endpoint *ep)
|
||||
static int next_packet_size(struct snd_usb_endpoint *ep, unsigned int avail)
|
||||
{
|
||||
unsigned int sample_accum;
|
||||
int ret;
|
||||
|
||||
if (ep->fill_max)
|
||||
return ep->maxframesize;
|
||||
|
||||
ep->sample_accum += ep->sample_rem;
|
||||
if (ep->sample_accum >= ep->pps) {
|
||||
ep->sample_accum -= ep->pps;
|
||||
sample_accum += ep->sample_rem;
|
||||
if (sample_accum >= ep->pps) {
|
||||
sample_accum -= ep->pps;
|
||||
ret = ep->packsize[1];
|
||||
} else {
|
||||
ret = ep->packsize[0];
|
||||
}
|
||||
if (avail && ret >= avail)
|
||||
ret = -EAGAIN;
|
||||
else
|
||||
ep->sample_accum = sample_accum;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -190,16 +200,27 @@ static int next_packet_size(struct snd_usb_endpoint *ep)
|
||||
/*
|
||||
* snd_usb_endpoint_next_packet_size: Return the number of samples to be sent
|
||||
* in the next packet
|
||||
*
|
||||
* If the size is equal or exceeds @avail, don't proceed but return -EAGAIN
|
||||
* Exception: @avail = 0 for skipping the check.
|
||||
*/
|
||||
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
|
||||
struct snd_urb_ctx *ctx, int idx)
|
||||
struct snd_urb_ctx *ctx, int idx,
|
||||
unsigned int avail)
|
||||
{
|
||||
if (ctx->packet_size[idx])
|
||||
return ctx->packet_size[idx];
|
||||
else if (ep->sync_source)
|
||||
return slave_next_packet_size(ep);
|
||||
unsigned int packet;
|
||||
|
||||
packet = ctx->packet_size[idx];
|
||||
if (packet) {
|
||||
if (avail && packet >= avail)
|
||||
return -EAGAIN;
|
||||
return packet;
|
||||
}
|
||||
|
||||
if (ep->sync_source)
|
||||
return slave_next_packet_size(ep, avail);
|
||||
else
|
||||
return next_packet_size(ep);
|
||||
return next_packet_size(ep, avail);
|
||||
}
|
||||
|
||||
static void call_retire_callback(struct snd_usb_endpoint *ep,
|
||||
@ -263,7 +284,7 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
|
||||
unsigned int length;
|
||||
int counts;
|
||||
|
||||
counts = snd_usb_endpoint_next_packet_size(ep, ctx, i);
|
||||
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;
|
||||
|
@ -46,6 +46,7 @@ void snd_usb_endpoint_free_all(struct snd_usb_audio *chip);
|
||||
|
||||
int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
|
||||
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
|
||||
struct snd_urb_ctx *ctx, int idx);
|
||||
struct snd_urb_ctx *ctx, int idx,
|
||||
unsigned int avail);
|
||||
|
||||
#endif /* __USBAUDIO_ENDPOINT_H */
|
||||
|
@ -1365,7 +1365,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
|
||||
spin_lock_irqsave(&subs->lock, flags);
|
||||
subs->frame_limit += ep->max_urb_frames;
|
||||
for (i = 0; i < ctx->packets; i++) {
|
||||
counts = snd_usb_endpoint_next_packet_size(ep, ctx, i);
|
||||
counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0);
|
||||
/* set up descriptor */
|
||||
urb->iso_frame_desc[i].offset = frames * stride;
|
||||
urb->iso_frame_desc[i].length = counts * stride;
|
||||
|
Loading…
Reference in New Issue
Block a user