summaryrefslogtreecommitdiff
path: root/sound/core/pcm_lib.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2017-05-24 22:36:23 +0200
committerTakashi Iwai <tiwai@suse.de>2017-06-02 19:38:22 +0200
commit5c7264cfbb209efea04bbbd69b8b4f5f2fc5f86d (patch)
tree851ec067b37a9fd5bfdae9e8211f3cabf25a53d2 /sound/core/pcm_lib.c
parent9f60063094ba72e2767be18289baf5151f1f1c2f (diff)
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>
Diffstat (limited to 'sound/core/pcm_lib.c')
-rw-r--r--sound/core/pcm_lib.c184
1 files changed, 46 insertions, 138 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 0c53a34201c1..af73c629a6b2 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -2008,13 +2008,13 @@ static void *get_dma_ptr(struct snd_pcm_runtime *runtime,
channel * (runtime->dma_bytes / runtime->channels);
}
-/* default copy_user ops for write */
-static int default_write_copy_user(struct snd_pcm_substream *substream,
- int channel, unsigned long hwoff,
- void __user *buf, unsigned long bytes)
+/* default copy_user ops for write; used for both interleaved and non- modes */
+static int default_write_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ void *buf, unsigned long bytes)
{
if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff),
- buf, bytes))
+ (void __user *)buf, bytes))
return -EFAULT;
return 0;
}
@@ -2040,6 +2040,18 @@ static int fill_silence(struct snd_pcm_substream *substream, int channel,
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;
* for interleaved mode, it's one shot for all samples
*/
@@ -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,
- void *data, bool interleaved,
- snd_pcm_uframes_t size)
+/* the common loop for read/write data */
+snd_pcm_sframes_t __snd_pcm_lib_xfer(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;
@@ -2132,12 +2145,14 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
pcm_copy_f writer;
pcm_transfer_f transfer;
bool nonblock;
+ bool is_playback;
int err;
err = pcm_sanity_check(substream);
if (err < 0)
return err;
+ is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
if (interleaved) {
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
runtime->channels > 1)
@@ -2150,12 +2165,16 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
}
if (!data) {
- transfer = fill_silence;
+ if (is_playback)
+ transfer = fill_silence;
+ else
+ return -EINVAL;
} else {
if (substream->ops->copy_user)
transfer = (pcm_transfer_f)substream->ops->copy_user;
else
- transfer = default_write_copy_user;
+ transfer = is_playback ?
+ default_write_copy : default_read_copy;
}
if (size == 0)
@@ -2168,129 +2187,8 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *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_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 &&
+ if (!is_playback &&
+ runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
size >= runtime->start_threshold) {
err = snd_pcm_start(substream);
if (err < 0)
@@ -2300,13 +2198,16 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
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);
+ if (is_playback)
+ avail = snd_pcm_playback_avail(runtime);
+ else
+ 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) {
+ if (!is_playback &&
+ runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
goto _end_unlock;
}
@@ -2334,7 +2235,7 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
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,
+ err = writer(substream, appl_ofs, data, offset, frames,
transfer);
snd_pcm_stream_lock_irq(substream);
if (err < 0)
@@ -2353,6 +2254,13 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
size -= frames;
xfer += 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:
runtime->twake = 0;
@@ -2361,7 +2269,7 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
snd_pcm_stream_unlock_irq(substream);
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