ALSA: pcm: Unify read/write loop
Both __snd_pcm_lib_read() and __snd_pcm_write() functions have almost the same code to loop over samples. For simplification, this patch unifies both as the single helper, __snd_pcm_lib_xfer(). Other than that, there should be no functional change by this patch. Reviewed-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
9f60063094
commit
5c7264cfbb
@ -1072,10 +1072,7 @@ void snd_pcm_set_sync(struct snd_pcm_substream *substream);
|
|||||||
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
|
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
|
||||||
unsigned int cmd, void *arg);
|
unsigned int cmd, void *arg);
|
||||||
void snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
|
void snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
|
||||||
snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
|
snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
|
||||||
void *buf, bool interleaved,
|
|
||||||
snd_pcm_uframes_t frames);
|
|
||||||
snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
|
|
||||||
void *buf, bool interleaved,
|
void *buf, bool interleaved,
|
||||||
snd_pcm_uframes_t frames);
|
snd_pcm_uframes_t frames);
|
||||||
|
|
||||||
@ -1083,28 +1080,28 @@ static inline snd_pcm_sframes_t
|
|||||||
snd_pcm_lib_write(struct snd_pcm_substream *substream,
|
snd_pcm_lib_write(struct snd_pcm_substream *substream,
|
||||||
const void __user *buf, snd_pcm_uframes_t frames)
|
const void __user *buf, snd_pcm_uframes_t frames)
|
||||||
{
|
{
|
||||||
return __snd_pcm_lib_write(substream, (void *)buf, true, frames);
|
return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline snd_pcm_sframes_t
|
static inline snd_pcm_sframes_t
|
||||||
snd_pcm_lib_read(struct snd_pcm_substream *substream,
|
snd_pcm_lib_read(struct snd_pcm_substream *substream,
|
||||||
void __user *buf, snd_pcm_uframes_t frames)
|
void __user *buf, snd_pcm_uframes_t frames)
|
||||||
{
|
{
|
||||||
return __snd_pcm_lib_read(substream, (void *)buf, true, frames);
|
return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline snd_pcm_sframes_t
|
static inline snd_pcm_sframes_t
|
||||||
snd_pcm_lib_writev(struct snd_pcm_substream *substream,
|
snd_pcm_lib_writev(struct snd_pcm_substream *substream,
|
||||||
void __user **bufs, snd_pcm_uframes_t frames)
|
void __user **bufs, snd_pcm_uframes_t frames)
|
||||||
{
|
{
|
||||||
return __snd_pcm_lib_write(substream, (void *)bufs, false, frames);
|
return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline snd_pcm_sframes_t
|
static inline snd_pcm_sframes_t
|
||||||
snd_pcm_lib_readv(struct snd_pcm_substream *substream,
|
snd_pcm_lib_readv(struct snd_pcm_substream *substream,
|
||||||
void __user **bufs, snd_pcm_uframes_t frames)
|
void __user **bufs, snd_pcm_uframes_t frames)
|
||||||
{
|
{
|
||||||
return __snd_pcm_lib_read(substream, (void *)bufs, false, frames);
|
return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime);
|
int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime);
|
||||||
|
@ -2008,13 +2008,13 @@ static void *get_dma_ptr(struct snd_pcm_runtime *runtime,
|
|||||||
channel * (runtime->dma_bytes / runtime->channels);
|
channel * (runtime->dma_bytes / runtime->channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* default copy_user ops for write */
|
/* default copy_user ops for write; used for both interleaved and non- modes */
|
||||||
static int default_write_copy_user(struct snd_pcm_substream *substream,
|
static int default_write_copy(struct snd_pcm_substream *substream,
|
||||||
int channel, unsigned long hwoff,
|
int channel, unsigned long hwoff,
|
||||||
void __user *buf, unsigned long bytes)
|
void *buf, unsigned long bytes)
|
||||||
{
|
{
|
||||||
if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff),
|
if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff),
|
||||||
buf, bytes))
|
(void __user *)buf, bytes))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2040,6 +2040,18 @@ static int fill_silence(struct snd_pcm_substream *substream, int channel,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* default copy_user ops for read; used for both interleaved and non- modes */
|
||||||
|
static int default_read_copy(struct snd_pcm_substream *substream,
|
||||||
|
int channel, unsigned long hwoff,
|
||||||
|
void *buf, unsigned long bytes)
|
||||||
|
{
|
||||||
|
if (copy_to_user((void __user *)buf,
|
||||||
|
get_dma_ptr(substream->runtime, channel, hwoff),
|
||||||
|
bytes))
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* call transfer function with the converted pointers and sizes;
|
/* call transfer function with the converted pointers and sizes;
|
||||||
* for interleaved mode, it's one shot for all samples
|
* for interleaved mode, it's one shot for all samples
|
||||||
*/
|
*/
|
||||||
@ -2121,7 +2133,8 @@ static int pcm_accessible_state(struct snd_pcm_runtime *runtime)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
|
/* the common loop for read/write data */
|
||||||
|
snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
|
||||||
void *data, bool interleaved,
|
void *data, bool interleaved,
|
||||||
snd_pcm_uframes_t size)
|
snd_pcm_uframes_t size)
|
||||||
{
|
{
|
||||||
@ -2132,12 +2145,14 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
|
|||||||
pcm_copy_f writer;
|
pcm_copy_f writer;
|
||||||
pcm_transfer_f transfer;
|
pcm_transfer_f transfer;
|
||||||
bool nonblock;
|
bool nonblock;
|
||||||
|
bool is_playback;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = pcm_sanity_check(substream);
|
err = pcm_sanity_check(substream);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||||
if (interleaved) {
|
if (interleaved) {
|
||||||
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
|
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
|
||||||
runtime->channels > 1)
|
runtime->channels > 1)
|
||||||
@ -2150,12 +2165,16 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
if (is_playback)
|
||||||
transfer = fill_silence;
|
transfer = fill_silence;
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
} else {
|
} else {
|
||||||
if (substream->ops->copy_user)
|
if (substream->ops->copy_user)
|
||||||
transfer = (pcm_transfer_f)substream->ops->copy_user;
|
transfer = (pcm_transfer_f)substream->ops->copy_user;
|
||||||
else
|
else
|
||||||
transfer = default_write_copy_user;
|
transfer = is_playback ?
|
||||||
|
default_write_copy : default_read_copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
@ -2168,14 +2187,30 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
|
|||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto _end_unlock;
|
goto _end_unlock;
|
||||||
|
|
||||||
|
if (!is_playback &&
|
||||||
|
runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
|
||||||
|
size >= runtime->start_threshold) {
|
||||||
|
err = snd_pcm_start(substream);
|
||||||
|
if (err < 0)
|
||||||
|
goto _end_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
runtime->twake = runtime->control->avail_min ? : 1;
|
runtime->twake = runtime->control->avail_min ? : 1;
|
||||||
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
|
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
|
||||||
snd_pcm_update_hw_ptr(substream);
|
snd_pcm_update_hw_ptr(substream);
|
||||||
|
if (is_playback)
|
||||||
avail = snd_pcm_playback_avail(runtime);
|
avail = snd_pcm_playback_avail(runtime);
|
||||||
|
else
|
||||||
|
avail = snd_pcm_capture_avail(runtime);
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
|
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
|
||||||
snd_pcm_uframes_t cont;
|
snd_pcm_uframes_t cont;
|
||||||
if (!avail) {
|
if (!avail) {
|
||||||
|
if (!is_playback &&
|
||||||
|
runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
|
||||||
|
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
|
||||||
|
goto _end_unlock;
|
||||||
|
}
|
||||||
if (nonblock) {
|
if (nonblock) {
|
||||||
err = -EAGAIN;
|
err = -EAGAIN;
|
||||||
goto _end_unlock;
|
goto _end_unlock;
|
||||||
@ -2185,6 +2220,8 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
|
|||||||
err = wait_for_avail(substream, &avail);
|
err = wait_for_avail(substream, &avail);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto _end_unlock;
|
goto _end_unlock;
|
||||||
|
if (!avail)
|
||||||
|
continue; /* draining */
|
||||||
}
|
}
|
||||||
frames = size > avail ? avail : size;
|
frames = size > avail ? avail : size;
|
||||||
cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
|
cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
|
||||||
@ -2217,7 +2254,8 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
|
|||||||
size -= frames;
|
size -= frames;
|
||||||
xfer += frames;
|
xfer += frames;
|
||||||
avail -= frames;
|
avail -= frames;
|
||||||
if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
|
if (is_playback &&
|
||||||
|
runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
|
||||||
snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
|
snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
|
||||||
err = snd_pcm_start(substream);
|
err = snd_pcm_start(substream);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
@ -2231,137 +2269,7 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
|
|||||||
snd_pcm_stream_unlock_irq(substream);
|
snd_pcm_stream_unlock_irq(substream);
|
||||||
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
|
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__snd_pcm_lib_write);
|
EXPORT_SYMBOL(__snd_pcm_lib_xfer);
|
||||||
|
|
||||||
/* default copy_user ops for read */
|
|
||||||
static int default_read_copy_user(struct snd_pcm_substream *substream,
|
|
||||||
int channel, unsigned long hwoff,
|
|
||||||
void *buf, unsigned long bytes)
|
|
||||||
{
|
|
||||||
if (copy_to_user((void __user *)buf,
|
|
||||||
get_dma_ptr(substream->runtime, channel, hwoff),
|
|
||||||
bytes))
|
|
||||||
return -EFAULT;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
|
|
||||||
void *data, bool interleaved,
|
|
||||||
snd_pcm_uframes_t size)
|
|
||||||
{
|
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
||||||
snd_pcm_uframes_t xfer = 0;
|
|
||||||
snd_pcm_uframes_t offset = 0;
|
|
||||||
snd_pcm_uframes_t avail;
|
|
||||||
pcm_copy_f reader;
|
|
||||||
pcm_transfer_f transfer;
|
|
||||||
bool nonblock;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = pcm_sanity_check(substream);
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
if (!data)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (interleaved) {
|
|
||||||
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
|
|
||||||
runtime->channels > 1)
|
|
||||||
return -EINVAL;
|
|
||||||
reader = interleaved_copy;
|
|
||||||
} else {
|
|
||||||
if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
|
|
||||||
return -EINVAL;
|
|
||||||
reader = noninterleaved_copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (substream->ops->copy_user)
|
|
||||||
transfer = (pcm_transfer_f)substream->ops->copy_user;
|
|
||||||
else
|
|
||||||
transfer = default_read_copy_user;
|
|
||||||
|
|
||||||
if (size == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
nonblock = !!(substream->f_flags & O_NONBLOCK);
|
|
||||||
|
|
||||||
snd_pcm_stream_lock_irq(substream);
|
|
||||||
err = pcm_accessible_state(runtime);
|
|
||||||
if (err < 0)
|
|
||||||
goto _end_unlock;
|
|
||||||
if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
|
|
||||||
size >= runtime->start_threshold) {
|
|
||||||
err = snd_pcm_start(substream);
|
|
||||||
if (err < 0)
|
|
||||||
goto _end_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime->twake = runtime->control->avail_min ? : 1;
|
|
||||||
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
|
|
||||||
snd_pcm_update_hw_ptr(substream);
|
|
||||||
avail = snd_pcm_capture_avail(runtime);
|
|
||||||
while (size > 0) {
|
|
||||||
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
|
|
||||||
snd_pcm_uframes_t cont;
|
|
||||||
if (!avail) {
|
|
||||||
if (runtime->status->state ==
|
|
||||||
SNDRV_PCM_STATE_DRAINING) {
|
|
||||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
|
|
||||||
goto _end_unlock;
|
|
||||||
}
|
|
||||||
if (nonblock) {
|
|
||||||
err = -EAGAIN;
|
|
||||||
goto _end_unlock;
|
|
||||||
}
|
|
||||||
runtime->twake = min_t(snd_pcm_uframes_t, size,
|
|
||||||
runtime->control->avail_min ? : 1);
|
|
||||||
err = wait_for_avail(substream, &avail);
|
|
||||||
if (err < 0)
|
|
||||||
goto _end_unlock;
|
|
||||||
if (!avail)
|
|
||||||
continue; /* draining */
|
|
||||||
}
|
|
||||||
frames = size > avail ? avail : size;
|
|
||||||
cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
|
|
||||||
if (frames > cont)
|
|
||||||
frames = cont;
|
|
||||||
if (snd_BUG_ON(!frames)) {
|
|
||||||
runtime->twake = 0;
|
|
||||||
snd_pcm_stream_unlock_irq(substream);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
appl_ptr = runtime->control->appl_ptr;
|
|
||||||
appl_ofs = appl_ptr % runtime->buffer_size;
|
|
||||||
snd_pcm_stream_unlock_irq(substream);
|
|
||||||
err = reader(substream, appl_ofs, data, offset, frames,
|
|
||||||
transfer);
|
|
||||||
snd_pcm_stream_lock_irq(substream);
|
|
||||||
if (err < 0)
|
|
||||||
goto _end_unlock;
|
|
||||||
err = pcm_accessible_state(runtime);
|
|
||||||
if (err < 0)
|
|
||||||
goto _end_unlock;
|
|
||||||
appl_ptr += frames;
|
|
||||||
if (appl_ptr >= runtime->boundary)
|
|
||||||
appl_ptr -= runtime->boundary;
|
|
||||||
runtime->control->appl_ptr = appl_ptr;
|
|
||||||
if (substream->ops->ack)
|
|
||||||
substream->ops->ack(substream);
|
|
||||||
|
|
||||||
offset += frames;
|
|
||||||
size -= frames;
|
|
||||||
xfer += frames;
|
|
||||||
avail -= frames;
|
|
||||||
}
|
|
||||||
_end_unlock:
|
|
||||||
runtime->twake = 0;
|
|
||||||
if (xfer > 0 && err >= 0)
|
|
||||||
snd_pcm_update_state(substream, runtime);
|
|
||||||
snd_pcm_stream_unlock_irq(substream);
|
|
||||||
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(__snd_pcm_lib_read);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* standard channel mapping helpers
|
* standard channel mapping helpers
|
||||||
|
Loading…
Reference in New Issue
Block a user