[alsa-devel] [PATCH 3/4] ALSA: pcm: Provide read/write helpers for in-kernel buffer transfer
Takashi Iwai
tiwai at suse.de
Tue May 23 08:11:54 CEST 2017
Thanks to the conversion to copy_silence PCM ops, now we can copy
from/to the kernel-space buffer directly via in_kernel parameter
without set_fs() hack in all places. This patch extends the existing
helper functions for buffer copy to handle in-kernel buffer
transfers, too.
Also, the similar API functions for in-kernel buffer transfer are
provided as well.
Signed-off-by: Takashi Iwai <tiwai at suse.de>
---
include/sound/pcm.h | 50 +++++++++++++++++++++++++++----
sound/core/pcm_lib.c | 83 ++++++++++++++++++++++++++++++++++++----------------
2 files changed, 102 insertions(+), 31 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 339ae4f77766..1c9107af7385 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -1086,37 +1086,75 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
void snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
unsigned long data, bool interleaved,
- snd_pcm_uframes_t size);
+ snd_pcm_uframes_t size, bool in_kernel);
snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
unsigned long data, bool interleaved,
- snd_pcm_uframes_t size);
+ snd_pcm_uframes_t size, bool in_kernel);
+/* copy from/to user-space buffer */
static inline snd_pcm_sframes_t
snd_pcm_lib_write(struct snd_pcm_substream *substream,
const void __user *buf, snd_pcm_uframes_t size)
{
- return __snd_pcm_lib_write(substream, (unsigned long)buf, true, size);
+ return __snd_pcm_lib_write(substream, (unsigned long)buf, true, size,
+ false);
}
static inline snd_pcm_sframes_t
snd_pcm_lib_writev(struct snd_pcm_substream *substream,
void __user **bufs, snd_pcm_uframes_t size)
{
- return __snd_pcm_lib_write(substream, (unsigned long)bufs, false, size);
+ return __snd_pcm_lib_write(substream, (unsigned long)bufs, false, size,
+ false);
}
static inline snd_pcm_sframes_t
snd_pcm_lib_read(struct snd_pcm_substream *substream,
void __user *buf, snd_pcm_uframes_t size)
{
- return __snd_pcm_lib_read(substream, (unsigned long)buf, true, size);
+ return __snd_pcm_lib_read(substream, (unsigned long)buf, true, size,
+ false);
}
static inline snd_pcm_sframes_t
snd_pcm_lib_readv(struct snd_pcm_substream *substream,
void __user **bufs, snd_pcm_uframes_t size)
{
- return __snd_pcm_lib_read(substream, (unsigned long)bufs, false, size);
+ return __snd_pcm_lib_read(substream, (unsigned long)bufs, false, size,
+ false);
+}
+
+/* copy from/to kernel-space buffer */
+static inline snd_pcm_sframes_t
+snd_pcm_kernel_write(struct snd_pcm_substream *substream,
+ const void *buf, snd_pcm_uframes_t size)
+{
+ return __snd_pcm_lib_write(substream, (unsigned long)buf, true, size,
+ true);
+}
+
+static inline snd_pcm_sframes_t
+snd_pcm_kernel_writev(struct snd_pcm_substream *substream,
+ void **bufs, snd_pcm_uframes_t size)
+{
+ return __snd_pcm_lib_write(substream, (unsigned long)bufs, false, size,
+ true);
+}
+
+static inline snd_pcm_sframes_t
+snd_pcm_kernel_read(struct snd_pcm_substream *substream,
+ void *buf, snd_pcm_uframes_t size)
+{
+ return __snd_pcm_lib_read(substream, (unsigned long)buf, true, size,
+ true);
+}
+
+static inline snd_pcm_sframes_t
+snd_pcm_kernel_readv(struct snd_pcm_substream *substream,
+ void **bufs, snd_pcm_uframes_t size)
+{
+ return __snd_pcm_lib_read(substream, (unsigned long)bufs, false, size,
+ true);
}
extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 02a154c1908f..dfa6d13ba8bd 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1991,7 +1991,7 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff,
unsigned long data, unsigned int off,
- snd_pcm_uframes_t size);
+ snd_pcm_uframes_t size, bool in_kernel);
/* sanity-check for read/write methods */
static int pcm_sanity_check(struct snd_pcm_substream *substream)
@@ -2010,19 +2010,26 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream)
static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
+ snd_pcm_uframes_t frames, bool in_kernel)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
+
if (substream->ops->copy_silence) {
err = substream->ops->copy_silence(substream, -1, hwoff, buf,
- frames, false);
+ frames, in_kernel);
if (err < 0)
return err;
} else {
- char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
- if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
+ char *hwbuf = runtime->dma_area +
+ frames_to_bytes(runtime, hwoff);
+
+ if (in_kernel)
+ memcpy(hwbuf, (void *)buf,
+ frames_to_bytes(runtime, frames));
+ else if (copy_from_user(hwbuf, buf,
+ frames_to_bytes(runtime, frames)))
return -EFAULT;
}
return 0;
@@ -2031,7 +2038,7 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
+ snd_pcm_uframes_t frames, bool in_kernel)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
@@ -2047,7 +2054,8 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
else
buf = *bufs + samples_to_bytes(runtime, off);
err = substream->ops->copy_silence(substream, c, hwoff,
- buf, frames, false);
+ buf, frames,
+ in_kernel);
if (err < 0)
return err;
}
@@ -2055,12 +2063,21 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
/* default transfer behaviour */
size_t dma_csize = runtime->dma_bytes / channels;
for (c = 0; c < channels; ++c, ++bufs) {
- char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
- if (*bufs == NULL) {
- snd_pcm_format_set_silence(runtime->format, hwbuf, frames);
+ char *hwbuf = runtime->dma_area + (c * dma_csize) +
+ samples_to_bytes(runtime, hwoff);
+
+ if (!*bufs) {
+ snd_pcm_format_set_silence(runtime->format,
+ hwbuf, frames);
} else {
- char __user *buf = *bufs + samples_to_bytes(runtime, off);
- if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames)))
+ char __user *buf = *bufs +
+ samples_to_bytes(runtime, off);
+
+ if (in_kernel)
+ memcpy(hwbuf, (void *)buf,
+ samples_to_bytes(runtime, frames));
+ else if (copy_from_user(hwbuf, buf,
+ samples_to_bytes(runtime, frames)))
return -EFAULT;
}
}
@@ -2070,7 +2087,7 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
unsigned long data, bool interleaved,
- snd_pcm_uframes_t size)
+ snd_pcm_uframes_t size, bool in_kernel)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t xfer = 0;
@@ -2147,7 +2164,8 @@ snd_pcm_sframes_t __snd_pcm_lib_write(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 = transfer(substream, appl_ofs, data, offset, frames);
+ err = transfer(substream, appl_ofs, data, offset, frames,
+ in_kernel);
snd_pcm_stream_lock_irq(substream);
if (err < 0)
goto _end_unlock;
@@ -2192,19 +2210,26 @@ EXPORT_SYMBOL(__snd_pcm_lib_write);
static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
+ snd_pcm_uframes_t frames, bool in_kernel)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
+
if (substream->ops->copy_silence) {
err = substream->ops->copy_silence(substream, -1, hwoff, buf,
- frames, false);
+ frames, in_kernel);
if (err < 0)
return err;
} else {
- char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
- if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
+ char *hwbuf = runtime->dma_area +
+ frames_to_bytes(runtime, hwoff);
+
+ if (in_kernel)
+ memcpy((void *)buf, hwbuf,
+ frames_to_bytes(runtime, frames));
+ else if (copy_to_user(buf, hwbuf,
+ frames_to_bytes(runtime, frames)))
return -EFAULT;
}
return 0;
@@ -2213,7 +2238,7 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
+ snd_pcm_uframes_t frames, bool in_kernel)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
@@ -2229,19 +2254,26 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
continue;
buf = *bufs + samples_to_bytes(runtime, off);
err = substream->ops->copy_silence(substream, c, hwoff,
- buf, frames, false);
+ buf, frames,
+ in_kernel);
if (err < 0)
return err;
}
} else {
snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
+
for (c = 0; c < channels; ++c, ++bufs) {
- if (*bufs == NULL)
+ if (!*bufs)
continue;
- hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
+ hwbuf = runtime->dma_area + (c * dma_csize) +
+ samples_to_bytes(runtime, hwoff);
buf = *bufs + samples_to_bytes(runtime, off);
- if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames)))
+ if (in_kernel)
+ memcpy((void *)buf, hwbuf,
+ samples_to_bytes(runtime, frames));
+ else if (copy_to_user(buf, hwbuf,
+ samples_to_bytes(runtime, frames)))
return -EFAULT;
}
}
@@ -2250,7 +2282,7 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream,
unsigned long data, bool interleaved,
- snd_pcm_uframes_t size)
+ snd_pcm_uframes_t size, bool in_kernel)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t xfer = 0;
@@ -2341,7 +2373,8 @@ 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 = transfer(substream, appl_ofs, data, offset, frames);
+ err = transfer(substream, appl_ofs, data, offset, frames,
+ in_kernel);
snd_pcm_stream_lock_irq(substream);
if (err < 0)
goto _end_unlock;
--
2.13.0
More information about the Alsa-devel
mailing list