[alsa-devel] [PATCH RFC 04/11] ALSA: pcm: add alternative calls

Takashi Sakamoto o-takashi at sakamocchi.jp
Wed May 24 02:52:48 CEST 2017


When investigating current implementation for data copy between
kernel/user spaces, helper functions includes much conditional processing.
This brings lack of lucidity. Furthermore, this is not good in a point
of hit ratio of i-cache.

This commit adds some helper functions for each cases to transfer PCM
frames; to read and write, for interleaved and non-interleaved frame
alignments. These helper functions also performs to transfer silent PCM
frames when source buffer is NULL.

Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
 sound/core/pcm_lib.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 121 insertions(+), 8 deletions(-)

diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index f06d9f4f9574..38baa91fd0f6 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -42,6 +42,100 @@
 #define trace_hw_ptr_error(substream, reason)
 #endif
 
+static int writei_from_space1(struct snd_pcm_substream *substream,
+			      unsigned int hwoff, unsigned long data,
+			      unsigned int off, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	char __user *buf = (char __user *)data;
+	char *dst = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+
+	if (buf == NULL) {
+		snd_pcm_format_set_silence(runtime->format, dst,
+					   count * runtime->channels);
+	} else {
+		if (copy_from_user(dst, buf, frames_to_bytes(runtime, count)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int writen_from_space1(struct snd_pcm_substream *substream,
+			      unsigned int hwoff, unsigned long data,
+			      unsigned int off, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	char __user **bufs = (char __user **)data;
+	char __user *buf;
+	char *dst;
+	int channels = runtime->channels;
+	snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
+	int c;
+
+	for (c = 0; c < channels; ++c) {
+		dst = runtime->dma_area + (c * dma_csize) +
+					samples_to_bytes(runtime, hwoff);
+
+		if (bufs == NULL || bufs[c] == NULL) {
+			snd_pcm_format_set_silence(runtime->format, dst, count);
+			continue;
+		}
+		buf = bufs[c] + samples_to_bytes(runtime, off);
+
+		dst = runtime->dma_area + (c * dma_csize) +
+					samples_to_bytes(runtime, hwoff);
+		if (copy_from_user(dst, buf, samples_to_bytes(runtime, count)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int readi_to_space1(struct snd_pcm_substream *substream,
+			   unsigned int hwoff, unsigned long data,
+			   unsigned int off, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	char __user *buf = (char __user *)data;
+	char *src = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+
+	if (buf == NULL)
+		return -EINVAL;
+	buf += frames_to_bytes(runtime, off);
+
+	if (copy_to_user(buf, src, frames_to_bytes(runtime, count)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int readn_to_space1(struct snd_pcm_substream *substream,
+			   unsigned int hwoff, unsigned long data,
+			   unsigned int off, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	char __user **bufs = (char __user **)data;
+	char __user *buf;
+	char *src;
+	unsigned int channels = runtime->channels;
+	snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
+	int c;
+
+	for (c = 0; c < channels; ++c) {
+		if (bufs == NULL || bufs[c] == NULL)
+			continue;
+		buf = bufs[c] + samples_to_bytes(runtime, off);
+
+		src = runtime->dma_area + (c * dma_csize) +
+					samples_to_bytes(runtime, hwoff);
+		if (copy_to_user(buf, src, samples_to_bytes(runtime, count)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
 static int writei_silence(struct snd_pcm_substream *substream,
 			  unsigned int hwoff, unsigned long data,
 			  unsigned int off, snd_pcm_uframes_t count)
@@ -156,10 +250,17 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream,
 		return;
 
 	if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
-	    runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED)
-		copy_frames = writei_silence;
-	else
-		copy_frames = writen_silence;
+	    runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
+		if (substream->ops->silence)
+			copy_frames = writei_silence;
+		else
+			copy_frames = writei_from_space1;
+	} else {
+		if (substream->ops->silence)
+			copy_frames = writen_silence;
+		else
+			copy_frames = writen_from_space1;
+	}
 
 	ofs = runtime->silence_start % runtime->buffer_size;
 	while (frames > 0) {
@@ -2178,7 +2279,10 @@ snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream,
 	    runtime->channels > 1)
 		return -EINVAL;
 
-	copy_frames = snd_pcm_lib_write_transfer;
+	if (substream->ops->copy)
+		copy_frames = snd_pcm_lib_write_transfer;
+	else
+		copy_frames = writei_from_space1;
 
 	return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock,
 				  copy_frames);
@@ -2243,7 +2347,10 @@ snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
 	if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
 		return -EINVAL;
 
-	copy_frames = snd_pcm_lib_writev_transfer;
+	if (substream->ops->copy)
+		copy_frames = snd_pcm_lib_writev_transfer;
+	else
+		copy_frames = writen_from_space1;
 
 	return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames,
 				  nonblock, copy_frames);
@@ -2395,7 +2502,10 @@ snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream,
 	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
 		return -EINVAL;
 
-	copy_frames = snd_pcm_lib_read_transfer;
+	if (substream->ops->copy)
+		copy_frames = snd_pcm_lib_read_transfer;
+	else
+		copy_frames = readi_to_space1;
 
 	return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock,
 				 copy_frames);
@@ -2458,7 +2568,10 @@ snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
 	if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
 		return -EINVAL;
 
-	copy_frames = snd_pcm_lib_readv_transfer;
+	if (substream->ops->copy)
+		copy_frames = snd_pcm_lib_readv_transfer;
+	else
+		copy_frames = readn_to_space1;
 
 	return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames,
 				 nonblock, copy_frames);
-- 
2.11.0



More information about the Alsa-devel mailing list