Commit 5c7264cf authored by Takashi Iwai's avatar Takashi Iwai

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: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 9f600630
...@@ -1072,10 +1072,7 @@ void snd_pcm_set_sync(struct snd_pcm_substream *substream); ...@@ -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 ...@@ -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, ...@@ -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, ...@@ -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,9 +2133,10 @@ static int pcm_accessible_state(struct snd_pcm_runtime *runtime) ...@@ -2121,9 +2133,10 @@ 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 */
void *data, bool interleaved, snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
snd_pcm_uframes_t size) void *data, bool interleaved,
snd_pcm_uframes_t size)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t xfer = 0;
...@@ -2132,12 +2145,14 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, ...@@ -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, ...@@ -2150,12 +2165,16 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
} }
if (!data) { if (!data) {
transfer = fill_silence; if (is_playback)
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,129 +2187,8 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, ...@@ -2168,129 +2187,8 @@ 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;
runtime->twake = runtime->control->avail_min ? : 1; if (!is_playback &&
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
snd_pcm_update_hw_ptr(substream);
avail = snd_pcm_playback_avail(runtime);
while (size > 0) {
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
snd_pcm_uframes_t cont;
if (!avail) {
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;
}
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 = writer(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;
if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
err = snd_pcm_start(substream);
if (err < 0)
goto _end_unlock;
}
}
_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_write);
/* 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) { size >= runtime->start_threshold) {
err = snd_pcm_start(substream); err = snd_pcm_start(substream);
if (err < 0) if (err < 0)
...@@ -2300,13 +2198,16 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, ...@@ -2300,13 +2198,16 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
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);
avail = snd_pcm_capture_avail(runtime); if (is_playback)
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 (runtime->status->state == if (!is_playback &&
SNDRV_PCM_STATE_DRAINING) { runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
goto _end_unlock; goto _end_unlock;
} }
...@@ -2334,7 +2235,7 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, ...@@ -2334,7 +2235,7 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
appl_ptr = runtime->control->appl_ptr; appl_ptr = runtime->control->appl_ptr;
appl_ofs = appl_ptr % runtime->buffer_size; appl_ofs = appl_ptr % runtime->buffer_size;
snd_pcm_stream_unlock_irq(substream); snd_pcm_stream_unlock_irq(substream);
err = reader(substream, appl_ofs, data, offset, frames, err = writer(substream, appl_ofs, data, offset, frames,
transfer); transfer);
snd_pcm_stream_lock_irq(substream); snd_pcm_stream_lock_irq(substream);
if (err < 0) if (err < 0)
...@@ -2353,6 +2254,13 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, ...@@ -2353,6 +2254,13 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
size -= frames; size -= frames;
xfer += frames; xfer += frames;
avail -= frames; avail -= frames;
if (is_playback &&
runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
err = snd_pcm_start(substream);
if (err < 0)
goto _end_unlock;
}
} }
_end_unlock: _end_unlock:
runtime->twake = 0; runtime->twake = 0;
...@@ -2361,7 +2269,7 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, ...@@ -2361,7 +2269,7 @@ snd_pcm_sframes_t __snd_pcm_lib_read(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_read); EXPORT_SYMBOL(__snd_pcm_lib_xfer);
/* /*
* standard channel mapping helpers * standard channel mapping helpers
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment