ALSA: pcm: Unify read/write loop
authorTakashi Iwai <tiwai@suse.de>
Wed, 24 May 2017 20:36:23 +0000 (22:36 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 2 Jun 2017 17:38:22 +0000 (19:38 +0200)
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>
include/sound/pcm.h
sound/core/pcm_lib.c

index 0fac948bb053f4d688eb8f9d4898346977c5701a..db649083c76df79888200a6793328bfdcb6c43e2 100644 (file)
@@ -1072,10 +1072,7 @@ void snd_pcm_set_sync(struct snd_pcm_substream *substream);
 int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
                      unsigned int cmd, void *arg);                      
 void snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
-snd_pcm_sframes_t __snd_pcm_lib_write(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,
+snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
                                     void *buf, bool interleaved,
                                     snd_pcm_uframes_t frames);
 
@@ -1083,28 +1080,28 @@ static inline snd_pcm_sframes_t
 snd_pcm_lib_write(struct snd_pcm_substream *substream,
                  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
 snd_pcm_lib_read(struct snd_pcm_substream *substream,
                 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
 snd_pcm_lib_writev(struct snd_pcm_substream *substream,
                   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
 snd_pcm_lib_readv(struct snd_pcm_substream *substream,
                  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);
index 0c53a34201c13a30f84017c119c2ae2edec9a8a9..af73c629a6b268b2395d283fa64886743f32d949 100644 (file)
@@ -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