From b55f9fdcd3f0b3da7c9d4b6c67d75a1878653221 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 17 May 2017 08:48:20 +0900 Subject: ALSA: pcm: use helper function to refer parameter as read-only ALSA pcm core has hw_param_interval_c() to pick up parameter with const qualifier for safe programming. This commit applies it to the cases. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 5088d4b8db22..af439e5554b6 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1415,7 +1415,8 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, unsigned int l = (unsigned long) rule->private; int width = l & 0xffff; unsigned int msbits = l >> 16; - struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + const struct snd_interval *i = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); if (!snd_interval_single(i)) return 0; -- cgit v1.2.3 From b46fe5d9607c2a14618c746a02892ead0f0f4637 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 17 May 2017 08:48:18 +0900 Subject: ALSA: pcm: constify function local and read-only table In a function snd_pcm_hw_params_choose(), target parameters are arranged into a table. Though each entry of this table is read-only, they don't have const qualifier. This commit adds the qualifier. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index af439e5554b6..ab4b1d1e44ee 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1734,7 +1734,7 @@ EXPORT_SYMBOL(snd_pcm_hw_param_last); int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params) { - static int vars[] = { + static const int vars[] = { SNDRV_PCM_HW_PARAM_ACCESS, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT, @@ -1745,7 +1745,8 @@ int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, SNDRV_PCM_HW_PARAM_TICK_TIME, -1 }; - int err, *v; + const int *v; + int err; for (v = vars; *v != -1; v++) { if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) -- cgit v1.2.3 From 2c4842d3b6b3cf6db0f21e487da7e9bd3aa23090 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 26 May 2017 09:30:46 +0900 Subject: ALSA: pcm: add local header file for snd-pcm module Several files are used to construct PCM core module, a.k.a snd-pcm. Although available APIs are described in 'include/sound/pcm.h', some of them are not exported as symbols in kernel space. Such APIs are just for module local usage. This commit adds module local header file and move some function prototypes into it so that scopes of them are controlled properly and developers get no confusion from unavailable symbols. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 32 ------------------------------- sound/core/pcm.c | 2 ++ sound/core/pcm_lib.c | 2 ++ sound/core/pcm_local.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ sound/core/pcm_misc.c | 3 +++ sound/core/pcm_native.c | 2 ++ 6 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 sound/core/pcm_local.h (limited to 'sound/core/pcm_lib.c') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index c609b891c4c2..79fedf517070 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -969,12 +969,6 @@ static inline unsigned int params_buffer_bytes(const struct snd_pcm_hw_params *p } int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v); -void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); -void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); -void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, - unsigned int k, struct snd_interval *c); -void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, - const struct snd_interval *b, struct snd_interval *c); int snd_interval_list(struct snd_interval *i, unsigned int count, const unsigned int *list, unsigned int mask); int snd_interval_ranges(struct snd_interval *i, unsigned int count, @@ -985,15 +979,9 @@ int snd_interval_ratnum(struct snd_interval *i, void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params); void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var); -int snd_pcm_hw_params_choose(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); -int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream); -int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); - -int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, - u_int32_t mask); int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, u_int64_t mask); int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, @@ -1081,10 +1069,6 @@ void snd_pcm_set_ops(struct snd_pcm * pcm, int direction, 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); -int snd_pcm_update_state(struct snd_pcm_substream *substream, - struct snd_pcm_runtime *runtime); -int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); -void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr); void snd_pcm_period_elapsed(struct snd_pcm_substream *substream); snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, @@ -1096,8 +1080,6 @@ snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, void __user **bufs, snd_pcm_uframes_t frames); -extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; - int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime); unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate); unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit); @@ -1131,20 +1113,6 @@ static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substrea } } -/* - * Timer interface - */ - -#ifdef CONFIG_SND_PCM_TIMER -void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream); -void snd_pcm_timer_init(struct snd_pcm_substream *substream); -void snd_pcm_timer_done(struct snd_pcm_substream *substream); -#else -static inline void -snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {} -static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {} -static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {} -#endif /** * snd_pcm_gettime - Fill the timespec depending on the timestamp mode * @runtime: PCM runtime instance diff --git a/sound/core/pcm.c b/sound/core/pcm.c index d30dba0ee688..4b3290447398 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -31,6 +31,8 @@ #include #include +#include "pcm_local.h" + MODULE_AUTHOR("Jaroslav Kysela , Abramo Bagnara "); MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); MODULE_LICENSE("GPL"); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ab4b1d1e44ee..e50548af4004 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -33,6 +33,8 @@ #include #include +#include "pcm_local.h" + #ifdef CONFIG_SND_PCM_XRUN_DEBUG #define CREATE_TRACE_POINTS #include "pcm_trace.h" diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h new file mode 100644 index 000000000000..34c66decaaf2 --- /dev/null +++ b/sound/core/pcm_local.h @@ -0,0 +1,51 @@ +/* + * pcm_local.h - a local header file for snd-pcm module. + * + * Copyright (c) Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#ifndef __SOUND_CORE_PCM_LOCAL_H +#define __SOUND_CORE_PCM_LOCAL_H + +extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; + +void snd_interval_mul(const struct snd_interval *a, + const struct snd_interval *b, struct snd_interval *c); +void snd_interval_div(const struct snd_interval *a, + const struct snd_interval *b, struct snd_interval *c); +void snd_interval_muldivk(const struct snd_interval *a, + const struct snd_interval *b, + unsigned int k, struct snd_interval *c); +void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, + const struct snd_interval *b, struct snd_interval *c); + +int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream); +int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); + +int snd_pcm_hw_params_choose(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); + +int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, + snd_pcm_hw_param_t var, u_int32_t mask); + +int snd_pcm_update_state(struct snd_pcm_substream *substream, + struct snd_pcm_runtime *runtime); +int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); + +void snd_pcm_playback_silence(struct snd_pcm_substream *substream, + snd_pcm_uframes_t new_hw_ptr); + +#ifdef CONFIG_SND_PCM_TIMER +void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream); +void snd_pcm_timer_init(struct snd_pcm_substream *substream); +void snd_pcm_timer_done(struct snd_pcm_substream *substream); +#else +static inline void +snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {} +static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {} +static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {} +#endif + +#endif /* __SOUND_CORE_PCM_LOCAL_H */ diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 53dc37357bca..dd8383e29315 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -23,6 +23,9 @@ #include #include #include + +#include "pcm_local.h" + #define SND_PCM_FORMAT_UNKNOWN (-1) /* NOTE: "signed" prefix must be given below since the default char is diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 5be549cf91e5..bf5d0f2acfb9 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -37,6 +37,8 @@ #include #include +#include "pcm_local.h" + /* * Compatibility */ -- cgit v1.2.3 From 29d1a873de542cbb46d0641037d2601cb76be5b1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 10 May 2017 20:02:35 +0200 Subject: ALSA: pcm: Introduce copy_user, copy_kernel and fill_silence ops For supporting the explicit in-kernel copy of PCM buffer data, and also for further code refactoring, three new PCM ops, copy_user, copy_kernel and fill_silence, are introduced. The old copy and silence ops will be deprecated and removed later once when all callers are converted. The copy_kernel ops is the new one, and it's supposed to transfer the PCM data from the given kernel buffer to the hardware ring-buffer (or vice-versa depending on the stream direction), while the copy_user ops is equivalent with the former copy ops, to transfer the data from the user-space buffer. The major difference of the new copy_* and fill_silence ops from the previous ops is that the new ops take bytes instead of frames for size and position arguments. It has two merits: first, it allows the callback implementation often simpler (just call directly memcpy() & co), and second, it may unify the implementations of both interleaved and non-interleaved cases, as we'll see in the later patch. As of this stage, copy_kernel ops isn't referred yet, but only copy_user is used. Reviewed-by: Takashi Sakamoto Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 7 +++++ sound/core/pcm_lib.c | 89 +++++++++++++++++++++++++++++++++++++++++++--------- sound/soc/soc-pcm.c | 3 ++ 3 files changed, 84 insertions(+), 15 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 79fedf517070..86b126be49a2 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -83,6 +83,13 @@ struct snd_pcm_ops { void __user *buf, snd_pcm_uframes_t count); int (*silence)(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count); + int (*fill_silence)(struct snd_pcm_substream *substream, int channel, + unsigned long pos, unsigned long bytes); + int (*copy_user)(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void __user *buf, + unsigned long bytes); + int (*copy_kernel)(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void *buf, unsigned long bytes); struct page *(*page)(struct snd_pcm_substream *substream, unsigned long offset); int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index e50548af4004..1fca1ffd235e 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -57,6 +57,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t frames, ofs, transfer; + char *hwbuf; + int err; if (runtime->silence_size < runtime->boundary) { snd_pcm_sframes_t noise_dist, n; @@ -111,27 +113,37 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { - if (substream->ops->silence) { - int err; + if (substream->ops->fill_silence) { + err = substream->ops->fill_silence(substream, 0, + frames_to_bytes(runtime, ofs), + frames_to_bytes(runtime, transfer)); + snd_BUG_ON(err < 0); + } else if (substream->ops->silence) { err = substream->ops->silence(substream, -1, ofs, transfer); snd_BUG_ON(err < 0); } else { - char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); + hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); } } else { unsigned int c; unsigned int channels = runtime->channels; - if (substream->ops->silence) { + if (substream->ops->fill_silence) { + for (c = 0; c < channels; ++c) { + err = substream->ops->fill_silence(substream, c, + samples_to_bytes(runtime, ofs), + samples_to_bytes(runtime, transfer)); + snd_BUG_ON(err < 0); + } + } else if (substream->ops->silence) { for (c = 0; c < channels; ++c) { - int err; err = substream->ops->silence(substream, c, ofs, transfer); snd_BUG_ON(err < 0); } } else { size_t dma_csize = runtime->dma_bytes / channels; for (c = 0; c < channels; ++c) { - char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); + hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); } } @@ -1997,7 +2009,13 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; int err; char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); - if (substream->ops->copy) { + if (substream->ops->copy_user) { + hwoff = frames_to_bytes(runtime, hwoff); + frames = frames_to_bytes(runtime, frames); + err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); + if (err < 0) + return err; + } else if (substream->ops->copy) { if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) return err; } else { @@ -2121,7 +2139,8 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area)) + if (snd_BUG_ON(!substream->ops->copy_user && !substream->ops->copy + && !runtime->dma_area)) return -EINVAL; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; @@ -2158,8 +2177,30 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, int err; void __user **bufs = (void __user **)data; int channels = runtime->channels; + char __user *buf; int c; - if (substream->ops->copy) { + + if (substream->ops->copy_user) { + hwoff = samples_to_bytes(runtime, hwoff); + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + for (c = 0; c < channels; ++c, ++bufs) { + buf = *bufs + off; + if (!*bufs) { + if (snd_BUG_ON(!substream->ops->fill_silence)) + return -EINVAL; + err = substream->ops->fill_silence(substream, c, + hwoff, + frames); + } else { + err = substream->ops->copy_user(substream, c, + hwoff, buf, + frames); + } + if (err < 0) + return err; + } + } else if (substream->ops->copy) { if (snd_BUG_ON(!substream->ops->silence)) return -EINVAL; for (c = 0; c < channels; ++c, ++bufs) { @@ -2167,7 +2208,7 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) return err; } else { - char __user *buf = *bufs + samples_to_bytes(runtime, off); + buf = *bufs + samples_to_bytes(runtime, off); if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) return err; } @@ -2219,7 +2260,13 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; int err; char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); - if (substream->ops->copy) { + if (substream->ops->copy_user) { + hwoff = frames_to_bytes(runtime, hwoff); + frames = frames_to_bytes(runtime, frames); + err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); + if (err < 0) + return err; + } else if (substream->ops->copy) { if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) return err; } else { @@ -2367,10 +2414,24 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, int err; void __user **bufs = (void __user **)data; int channels = runtime->channels; + char __user *buf; + char *hwbuf; int c; - if (substream->ops->copy) { + + if (substream->ops->copy_user) { + hwoff = samples_to_bytes(runtime, hwoff); + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + for (c = 0; c < channels; ++c, ++bufs) { + if (!*bufs) + continue; + err = substream->ops->copy_user(substream, c, hwoff, + *bufs + off, frames); + if (err < 0) + return err; + } + } else if (substream->ops->copy) { for (c = 0; c < channels; ++c, ++bufs) { - char __user *buf; if (*bufs == NULL) continue; buf = *bufs + samples_to_bytes(runtime, off); @@ -2380,8 +2441,6 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, } else { snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; for (c = 0; c < channels; ++c, ++bufs) { - char *hwbuf; - char __user *buf; if (*bufs == NULL) continue; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index efc5831f205d..8867ed9e5f56 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2743,6 +2743,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (platform->driver->ops) { rtd->ops.ack = platform->driver->ops->ack; + rtd->ops.copy_user = platform->driver->ops->copy_user; + rtd->ops.copy_kernel = platform->driver->ops->copy_kernel; + rtd->ops.fill_silence = platform->driver->ops->fill_silence; rtd->ops.copy = platform->driver->ops->copy; rtd->ops.silence = platform->driver->ops->silence; rtd->ops.page = platform->driver->ops->page; -- cgit v1.2.3 From 2ae48354a1a7afbec0c61280e6410a90894a21e7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 10 May 2017 22:21:52 +0200 Subject: ALSA: pcm: Drop the old copy and silence ops Now that all users of old copy and silence ops have been converted to the new PCM ops, the old stuff can be retired and go away. Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 5 ----- sound/core/pcm_lib.c | 38 +------------------------------------- sound/soc/soc-pcm.c | 2 -- 3 files changed, 1 insertion(+), 44 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 86b126be49a2..0da5117636ec 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -78,11 +78,6 @@ struct snd_pcm_ops { struct timespec *system_ts, struct timespec *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report); - int (*copy)(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, - void __user *buf, snd_pcm_uframes_t count); - int (*silence)(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, snd_pcm_uframes_t count); int (*fill_silence)(struct snd_pcm_substream *substream, int channel, unsigned long pos, unsigned long bytes); int (*copy_user)(struct snd_pcm_substream *substream, int channel, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 1fca1ffd235e..9c5fe62e2c51 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -118,9 +118,6 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram frames_to_bytes(runtime, ofs), frames_to_bytes(runtime, transfer)); snd_BUG_ON(err < 0); - } else if (substream->ops->silence) { - err = substream->ops->silence(substream, -1, ofs, transfer); - snd_BUG_ON(err < 0); } else { hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); @@ -135,11 +132,6 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram samples_to_bytes(runtime, transfer)); snd_BUG_ON(err < 0); } - } else if (substream->ops->silence) { - for (c = 0; c < channels; ++c) { - err = substream->ops->silence(substream, c, ofs, transfer); - snd_BUG_ON(err < 0); - } } else { size_t dma_csize = runtime->dma_bytes / channels; for (c = 0; c < channels; ++c) { @@ -2015,9 +2007,6 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); if (err < 0) return err; - } else if (substream->ops->copy) { - if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 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))) @@ -2139,8 +2128,7 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (snd_BUG_ON(!substream->ops->copy_user && !substream->ops->copy - && !runtime->dma_area)) + if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area)) return -EINVAL; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; @@ -2200,19 +2188,6 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, if (err < 0) return err; } - } else if (substream->ops->copy) { - if (snd_BUG_ON(!substream->ops->silence)) - return -EINVAL; - for (c = 0; c < channels; ++c, ++bufs) { - if (*bufs == NULL) { - if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) - return err; - } else { - buf = *bufs + samples_to_bytes(runtime, off); - if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) - return err; - } - } } else { /* default transfer behaviour */ size_t dma_csize = runtime->dma_bytes / channels; @@ -2266,9 +2241,6 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); if (err < 0) return err; - } else if (substream->ops->copy) { - if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 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))) @@ -2430,14 +2402,6 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, if (err < 0) return err; } - } else if (substream->ops->copy) { - for (c = 0; c < channels; ++c, ++bufs) { - if (*bufs == NULL) - continue; - buf = *bufs + samples_to_bytes(runtime, off); - if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) - return err; - } } else { snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; for (c = 0; c < channels; ++c, ++bufs) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 8867ed9e5f56..dcc5ece08668 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2746,8 +2746,6 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.copy_user = platform->driver->ops->copy_user; rtd->ops.copy_kernel = platform->driver->ops->copy_kernel; rtd->ops.fill_silence = platform->driver->ops->fill_silence; - rtd->ops.copy = platform->driver->ops->copy; - rtd->ops.silence = platform->driver->ops->silence; rtd->ops.page = platform->driver->ops->page; rtd->ops.mmap = platform->driver->ops->mmap; } -- cgit v1.2.3 From 6ba63929ae76e5aaafa14021517fab41cfcd5c83 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 24 May 2017 17:51:30 +0200 Subject: ALSA: pcm: Check PCM state by a common helper function Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 81 +++++++++++++++++++--------------------------------- 1 file changed, 29 insertions(+), 52 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 9c5fe62e2c51..5fcd798672b4 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2019,6 +2019,22 @@ typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwof unsigned long data, unsigned int off, snd_pcm_uframes_t size); +static int pcm_accessible_state(struct snd_pcm_runtime *runtime) +{ + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PAUSED: + return 0; + case SNDRV_PCM_STATE_XRUN: + return -EPIPE; + case SNDRV_PCM_STATE_SUSPENDED: + return -ESTRPIPE; + default: + return -EBADFD; + } +} + static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, unsigned long data, snd_pcm_uframes_t size, @@ -2035,21 +2051,9 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, return 0; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_RUNNING: - case SNDRV_PCM_STATE_PAUSED: - break; - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; - goto _end_unlock; - default: - err = -EBADFD; + err = pcm_accessible_state(runtime); + if (err < 0) goto _end_unlock; - } runtime->twake = runtime->control->avail_min ? : 1; if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) @@ -2085,16 +2089,9 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end_unlock; - switch (runtime->status->state) { - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; + err = pcm_accessible_state(runtime); + if (err < 0) goto _end_unlock; - default: - break; - } appl_ptr += frames; if (appl_ptr >= runtime->boundary) appl_ptr -= runtime->boundary; @@ -2265,27 +2262,14 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, return 0; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - if (size >= runtime->start_threshold) { - err = snd_pcm_start(substream); - if (err < 0) - goto _end_unlock; - } - break; - case SNDRV_PCM_STATE_DRAINING: - case SNDRV_PCM_STATE_RUNNING: - case SNDRV_PCM_STATE_PAUSED: - break; - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; - goto _end_unlock; - default: - err = -EBADFD; + err = pcm_accessible_state(runtime); + if (err < 0) goto _end_unlock; + if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && + size >= runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; } runtime->twake = runtime->control->avail_min ? : 1; @@ -2329,16 +2313,9 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end_unlock; - switch (runtime->status->state) { - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; + err = pcm_accessible_state(runtime); + if (err < 0) goto _end_unlock; - default: - break; - } appl_ptr += frames; if (appl_ptr >= runtime->boundary) appl_ptr -= runtime->boundary; -- cgit v1.2.3 From bdc4acf7f6eb14a64c549c04c741b18e3afb350e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 24 May 2017 17:59:17 +0200 Subject: ALSA: pcm: Shuffle codes Just shuffle the codes, without any change otherwise. Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 212 +++++++++++++++++++++++++-------------------------- 1 file changed, 106 insertions(+), 106 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 5fcd798672b4..51eeea9088de 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1993,6 +1993,10 @@ static int wait_for_avail(struct snd_pcm_substream *substream, return err; } +typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, + unsigned long data, unsigned int off, + snd_pcm_uframes_t size); + static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, @@ -2015,9 +2019,68 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, return 0; } -typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t size); +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) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + void __user **bufs = (void __user **)data; + int channels = runtime->channels; + char __user *buf; + int c; + + if (substream->ops->copy_user) { + hwoff = samples_to_bytes(runtime, hwoff); + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + for (c = 0; c < channels; ++c, ++bufs) { + buf = *bufs + off; + if (!*bufs) { + if (snd_BUG_ON(!substream->ops->fill_silence)) + return -EINVAL; + err = substream->ops->fill_silence(substream, c, + hwoff, + frames); + } else { + err = substream->ops->copy_user(substream, c, + hwoff, buf, + frames); + } + if (err < 0) + return err; + } + } else { + /* 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); + } else { + char __user *buf = *bufs + samples_to_bytes(runtime, off); + if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) + return -EFAULT; + } + } + } + return 0; +} + +/* sanity-check for read/write methods */ +static int pcm_sanity_check(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; + runtime = substream->runtime; + if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area)) + return -EINVAL; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + return 0; +} static int pcm_accessible_state(struct snd_pcm_runtime *runtime) { @@ -2118,20 +2181,6 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } -/* sanity-check for read/write methods */ -static int pcm_sanity_check(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime; - if (PCM_RUNTIME_CHECK(substream)) - return -ENXIO; - runtime = substream->runtime; - if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area)) - return -EINVAL; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) - return -EBADFD; - return 0; -} - snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) { struct snd_pcm_runtime *runtime; @@ -2153,55 +2202,6 @@ snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const v EXPORT_SYMBOL(snd_pcm_lib_write); -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) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - void __user **bufs = (void __user **)data; - int channels = runtime->channels; - char __user *buf; - int c; - - if (substream->ops->copy_user) { - hwoff = samples_to_bytes(runtime, hwoff); - off = samples_to_bytes(runtime, off); - frames = samples_to_bytes(runtime, frames); - for (c = 0; c < channels; ++c, ++bufs) { - buf = *bufs + off; - if (!*bufs) { - if (snd_BUG_ON(!substream->ops->fill_silence)) - return -EINVAL; - err = substream->ops->fill_silence(substream, c, - hwoff, - frames); - } else { - err = substream->ops->copy_user(substream, c, - hwoff, buf, - frames); - } - if (err < 0) - return err; - } - } else { - /* 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); - } else { - char __user *buf = *bufs + samples_to_bytes(runtime, off); - if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) - return -EFAULT; - } - } - } - return 0; -} - snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, void __user **bufs, snd_pcm_uframes_t frames) @@ -2246,6 +2246,46 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, return 0; } +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) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + void __user **bufs = (void __user **)data; + int channels = runtime->channels; + char __user *buf; + char *hwbuf; + int c; + + if (substream->ops->copy_user) { + hwoff = samples_to_bytes(runtime, hwoff); + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + for (c = 0; c < channels; ++c, ++bufs) { + if (!*bufs) + continue; + err = substream->ops->copy_user(substream, c, hwoff, + *bufs + off, frames); + 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) + continue; + + 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))) + return -EFAULT; + } + } + return 0; +} + static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, unsigned long data, snd_pcm_uframes_t size, @@ -2354,46 +2394,6 @@ snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __u EXPORT_SYMBOL(snd_pcm_lib_read); -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) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - void __user **bufs = (void __user **)data; - int channels = runtime->channels; - char __user *buf; - char *hwbuf; - int c; - - if (substream->ops->copy_user) { - hwoff = samples_to_bytes(runtime, hwoff); - off = samples_to_bytes(runtime, off); - frames = samples_to_bytes(runtime, frames); - for (c = 0; c < channels; ++c, ++bufs) { - if (!*bufs) - continue; - err = substream->ops->copy_user(substream, c, hwoff, - *bufs + off, frames); - 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) - continue; - - 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))) - return -EFAULT; - } - } - return 0; -} - snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, void __user **bufs, snd_pcm_uframes_t frames) -- cgit v1.2.3 From c48f12ee0acbd431d6c3ed249f79a6d68b757058 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 21 May 2017 09:35:21 +0200 Subject: ALSA: pcm: Call directly the common read/write helpers Make snd_pcm_lib_read() and *_write() static inline functions that call the common helper functions directly. This reduces a slight amount of codes, and at the same time, it's a preparation for the further cleanups / fixes. Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 43 +++++++++++--- sound/core/pcm_lib.c | 156 ++++++++++++++++++--------------------------------- 2 files changed, 89 insertions(+), 110 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 0da5117636ec..0fac948bb053 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1072,15 +1072,40 @@ 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, - const void __user *buf, - snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, - void __user *buf, snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, - void __user **bufs, snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, - void __user **bufs, snd_pcm_uframes_t frames); +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, + void *buf, bool interleaved, + snd_pcm_uframes_t frames); + +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); +} + +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); +} + +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); +} + +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); +} int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime); unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 51eeea9088de..ae030c5eb7c6 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1994,12 +1994,12 @@ 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, + void *data, unsigned int off, snd_pcm_uframes_t size); static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, - unsigned long data, unsigned int off, + void *data, unsigned int off, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2021,7 +2021,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, + void *data, unsigned int off, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2098,21 +2098,39 @@ static int pcm_accessible_state(struct snd_pcm_runtime *runtime) } } -static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, - unsigned long data, - snd_pcm_uframes_t size, - int nonblock, - transfer_f transfer) +snd_pcm_sframes_t __snd_pcm_lib_write(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; - int err = 0; + transfer_f transfer; + bool nonblock; + int err; + + err = pcm_sanity_check(substream); + if (err < 0) + return err; + runtime = substream->runtime; + + if (interleaved) { + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && + runtime->channels > 1) + return -EINVAL; + transfer = snd_pcm_lib_write_transfer; + } else { + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + transfer = snd_pcm_lib_writev_transfer; + } 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) @@ -2180,53 +2198,11 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, snd_pcm_stream_unlock_irq(substream); return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } - -snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) -{ - struct snd_pcm_runtime *runtime; - int nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - runtime = substream->runtime; - nonblock = !!(substream->f_flags & O_NONBLOCK); - - if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && - runtime->channels > 1) - return -EINVAL; - return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, - snd_pcm_lib_write_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_write); - -snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, - void __user **bufs, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime; - int nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - runtime = substream->runtime; - nonblock = !!(substream->f_flags & O_NONBLOCK); - - if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) - return -EINVAL; - return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, - nonblock, snd_pcm_lib_writev_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_writev); +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, + void *data, unsigned int off, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2248,7 +2224,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, + void *data, unsigned int off, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2286,21 +2262,39 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, return 0; } -static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, - unsigned long data, - snd_pcm_uframes_t size, - int nonblock, - transfer_f transfer) +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; - int err = 0; + transfer_f transfer; + bool nonblock; + int err; + + err = pcm_sanity_check(substream); + if (err < 0) + return err; + runtime = substream->runtime; + + if (interleaved) { + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && + runtime->channels > 1) + return -EINVAL; + transfer = snd_pcm_lib_read_transfer; + } else { + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + transfer = snd_pcm_lib_readv_transfer; + } 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) @@ -2375,47 +2369,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, snd_pcm_stream_unlock_irq(substream); return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } - -snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size) -{ - struct snd_pcm_runtime *runtime; - int nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - runtime = substream->runtime; - nonblock = !!(substream->f_flags & O_NONBLOCK); - if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) - return -EINVAL; - return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_read); - -snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, - void __user **bufs, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime; - int nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) - return -EBADFD; - - nonblock = !!(substream->f_flags & O_NONBLOCK); - if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) - return -EINVAL; - return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_readv); +EXPORT_SYMBOL(__snd_pcm_lib_read); /* * standard channel mapping helpers -- cgit v1.2.3 From 9f60063094ba72e2767be18289baf5151f1f1c2f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 24 May 2017 18:15:26 +0200 Subject: ALSA: pcm: More unification of PCM transfer codes This patch proceeds more abstraction of PCM read/write loop codes. For both interleaved and non-interleaved transfers, the same copy or silence transfer code (which is defined as pcm_transfer_f) is used now. This became possible since we switched to byte size to copy_* and fill_silence ops argument instead of frames. And, for both read and write, we can use the same copy function (which is defined as pcm_copy_f), just depending on whether interleaved or non-interleaved mode. The transfer function is determined at the beginning of the loop, depending on whether the driver gives the specific copy ops or it's the standard read/write. Another bonus by this change is that we now guarantee the silencing behavior when NULL buffer is passed to write helpers. It'll simplify some codes later. Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 254 +++++++++++++++++++++++++-------------------------- 1 file changed, 123 insertions(+), 131 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ae030c5eb7c6..0c53a34201c1 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1993,77 +1993,100 @@ static int wait_for_avail(struct snd_pcm_substream *substream, return err; } -typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, - void *data, unsigned int off, - snd_pcm_uframes_t size); +typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes); -static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - void *data, unsigned int off, - snd_pcm_uframes_t frames) +typedef int (*pcm_copy_f)(struct snd_pcm_substream *, snd_pcm_uframes_t, void *, + snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f); + +/* calculate the target DMA-buffer position to be written/read */ +static void *get_dma_ptr(struct snd_pcm_runtime *runtime, + int channel, unsigned long hwoff) +{ + return runtime->dma_area + hwoff + + 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) +{ + if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff), + buf, bytes)) + return -EFAULT; + return 0; +} + +/* fill silence instead of copy data; called as a transfer helper + * from __snd_pcm_lib_write() or directly from noninterleaved_copy() when + * a NULL buffer is passed + */ +static int fill_silence(struct snd_pcm_substream *substream, int channel, + unsigned long hwoff, void *buf, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; - int err; - char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); - if (substream->ops->copy_user) { - hwoff = frames_to_bytes(runtime, hwoff); - frames = frames_to_bytes(runtime, frames); - err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); - 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))) - return -EFAULT; - } + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + if (substream->ops->fill_silence) + return substream->ops->fill_silence(substream, channel, + hwoff, bytes); + + snd_pcm_format_set_silence(runtime->format, + get_dma_ptr(runtime, channel, hwoff), + bytes_to_samples(runtime, bytes)); return 0; } - -static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - void *data, unsigned int off, - snd_pcm_uframes_t frames) + +/* call transfer function with the converted pointers and sizes; + * for interleaved mode, it's one shot for all samples + */ +static int interleaved_copy(struct snd_pcm_substream *substream, + snd_pcm_uframes_t hwoff, void *data, + snd_pcm_uframes_t off, + snd_pcm_uframes_t frames, + pcm_transfer_f transfer) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* convert to bytes */ + hwoff = frames_to_bytes(runtime, hwoff); + off = frames_to_bytes(runtime, off); + frames = frames_to_bytes(runtime, frames); + return transfer(substream, 0, hwoff, data + off, frames); +} + +/* call transfer function with the converted pointers and sizes for each + * non-interleaved channel; when buffer is NULL, silencing instead of copying + */ +static int noninterleaved_copy(struct snd_pcm_substream *substream, + snd_pcm_uframes_t hwoff, void *data, + snd_pcm_uframes_t off, + snd_pcm_uframes_t frames, + pcm_transfer_f transfer) { struct snd_pcm_runtime *runtime = substream->runtime; - int err; - void __user **bufs = (void __user **)data; int channels = runtime->channels; - char __user *buf; - int c; - - if (substream->ops->copy_user) { - hwoff = samples_to_bytes(runtime, hwoff); - off = samples_to_bytes(runtime, off); - frames = samples_to_bytes(runtime, frames); - for (c = 0; c < channels; ++c, ++bufs) { - buf = *bufs + off; - if (!*bufs) { - if (snd_BUG_ON(!substream->ops->fill_silence)) - return -EINVAL; - err = substream->ops->fill_silence(substream, c, - hwoff, - frames); - } else { - err = substream->ops->copy_user(substream, c, - hwoff, buf, - frames); - } - if (err < 0) - return err; - } - } else { - /* 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); - } else { - char __user *buf = *bufs + samples_to_bytes(runtime, off); - if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) - return -EFAULT; - } - } + void **bufs = data; + int c, err; + + /* convert to bytes; note that it's not frames_to_bytes() here. + * in non-interleaved mode, we copy for each channel, thus + * each copy is n_samples bytes x channels = whole frames. + */ + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + hwoff = samples_to_bytes(runtime, hwoff); + for (c = 0; c < channels; ++c, ++bufs) { + if (!data || !*bufs) + err = fill_silence(substream, c, hwoff, NULL, frames); + else + err = transfer(substream, c, hwoff, *bufs + off, + frames); + if (err < 0) + return err; } return 0; } @@ -2106,24 +2129,33 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; snd_pcm_uframes_t avail; - transfer_f transfer; + pcm_copy_f writer; + pcm_transfer_f transfer; bool nonblock; int err; err = pcm_sanity_check(substream); if (err < 0) return err; - runtime = substream->runtime; if (interleaved) { if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && runtime->channels > 1) return -EINVAL; - transfer = snd_pcm_lib_write_transfer; + writer = interleaved_copy; } else { if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) return -EINVAL; - transfer = snd_pcm_lib_writev_transfer; + writer = noninterleaved_copy; + } + + if (!data) { + transfer = fill_silence; + } else { + if (substream->ops->copy_user) + transfer = (pcm_transfer_f)substream->ops->copy_user; + else + transfer = default_write_copy_user; } if (size == 0) @@ -2166,7 +2198,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 = writer(substream, appl_ofs, data, offset, frames, + transfer); snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end_unlock; @@ -2200,65 +2233,15 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, } EXPORT_SYMBOL(__snd_pcm_lib_write); -static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - void *data, unsigned int off, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); - if (substream->ops->copy_user) { - hwoff = frames_to_bytes(runtime, hwoff); - frames = frames_to_bytes(runtime, frames); - err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); - 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))) - return -EFAULT; - } - return 0; -} - -static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - void *data, unsigned int off, - snd_pcm_uframes_t frames) +/* 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) { - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - void __user **bufs = (void __user **)data; - int channels = runtime->channels; - char __user *buf; - char *hwbuf; - int c; - - if (substream->ops->copy_user) { - hwoff = samples_to_bytes(runtime, hwoff); - off = samples_to_bytes(runtime, off); - frames = samples_to_bytes(runtime, frames); - for (c = 0; c < channels; ++c, ++bufs) { - if (!*bufs) - continue; - err = substream->ops->copy_user(substream, c, hwoff, - *bufs + off, frames); - 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) - continue; - - 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))) - return -EFAULT; - } - } + if (copy_to_user((void __user *)buf, + get_dma_ptr(substream->runtime, channel, hwoff), + bytes)) + return -EFAULT; return 0; } @@ -2270,26 +2253,34 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; snd_pcm_uframes_t avail; - transfer_f transfer; + pcm_copy_f reader; + pcm_transfer_f transfer; bool nonblock; int err; err = pcm_sanity_check(substream); if (err < 0) return err; - runtime = substream->runtime; + + if (!data) + return -EINVAL; if (interleaved) { if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && runtime->channels > 1) return -EINVAL; - transfer = snd_pcm_lib_read_transfer; + reader = interleaved_copy; } else { if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) return -EINVAL; - transfer = snd_pcm_lib_readv_transfer; + 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; @@ -2343,7 +2334,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 = reader(substream, appl_ofs, data, offset, frames, + transfer); snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end_unlock; -- cgit v1.2.3 From 5c7264cfbb209efea04bbbd69b8b4f5f2fc5f86d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 24 May 2017 22:36:23 +0200 Subject: 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 Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 13 ++-- sound/core/pcm_lib.c | 184 +++++++++++++-------------------------------------- 2 files changed, 51 insertions(+), 146 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 0fac948bb053..db649083c76d 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -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); 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 -- cgit v1.2.3 From a9cd29e79965f0f769d13edcf2e9adb389698e7b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 24 May 2017 18:18:15 +0200 Subject: ALSA: pcm: Simplify snd_pcm_playback_silence() Use the existing silence helper codes for simplification. Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 50 ++++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 30 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index af73c629a6b2..f31949b20c0d 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -44,6 +44,9 @@ #define trace_hw_ptr_error(substream, reason) #endif +static int fill_silence_frames(struct snd_pcm_substream *substream, + snd_pcm_uframes_t off, snd_pcm_uframes_t frames); + /* * fill ring buffer with silence * runtime->silence_start: starting pointer to silence area @@ -57,7 +60,6 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t frames, ofs, transfer; - char *hwbuf; int err; if (runtime->silence_size < runtime->boundary) { @@ -111,35 +113,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram ofs = runtime->silence_start % runtime->buffer_size; while (frames > 0) { transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; - if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || - runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { - if (substream->ops->fill_silence) { - err = substream->ops->fill_silence(substream, 0, - frames_to_bytes(runtime, ofs), - frames_to_bytes(runtime, transfer)); - snd_BUG_ON(err < 0); - } else { - hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); - snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); - } - } else { - unsigned int c; - unsigned int channels = runtime->channels; - if (substream->ops->fill_silence) { - for (c = 0; c < channels; ++c) { - err = substream->ops->fill_silence(substream, c, - samples_to_bytes(runtime, ofs), - samples_to_bytes(runtime, transfer)); - snd_BUG_ON(err < 0); - } - } else { - size_t dma_csize = runtime->dma_bytes / channels; - for (c = 0; c < channels; ++c) { - hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); - snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); - } - } - } + err = fill_silence_frames(substream, ofs, transfer); + snd_BUG_ON(err < 0); runtime->silence_filled += transfer; frames -= transfer; ofs = 0; @@ -2103,6 +2078,21 @@ static int noninterleaved_copy(struct snd_pcm_substream *substream, return 0; } +/* fill silence on the given buffer position; + * called from snd_pcm_playback_silence() + */ +static int fill_silence_frames(struct snd_pcm_substream *substream, + snd_pcm_uframes_t off, snd_pcm_uframes_t frames) +{ + if (substream->runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + substream->runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) + return interleaved_copy(substream, off, NULL, 0, frames, + fill_silence); + else + return noninterleaved_copy(substream, off, NULL, 0, frames, + fill_silence); +} + /* sanity-check for read/write methods */ static int pcm_sanity_check(struct snd_pcm_substream *substream) { -- cgit v1.2.3 From 68541213720df9bb7904cc1fecab563d424849ae Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 24 May 2017 18:23:20 +0200 Subject: ALSA: pcm: Direct in-kernel read/write support Now all materials are ready, let's allow the direct in-kernel read/write, i.e. a kernel-space buffer is passed for read or write, instead of the normal user-space buffer. This feature is used by OSS layer and UAC1 driver, for example. The __snd_pcm_lib_xfer() takes in_kernel argument that indicates the in-kernel buffer copy. When this flag is set, another transfer code is used. It's either via copy_kernel PCM ops or the normal memcpy(), depending on the driver setup. As external API, snd_pcm_kernel_read(), *_write() and other variants are provided. That's all. This support is really simple because of the code refactoring until now. Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 38 +++++++++++++++++++++++++++++++++----- sound/core/pcm_lib.c | 26 +++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 6 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index db649083c76d..c24f85f12b71 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1074,34 +1074,62 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, void snd_pcm_period_elapsed(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_uframes_t frames, bool in_kernel); 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_xfer(substream, (void *)buf, true, frames); + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames, false); } 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_xfer(substream, (void *)buf, true, frames); + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames, false); } 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_xfer(substream, (void *)bufs, false, frames); + return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames, false); } 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_xfer(substream, (void *)bufs, false, frames); + return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames, false); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_write(struct snd_pcm_substream *substream, + const void *buf, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames, true); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_read(struct snd_pcm_substream *substream, + void *buf, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, buf, true, frames, true); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_writev(struct snd_pcm_substream *substream, + void **bufs, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, bufs, false, frames, true); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_readv(struct snd_pcm_substream *substream, + void **bufs, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, bufs, false, frames, true); } int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index f31949b20c0d..95b8ef15029f 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1994,6 +1994,15 @@ static int default_write_copy(struct snd_pcm_substream *substream, return 0; } +/* default copy_kernel ops for write */ +static int default_write_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + memcpy(get_dma_ptr(substream->runtime, channel, hwoff), buf, bytes); + return 0; +} + /* fill silence instead of copy data; called as a transfer helper * from __snd_pcm_lib_write() or directly from noninterleaved_copy() when * a NULL buffer is passed @@ -2027,6 +2036,15 @@ static int default_read_copy(struct snd_pcm_substream *substream, return 0; } +/* default copy_kernel ops for read */ +static int default_read_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + memcpy(buf, get_dma_ptr(substream->runtime, channel, hwoff), bytes); + return 0; +} + /* call transfer function with the converted pointers and sizes; * for interleaved mode, it's one shot for all samples */ @@ -2126,7 +2144,7 @@ static int pcm_accessible_state(struct snd_pcm_runtime *runtime) /* 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) + snd_pcm_uframes_t size, bool in_kernel) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t xfer = 0; @@ -2159,6 +2177,12 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, transfer = fill_silence; else return -EINVAL; + } else if (in_kernel) { + if (substream->ops->copy_kernel) + transfer = substream->ops->copy_kernel; + else + transfer = is_playback ? + default_write_copy_kernel : default_read_copy_kernel; } else { if (substream->ops->copy_user) transfer = (pcm_transfer_f)substream->ops->copy_user; -- cgit v1.2.3 From 60f96aaecb19ca294addfff0d2d0335293f3c379 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 9 Jun 2017 21:46:48 +0900 Subject: ALSA: pcm: localize snd_pcm_hw_params_choose() As of v4.12, snd_pcm_hw_params_choose() is just called in a process context of ioctl(2) with SNDRV_PCM_IOCTL_HW_PARAMS. The function locates in a different file, which has no tracepoints. This commit moves the function to a file with the tracepoints for later commit. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 40 ---------------------------------------- sound/core/pcm_local.h | 3 --- sound/core/pcm_native.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 43 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 95b8ef15029f..9dc7bbfe8853 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1700,46 +1700,6 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, EXPORT_SYMBOL(snd_pcm_hw_param_last); -/** - * snd_pcm_hw_param_choose - choose a configuration defined by @params - * @pcm: PCM instance - * @params: the hw_params instance - * - * Choose one configuration from configuration space defined by @params. - * The configuration chosen is that obtained fixing in this order: - * first access, first format, first subformat, min channels, - * min rate, min period time, max buffer size, min tick time - * - * Return: Zero if successful, or a negative error code on failure. - */ -int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, - struct snd_pcm_hw_params *params) -{ - static const int vars[] = { - SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_SUBFORMAT, - SNDRV_PCM_HW_PARAM_CHANNELS, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_PERIOD_TIME, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - SNDRV_PCM_HW_PARAM_TICK_TIME, - -1 - }; - const int *v; - int err; - - for (v = vars; *v != -1; v++) { - if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) - err = snd_pcm_hw_param_first(pcm, params, *v, NULL); - else - err = snd_pcm_hw_param_last(pcm, params, *v, NULL); - if (snd_BUG_ON(err < 0)) - return err; - } - return 0; -} - static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, void *arg) { diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h index 34c66decaaf2..e4bf2af62b02 100644 --- a/sound/core/pcm_local.h +++ b/sound/core/pcm_local.h @@ -24,9 +24,6 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream); int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); -int snd_pcm_hw_params_choose(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params); - int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, u_int32_t mask); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 3293db0172db..8d9d181b1c03 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -571,6 +571,46 @@ static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream, #endif } +/** + * snd_pcm_hw_param_choose - choose a configuration defined by @params + * @pcm: PCM instance + * @params: the hw_params instance + * + * Choose one configuration from configuration space defined by @params. + * The configuration chosen is that obtained fixing in this order: + * first access, first format, first subformat, min channels, + * min rate, min period time, max buffer size, min tick time + * + * Return: Zero if successful, or a negative error code on failure. + */ +static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params) +{ + static const int vars[] = { + SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_SUBFORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + SNDRV_PCM_HW_PARAM_TICK_TIME, + -1 + }; + const int *v; + int err; + + for (v = vars; *v != -1; v++) { + if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) + err = snd_pcm_hw_param_first(pcm, params, *v, NULL); + else + err = snd_pcm_hw_param_last(pcm, params, *v, NULL); + if (snd_BUG_ON(err < 0)) + return err; + } + return 0; +} + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { -- cgit v1.2.3 From 66e01a5cf63f2b132059d0d3d78ed737207489f2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 12 Jun 2017 09:41:44 +0900 Subject: ALSA: pcm: unify codes to operate application-side position on PCM buffer In a series of recent work, ALSA PCM core got some arrangements to handle application-side position on PCM buffer. However, relevant codes still disperse to two translation units This commit unifies these codes into a helper function. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 27 ++++++++++++++++++++++++--- sound/core/pcm_local.h | 2 ++ sound/core/pcm_native.c | 28 ++++------------------------ 3 files changed, 30 insertions(+), 27 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 9dc7bbfe8853..d82f1437667f 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2101,6 +2101,27 @@ static int pcm_accessible_state(struct snd_pcm_runtime *runtime) } } +/* update to the given appl_ptr and call ack callback if needed; + * when an error is returned, take back to the original value + */ +int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, + snd_pcm_uframes_t appl_ptr) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr; + int ret; + + runtime->control->appl_ptr = appl_ptr; + if (substream->ops->ack) { + ret = substream->ops->ack(substream); + if (ret < 0) { + runtime->control->appl_ptr = old_appl_ptr; + return ret; + } + } + return 0; +} + /* the common loop for read/write data */ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, void *data, bool interleaved, @@ -2220,9 +2241,9 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, 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); + err = pcm_lib_apply_appl_ptr(substream, appl_ptr); + if (err < 0) + goto _end_unlock; offset += frames; size -= frames; diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h index e4bf2af62b02..16f254732b2a 100644 --- a/sound/core/pcm_local.h +++ b/sound/core/pcm_local.h @@ -27,6 +27,8 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, u_int32_t mask); +int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, + snd_pcm_uframes_t appl_ptr); int snd_pcm_update_state(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime); int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 5099078dde93..07995e645327 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2598,27 +2598,6 @@ static int do_pcm_hwsync(struct snd_pcm_substream *substream) } } -/* update to the given appl_ptr and call ack callback if needed; - * when an error is returned, take back to the original value - */ -static int apply_appl_ptr(struct snd_pcm_substream *substream, - snd_pcm_uframes_t appl_ptr) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr; - int ret; - - runtime->control->appl_ptr = appl_ptr; - if (substream->ops->ack) { - ret = substream->ops->ack(substream); - if (ret < 0) { - runtime->control->appl_ptr = old_appl_ptr; - return ret; - } - } - return 0; -} - /* increase the appl_ptr; returns the processed frames or a negative error */ static snd_pcm_sframes_t forward_appl_ptr(struct snd_pcm_substream *substream, snd_pcm_uframes_t frames, @@ -2635,7 +2614,7 @@ static snd_pcm_sframes_t forward_appl_ptr(struct snd_pcm_substream *substream, appl_ptr = runtime->control->appl_ptr + frames; if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) appl_ptr -= runtime->boundary; - ret = apply_appl_ptr(substream, appl_ptr); + ret = pcm_lib_apply_appl_ptr(substream, appl_ptr); return ret < 0 ? ret : frames; } @@ -2655,7 +2634,7 @@ static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream, appl_ptr = runtime->control->appl_ptr - frames; if (appl_ptr < 0) appl_ptr += runtime->boundary; - ret = apply_appl_ptr(substream, appl_ptr); + ret = pcm_lib_apply_appl_ptr(substream, appl_ptr); return ret < 0 ? ret : frames; } @@ -2783,7 +2762,8 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, } snd_pcm_stream_lock_irq(substream); if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) { - err = apply_appl_ptr(substream, sync_ptr.c.control.appl_ptr); + err = pcm_lib_apply_appl_ptr(substream, + sync_ptr.c.control.appl_ptr); if (err < 0) { snd_pcm_stream_unlock_irq(substream); return err; -- cgit v1.2.3 From fccf53881e9b564321326f62ed5f85130fa6e569 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 12 Jun 2017 09:41:45 +0900 Subject: ALSA: pcm: add 'applptr' event of tracepoint In design of ALSA PCM core, status and control data for runtime of ALSA PCM substream are shared between kernel/user spaces by page frame mapping with read-only attribute. Both of hardware-side and application-side position on PCM buffer are maintained as a part of the status data. In a view of ALSA PCM application, these two positions can be updated by executing ioctl(2) with some commands. There's an event of tracepoint for hardware-side position; 'hwptr'. On the other hand, no events for application-side position. This commit adds a new event for this purpose; 'applptr'. When the application-side position is changed in kernel space, this event is probed with useful information for developers. I note that the event is not probed for all of ALSA PCM applications, When applications are written by read/write programming scenario, the event is surely probed. The applications execute ioctl(2) with SNDRV_PCM_IOCTL_[READ|WRITE][N/I]_FRAMES to read/write any PCM frame, then ALSA PCM core updates the application-side position in kernel land. However, when applications are written by mmap programming scenario, if maintaining the application side position in kernel space accurately, applications should voluntarily execute ioctl(2) with SNDRV_PCM_IOCTL_SYNC_PTR to commit the number of handled PCM frames. If not voluntarily, the application-side position is not changed, thus the added event is not probed. There's a loophole, using architectures to which ALSA PCM core judges non cache coherent. In this case, the status and control data is not mapped into processe's VMA for any applications. Userland library, alsa-lib, is programmed for this case. It executes ioctl(2) with SNDRV_PCM_IOCTL_SYNC_PTR command every time to requiring the status and control data. ARM is such an architecture. Below is an example with serial sound interface (ssi) on i.mx6 quad core SoC. I use v4.1 kernel released by fsl-community with patches from VIA Tech. Inc. for VAB820, and my backport patches for relevant features for this patchset. I use Ubuntu 17.04 from ports.ubuntu.com as user land for armhf architecture. $ aplay -v -M -D hw:imx6vab820sgtl5,0 /dev/urandom -f S16_LE -r 48000 --period-size=128 --buffer-size=256 Playing raw data '/dev/urandom' : Signed 16 bit Little Endian, Rate 48000 Hz, Mono Hardware PCM card 0 'imx6-vab820-sgtl5000' device 0 subdevice 0 Its setup is: stream : PLAYBACK access : MMAP_INTERLEAVED format : S16_LE subformat : STD channels : 1 rate : 48000 exact rate : 48000 (48000/1) msbits : 16 buffer_size : 256 period_size : 128 period_time : 2666 tstamp_mode : NONE tstamp_type : MONOTONIC period_step : 1 avail_min : 128 period_event : 0 start_threshold : 256 stop_threshold : 256 silence_threshold: 0 silence_size : 0 boundary : 1073741824 appl_ptr : 0 hw_ptr : 0 mmap_area[0] = 0x76f98000,0,16 (16) $ trace-cmd record -e snd_pcm:hwptr -e snd_pcm:applptr $ trace-cmd report ... 60.208495: applptr: pcmC0D0p/sub0: prev=1792, curr=1792, avail=0, period=128, buf=256 60.208633: applptr: pcmC0D0p/sub0: prev=1792, curr=1792, avail=0, period=128, buf=256 60.210022: hwptr: pcmC0D0p/sub0: IRQ: pos=128, old=1536, base=1536, period=128, buf=256 60.210202: applptr: pcmC0D0p/sub0: prev=1792, curr=1792, avail=128, period=128, buf=256 60.210344: hwptr: pcmC0D0p/sub0: POS: pos=128, old=1664, base=1536, period=128, buf=256 60.210348: applptr: pcmC0D0p/sub0: prev=1792, curr=1792, avail=128, period=128, buf=256 60.210486: applptr: pcmC0D0p/sub0: prev=1792, curr=1792, avail=128, period=128, buf=256 60.210626: applptr: pcmC0D0p/sub0: prev=1792, curr=1920, avail=0, period=128, buf=256 60.211002: applptr: pcmC0D0p/sub0: prev=1920, curr=1920, avail=0, period=128, buf=256 60.211142: hwptr: pcmC0D0p/sub0: POS: pos=128, old=1664, base=1536, period=128, buf=256 60.211146: applptr: pcmC0D0p/sub0: prev=1920, curr=1920, avail=0, period=128, buf=256 60.211287: applptr: pcmC0D0p/sub0: prev=1920, curr=1920, avail=0, period=128, buf=256 60.212690: hwptr: pcmC0D0p/sub0: IRQ: pos=0, old=1664, base=1536, period=128, buf=256 60.212866: applptr: pcmC0D0p/sub0: prev=1920, curr=1920, avail=128, period=128, buf=256 60.212999: hwptr: pcmC0D0p/sub0: POS: pos=0, old=1792, base=1792, period=128, buf=256 60.213003: applptr: pcmC0D0p/sub0: prev=1920, curr=1920, avail=128, period=128, buf=256 60.213135: applptr: pcmC0D0p/sub0: prev=1920, curr=1920, avail=128, period=128, buf=256 60.213276: applptr: pcmC0D0p/sub0: prev=1920, curr=2048, avail=0, period=128, buf=256 60.213654: applptr: pcmC0D0p/sub0: prev=2048, curr=2048, avail=0, period=128, buf=256 60.213796: hwptr: pcmC0D0p/sub0: POS: pos=0, old=1792, base=1792, period=128, buf=256 60.213800: applptr: pcmC0D0p/sub0: prev=2048, curr=2048, avail=0, period=128, buf=256 60.213937: applptr: pcmC0D0p/sub0: prev=2048, curr=2048, avail=0, period=128, buf=256 60.215356: hwptr: pcmC0D0p/sub0: IRQ: pos=128, old=1792, base=1792, period=128, buf=256 60.215542: applptr: pcmC0D0p/sub0: prev=2048, curr=2048, avail=128, period=128, buf=256 60.215679: hwptr: pcmC0D0p/sub0: POS: pos=128, old=1920, base=1792, period=128, buf=256 60.215683: applptr: pcmC0D0p/sub0: prev=2048, curr=2048, avail=128, period=128, buf=256 60.215813: applptr: pcmC0D0p/sub0: prev=2048, curr=2048, avail=128, period=128, buf=256 60.215947: applptr: pcmC0D0p/sub0: prev=2048, curr=2176, avail=0, period=128, buf=256 ... We can surely see 'applptr' event is probed even if the application run for mmap programming scenario ('-M' option and 'hw' plugin). Below is a result of strace: 02:44:15.886382 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.887203 poll([{fd=4, events=POLLOUT|POLLERR|POLLNVAL}], 1, -1) = 1 ([{fd=4, revents=POLLOUT}]) 02:44:15.887471 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.887637 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.887805 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.887969 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.888132 read(3, "..."..., 256) = 256 02:44:15.889040 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.889221 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.889431 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.889606 poll([{fd=4, events=POLLOUT|POLLERR|POLLNVAL}], 1, -1) = 1 ([{fd=4, revents=POLLOUT}]) 02:44:15.889833 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.889998 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.890164 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.891048 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.891228 read(3, "..."..., 256) = 256 02:44:15.891497 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.891661 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.891829 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 02:44:15.891991 poll([{fd=4, events=POLLOUT|POLLERR|POLLNVAL}], 1, -1) = 1 ([{fd=4, revents=POLLOUT}]) 02:44:15.893007 ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0x56a32b30) = 0 We can see 7 calls of ioctl(2) with SNDRV_PCM_IOCTL_SYNC_PTR per loop with call of poll(2). 128 PCM frames are transferred per loop of one poll(2), because the PCM substream is configured with S16_LE format and 1 channel (2 byte * 1 * 128 = 256 bytes). This equals to the size of period of PCM buffer. Comparing to the probed data, one of the 7 calls of ioctl(2) is actually used to commit the number of copied PCM frames to kernel space. The other calls are just used to check runtime status of PCM substream; e.g. XRUN. The tracepoint event is useful to investigate this case. I note that below modules are related to the above sample. * snd-soc-dummy.ko * snd-soc-imx-sgtl5000.ko * snd-soc-fsl-ssi.ko * snd-soc-imx-pcm-dma.ko * snd-soc-sgtl5000.ko My additional note is lock acquisition. The event is probed under acquiring PCM stream lock. This means that calculation in the event is free from any hardware events. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 4 ++++ sound/core/pcm_trace.h | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index d82f1437667f..e73b6e4135f6 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -42,6 +42,7 @@ #define trace_hwptr(substream, pos, in_interrupt) #define trace_xrun(substream) #define trace_hw_ptr_error(substream, reason) +#define trace_applptr(substream, prev, curr) #endif static int fill_silence_frames(struct snd_pcm_substream *substream, @@ -2119,6 +2120,9 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, return ret; } } + + trace_applptr(substream, old_appl_ptr, appl_ptr); + return 0; } diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h index b63b654da5ff..e672368ab878 100644 --- a/sound/core/pcm_trace.h +++ b/sound/core/pcm_trace.h @@ -102,6 +102,44 @@ TRACE_EVENT(hw_ptr_error, __entry->number, __entry->reason) ); +TRACE_EVENT(applptr, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t prev, snd_pcm_uframes_t curr), + TP_ARGS(substream, prev, curr), + TP_STRUCT__entry( + __field( unsigned int, card ) + __field( unsigned int, device ) + __field( unsigned int, number ) + __field( unsigned int, stream ) + __field( snd_pcm_uframes_t, prev ) + __field( snd_pcm_uframes_t, curr ) + __field( snd_pcm_uframes_t, avail ) + __field( snd_pcm_uframes_t, period_size ) + __field( snd_pcm_uframes_t, buffer_size ) + ), + TP_fast_assign( + __entry->card = (substream)->pcm->card->number; + __entry->device = (substream)->pcm->device; + __entry->number = (substream)->number; + __entry->stream = (substream)->stream; + __entry->prev = (prev); + __entry->curr = (curr); + __entry->avail = (substream)->stream ? snd_pcm_capture_avail(substream->runtime) : snd_pcm_playback_avail(substream->runtime); + __entry->period_size = (substream)->runtime->period_size; + __entry->buffer_size = (substream)->runtime->buffer_size; + ), + TP_printk("pcmC%dD%d%s/sub%d: prev=%lu, curr=%lu, avail=%lu, period=%lu, buf=%lu", + __entry->card, + __entry->device, + __entry->stream ? "c" : "p", + __entry->number, + __entry->prev, + __entry->curr, + __entry->avail, + __entry->period_size, + __entry->buffer_size + ) +); + #endif /* _PCM_TRACE_H */ /* This part must be outside protection */ -- cgit v1.2.3 From f8ff2f28ba49fa41a06215ac3187dede347bc9a7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 13 Jun 2017 15:57:28 +0200 Subject: ALSA: pcm: Skip ack callback without actual appl_ptr update We call ack callback whenever appl_ptr gets updated via pcm_lib_apply_appl_ptr(). There are various code paths to call this function. A part of them are for read/write/forward/rewind, where the appl_ptr is always changed and thus the call of ack is mandatory. OTOH, another part of code paths are from the explicit user call, e.g. via SYNC_PTR ioctl. There, we may receive the same appl_ptr value, and in such a case, calling ack is obviously superfluous. This patch adds the check of the given appl_ptr value, and returns immediately if it's no real update. Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index e73b6e4135f6..75308ddc54ca 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2112,6 +2112,9 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr; int ret; + if (old_appl_ptr == appl_ptr) + return 0; + runtime->control->appl_ptr = appl_ptr; if (substream->ops->ack) { ret = substream->ops->ack(substream); -- cgit v1.2.3 From e11f0f90a626f93899687b1cc909ee37dd6c5809 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 14 Jun 2017 19:30:03 +0900 Subject: ALSA: pcm: remove SNDRV_PCM_IOCTL1_INFO internal command Drivers can implement 'struct snd_pcm_ops.ioctl' to handle some requests from ALSA PCM core. These requests are internal purpose in kernel land. Usually common set of operations are used for it. SNDRV_PCM_IOCTL1_INFO is one of the requests. According to code comment, it has been obsoleted in the old days. We can see old releases in ftp.alsa-project.org. The command was firstly introduced in v0.5.0 release as SND_PCM_IOCTL1_INFO, to allow drivers to fill data of 'struct snd_pcm_channel_info' type. In v0.9.0 release, this was obsoleted by the other commands for ioctl(2) such as SNDRV_PCM_IOCTL_CHANNEL_INFO. This commit removes the long-abandoned command, bye. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 2 +- sound/core/pcm_lib.c | 2 -- sound/core/pcm_native.c | 6 +----- 3 files changed, 2 insertions(+), 8 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index c24f85f12b71..48e3eecac86d 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -102,7 +102,7 @@ struct snd_pcm_ops { #endif #define SNDRV_PCM_IOCTL1_RESET 0 -#define SNDRV_PCM_IOCTL1_INFO 1 +/* 1 is absent slot. */ #define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 #define SNDRV_PCM_IOCTL1_GSTATE 3 #define SNDRV_PCM_IOCTL1_FIFO_SIZE 4 diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 75308ddc54ca..631cd598ba6c 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1787,8 +1787,6 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { switch (cmd) { - case SNDRV_PCM_IOCTL1_INFO: - return 0; case SNDRV_PCM_IOCTL1_RESET: return snd_pcm_lib_ioctl_reset(substream, arg); case SNDRV_PCM_IOCTL1_CHANNEL_INFO: diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 05858c91c0ea..7e8f3656b695 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -212,11 +212,7 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) info->subdevices_avail = pstr->substream_count - pstr->substream_opened; strlcpy(info->subname, substream->name, sizeof(info->subname)); runtime = substream->runtime; - /* AB: FIXME!!! This is definitely nonsense */ - if (runtime) { - info->sync = runtime->sync; - substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); - } + return 0; } -- cgit v1.2.3 From 602d7d72c8255f80898e94562f777635efd1ddaf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 16 Jun 2017 16:12:30 +0200 Subject: ALSA: pcm: Follow standard EXPORT_SYMBOL() declarations Just a tidy up to follow the standard EXPORT_SYMBOL*() declarations in order to improve grep-ability. - Remove superfluous blank line before EXPORT_SYMBOL*() lines Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 21 --------------------- sound/core/pcm_memory.c | 6 ------ sound/core/pcm_misc.c | 11 ----------- sound/core/pcm_native.c | 7 ------- 4 files changed, 45 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 631cd598ba6c..461c21f21caf 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -490,7 +490,6 @@ void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, for (substream = stream->substream; substream != NULL; substream = substream->next) substream->ops = ops; } - EXPORT_SYMBOL(snd_pcm_set_ops); /** @@ -508,7 +507,6 @@ void snd_pcm_set_sync(struct snd_pcm_substream *substream) runtime->sync.id32[2] = -1; runtime->sync.id32[3] = -1; } - EXPORT_SYMBOL(snd_pcm_set_sync); /* @@ -625,7 +623,6 @@ int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) } return changed; } - EXPORT_SYMBOL(snd_interval_refine); static int snd_interval_refine_first(struct snd_interval *i) @@ -888,7 +885,6 @@ int snd_interval_ratnum(struct snd_interval *i, } return err; } - EXPORT_SYMBOL(snd_interval_ratnum); /** @@ -1026,7 +1022,6 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, } return snd_interval_refine(i, &list_range); } - EXPORT_SYMBOL(snd_interval_list); /** @@ -1165,7 +1160,6 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, va_end(args); return 0; } - EXPORT_SYMBOL(snd_pcm_hw_rule_add); /** @@ -1229,7 +1223,6 @@ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_pa struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; return snd_interval_setinteger(constrs_interval(constrs, var)); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); /** @@ -1255,7 +1248,6 @@ int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_par t.integer = 0; return snd_interval_refine(constrs_interval(constrs, var), &t); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, @@ -1286,7 +1278,6 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_list, (void *)l, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_list); static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, @@ -1353,7 +1344,6 @@ int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_ratnums, (void *)r, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, @@ -1388,7 +1378,6 @@ int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_ratdens, (void *)r, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, @@ -1435,7 +1424,6 @@ int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, (void*) l, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, @@ -1463,7 +1451,6 @@ int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_step, (void *) step, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_step); static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) @@ -1494,7 +1481,6 @@ int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_pow2, NULL, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params, @@ -1553,7 +1539,6 @@ void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) _snd_pcm_hw_param_any(params, k); params->info = ~0U; } - EXPORT_SYMBOL(_snd_pcm_hw_params_any); /** @@ -1586,7 +1571,6 @@ int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, } return -EINVAL; } - EXPORT_SYMBOL(snd_pcm_hw_param_value); void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, @@ -1604,7 +1588,6 @@ void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_BUG(); } } - EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, @@ -1651,7 +1634,6 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, } return snd_pcm_hw_param_value(params, var, dir); } - EXPORT_SYMBOL(snd_pcm_hw_param_first); static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, @@ -1698,7 +1680,6 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, } return snd_pcm_hw_param_value(params, var, dir); } - EXPORT_SYMBOL(snd_pcm_hw_param_last); static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, @@ -1796,7 +1777,6 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, } return -ENXIO; } - EXPORT_SYMBOL(snd_pcm_lib_ioctl); /** @@ -1832,7 +1812,6 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) kill_fasync(&runtime->fasync, SIGIO, POLL_IN); snd_pcm_stream_unlock_irqrestore(substream, flags); } - EXPORT_SYMBOL(snd_pcm_period_elapsed); /* diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index b45f6aa32264..ae33e456708c 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -120,7 +120,6 @@ int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free(substream); return 0; } - EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); #ifdef CONFIG_SND_VERBOSE_PROCFS @@ -263,7 +262,6 @@ int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream, substream->dma_buffer.dev.dev = data; return snd_pcm_lib_preallocate_pages1(substream, size, max); } - EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); /** @@ -292,7 +290,6 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, return err; return 0; } - EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); #ifdef CONFIG_SND_DMA_SGBUF @@ -314,7 +311,6 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne return NULL; return sgbuf->page_table[idx]; } - EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); #endif /* CONFIG_SND_DMA_SGBUF */ @@ -370,7 +366,6 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) runtime->dma_bytes = size; return 1; /* area was changed */ } - EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); /** @@ -398,7 +393,6 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream) snd_pcm_set_runtime_buffer(substream, NULL); return 0; } - EXPORT_SYMBOL(snd_pcm_lib_free_pages); int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream, diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index dd8383e29315..9be81025372f 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -248,7 +248,6 @@ int snd_pcm_format_signed(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_signed); /** @@ -267,7 +266,6 @@ int snd_pcm_format_unsigned(snd_pcm_format_t format) return val; return !val; } - EXPORT_SYMBOL(snd_pcm_format_unsigned); /** @@ -280,7 +278,6 @@ int snd_pcm_format_linear(snd_pcm_format_t format) { return snd_pcm_format_signed(format) >= 0; } - EXPORT_SYMBOL(snd_pcm_format_linear); /** @@ -299,7 +296,6 @@ int snd_pcm_format_little_endian(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_little_endian); /** @@ -318,7 +314,6 @@ int snd_pcm_format_big_endian(snd_pcm_format_t format) return val; return !val; } - EXPORT_SYMBOL(snd_pcm_format_big_endian); /** @@ -337,7 +332,6 @@ int snd_pcm_format_width(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_width); /** @@ -356,7 +350,6 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_physical_width); /** @@ -374,7 +367,6 @@ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) return -EINVAL; return samples * phys_width / 8; } - EXPORT_SYMBOL(snd_pcm_format_size); /** @@ -391,7 +383,6 @@ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) return NULL; return pcm_formats[(INT)format].silence; } - EXPORT_SYMBOL(snd_pcm_format_silence_64); /** @@ -462,7 +453,6 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int #endif return 0; } - EXPORT_SYMBOL(snd_pcm_format_set_silence); /** @@ -491,7 +481,6 @@ int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime) } return 0; } - EXPORT_SYMBOL(snd_pcm_limit_hw_rates); /** diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 7e8f3656b695..d35c6614fdab 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1278,7 +1278,6 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state) { return snd_pcm_action(&snd_pcm_action_stop, substream, state); } - EXPORT_SYMBOL(snd_pcm_stop); /** @@ -1453,7 +1452,6 @@ int snd_pcm_suspend(struct snd_pcm_substream *substream) snd_pcm_stream_unlock_irqrestore(substream, flags); return err; } - EXPORT_SYMBOL(snd_pcm_suspend); /** @@ -1485,7 +1483,6 @@ int snd_pcm_suspend_all(struct snd_pcm *pcm) } return 0; } - EXPORT_SYMBOL(snd_pcm_suspend_all); /* resume */ @@ -2369,7 +2366,6 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) } snd_pcm_detach_substream(substream); } - EXPORT_SYMBOL(snd_pcm_release_substream); int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, @@ -2411,7 +2407,6 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, snd_pcm_release_substream(substream); return err; } - EXPORT_SYMBOL(snd_pcm_open_substream); static int snd_pcm_open_file(struct file *file, @@ -3504,7 +3499,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, area->vm_page_prot = pgprot_noncached(area->vm_page_prot); return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes); } - EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); #endif /* SNDRV_PCM_INFO_MMAP */ @@ -3553,7 +3547,6 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, atomic_inc(&substream->mmap_count); return err; } - EXPORT_SYMBOL(snd_pcm_mmap_data); static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) -- cgit v1.2.3 From aa30db060121f688d01f74f8d3fe603f7c4c731c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 16 Jun 2017 22:29:55 +0200 Subject: ALSA: pcm: Fix possible inconsistent appl_ptr update via mmap The ALSA PCM core refers to the appl_ptr value stored on the mmapped page that is shared between kernel and user-space. Although the reference is performed in the PCM stream lock, it doesn't guarantee the atomic access when the value gets updated concurrently from the user-space on another CPU. In most of codes, this is no big problem, but still there are a few places that may result in slight inconsistencies because they access runtime->control->appl_ptr multiple times; that is, the second read might be a different value from the first value. It can be even backward or jumping, as we have no control for it. Hence, the calculation may give an unexpected value. Luckily, there is no security vulnerability by that, as far as I've checked. But still we should address it. This patch tries to reduce such possible cases. The fix is simple -- we just read once, store it to a local variable and use it for the rest calculations. The READ_ONCE() macro is used for it in order to avoid the ill-effect by possible compiler optimizations. Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index e8131c060c86..e76d55a4d1b2 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -65,15 +65,16 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram if (runtime->silence_size < runtime->boundary) { snd_pcm_sframes_t noise_dist, n; - if (runtime->silence_start != runtime->control->appl_ptr) { - n = runtime->control->appl_ptr - runtime->silence_start; + snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr); + if (runtime->silence_start != appl_ptr) { + n = appl_ptr - runtime->silence_start; if (n < 0) n += runtime->boundary; if ((snd_pcm_uframes_t)n < runtime->silence_filled) runtime->silence_filled -= n; else runtime->silence_filled = 0; - runtime->silence_start = runtime->control->appl_ptr; + runtime->silence_start = appl_ptr; } if (runtime->silence_filled >= runtime->buffer_size) return; @@ -2203,7 +2204,9 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, continue; /* draining */ } frames = size > avail ? avail : size; - cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; + appl_ptr = READ_ONCE(runtime->control->appl_ptr); + appl_ofs = appl_ptr % runtime->buffer_size; + cont = runtime->buffer_size - appl_ofs; if (frames > cont) frames = cont; if (snd_BUG_ON(!frames)) { @@ -2211,8 +2214,6 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, 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); -- cgit v1.2.3