[alsa-devel] [PATCH RFC 00/21] ALSA: pcm: add tracepoints for PCM params operation
Hi,
In the last Audio Mini Conference held with Linux Plumber conference 2016[1], I mentioned about tracepoints for PCM params operation. This patchset is for the idea.
In ALSA PCM interface, applications can get hardware capability by ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE/SNDRV_PCM_IOCT_HW_PARAMS in a shape of 'struct snd_pcm_hw_params'. In kernel side, relevant processing is somewhat complicated and developers sometimes have hard time to debug drivers for PCM constraints and rules.
This patchset adds tracepoints for hw_params operations. When CONFIG_SND_DEBUG is enabled, you can see 'trace_hw_params_mask/trace_hw_params_interval' events of 'snd_pcm' subsystem. When applications execute ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE/SNDRV_PCM_IOCTL_HW_PARAMS, these events are probed. Developers can get how many PCM rules are added into runtime of PCM substream and which rule changed which parameters.
This patchset also includes some improvements. The last three commits brings small changes to kernel/userspace interface for error handling.
I'm happy to receive your comment for this patchset. For your information, low level application of SNDRV_PCM_IOCTL_HW_REFINE operation is available in my github repository[2].
[1] You can see minutes. http://mailman.alsa-project.org/pipermail/alsa-devel/2016-November/114279.ht... [2] refine-pcm-param.c https://github.com/takaswie/alsa-ioctl-test
Takashi Sakamoto (21): ALSA: pcm: add a helper function to constrain mask-type parameters ALSA: pcm: add a helper function to constrain interval-type parameters ALSA: pcm: add a helper function to apply parameter rules ALSA: pcm: tracepoints for refining PCM parameters ALSA: pcm: enable parameter tracepoints at CONFIG_SND_DEBUG only ALSA: pcm: use goto statement instead of while statement to reduce indentation ALSA: pcm: remove function local variable with alternative evaluation ALSA: pcm: adaption of code formatting ALSA: pcm: add comment about application of rule to PCM parameters ALSA: pcm: constify function local and read-only table ALSA: pcm: localize snd_pcm_hw_params_choose() ALSA: pcm: add tracepoints for selection process of hardware parameters at SNDRV_PCM_IOCTL_HW_PARAMS ALSA: pcm: use helper functions to check whether parameters are determined ALSA: pcm: use helper function to refer parameter as read-only ALSA: pcm: add const qualifier for read-only table for sampling rate ALSA: pcm/oss: refer to parameters instead of copying to reduce usage of kernel stack ALSA: pcm: check type of parameter in added rule ALSA: pcm: calculate non-mask/non-interval parameters always when possible ALSA: pcm: move fixup of info flag after selecting single parameters ALSA: pcm: return error immediately for parameters refinement ALSA: pcm: return error immediately at refine ioctl
include/sound/pcm.h | 1 - sound/core/Makefile | 1 + sound/core/oss/pcm_oss.c | 20 +- sound/core/oss/pcm_plugin.c | 5 +- sound/core/oss/pcm_plugin.h | 2 +- sound/core/pcm_drm_eld.c | 8 +- sound/core/pcm_lib.c | 55 +---- sound/core/pcm_native.c | 485 ++++++++++++++++++++++++++++-------------- sound/core/pcm_params_trace.h | 142 +++++++++++++ 9 files changed, 494 insertions(+), 225 deletions(-) create mode 100644 sound/core/pcm_params_trace.h
Application of constraints to mask-type parameters for PCM substream is done in a call of snd_pcm_hw_refine(), while the function includes much codes and is not enough friendly to readers.
This commit splits the codes to a separated function so that readers can get it easily. I leave desicion into compilers to merge the function into its callee.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 52 +++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 19 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 13dec5e..e5135e6 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -278,27 +278,12 @@ static const char * const snd_pcm_hw_param_names[] = { }; #endif
-int snd_pcm_hw_refine(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int constrain_mask_params(struct snd_pcm_hw_constraints *constrs, + struct snd_pcm_hw_params *params) { + struct snd_mask *m; unsigned int k; - struct snd_pcm_hardware *hw; - struct snd_interval *i = NULL; - struct snd_mask *m = NULL; - struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; - unsigned int rstamps[constrs->rules_num]; - unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; - unsigned int stamp = 2; - int changed, again; - - params->info = 0; - params->fifo_size = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) - params->msbits = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { - params->rate_num = 0; - params->rate_den = 0; - } + int changed;
for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { m = hw_param_mask(params, k); @@ -320,6 +305,35 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, return changed; }
+ return 0; +} + +int snd_pcm_hw_refine(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int k; + struct snd_pcm_hardware *hw; + struct snd_mask *m = NULL; + struct snd_interval *i = NULL; + struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; + unsigned int rstamps[constrs->rules_num]; + unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; + unsigned int stamp = 2; + int changed, again; + + params->info = 0; + params->fifo_size = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) + params->msbits = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { + params->rate_num = 0; + params->rate_den = 0; + } + + changed = constrain_mask_params(constrs, params); + if (changed < 0) + return changed; + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { i = hw_param_interval(params, k); if (snd_interval_empty(i))
Application of constraints to interval-type parameters for PCM substream is done in a call of snd_pcm_hw_refine(), while the function includes much codes and is not enough friendly to readers.
This commit splits the codes to a separated function so that readers can get it easily. I leave desicion into compilers to merge the function into its callee.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 60 ++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 23 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index e5135e6..b828e94 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -308,31 +308,12 @@ static int constrain_mask_params(struct snd_pcm_hw_constraints *constrs, return 0; }
-int snd_pcm_hw_refine(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int constrain_interval_params(struct snd_pcm_hw_constraints *constrs, + struct snd_pcm_hw_params *params) { + struct snd_interval *i; unsigned int k; - struct snd_pcm_hardware *hw; - struct snd_mask *m = NULL; - struct snd_interval *i = NULL; - struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; - unsigned int rstamps[constrs->rules_num]; - unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; - unsigned int stamp = 2; - int changed, again; - - params->info = 0; - params->fifo_size = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) - params->msbits = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { - params->rate_num = 0; - params->rate_den = 0; - } - - changed = constrain_mask_params(constrs, params); - if (changed < 0) - return changed; + int changed;
for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { i = hw_param_interval(params, k); @@ -365,6 +346,39 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, return changed; }
+ return 0; +} + +int snd_pcm_hw_refine(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int k; + struct snd_pcm_hardware *hw; + struct snd_mask *m = NULL; + struct snd_interval *i = NULL; + struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; + unsigned int rstamps[constrs->rules_num]; + unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; + unsigned int stamp = 2; + int changed, again; + + params->info = 0; + params->fifo_size = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) + params->msbits = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { + params->rate_num = 0; + params->rate_den = 0; + } + + changed = constrain_mask_params(constrs, params); + if (changed < 0) + return changed; + + changed = constrain_interval_params(constrs, params); + if (changed < 0) + return changed; + for (k = 0; k < constrs->rules_num; k++) rstamps[k] = 0; for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
Application of rules to parameters of PCM substream is done in a call of snd_pcm_hw_refine(), while the function includes much codes and is not enough friendly to readers.
This commit splits the codes to a separated function so that readers can get it easily. I leave desicion into compilers to merge the function into its callee.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 62 +++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 22 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index b828e94..78afdf1 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -349,35 +349,19 @@ static int constrain_interval_params(struct snd_pcm_hw_constraints *constrs, return 0; }
-int snd_pcm_hw_refine(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int constrain_params_by_rules(struct snd_pcm_hw_constraints *constrs, + struct snd_pcm_hw_params *params) { unsigned int k; - struct snd_pcm_hardware *hw; - struct snd_mask *m = NULL; - struct snd_interval *i = NULL; - struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; unsigned int rstamps[constrs->rules_num]; unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; unsigned int stamp = 2; int changed, again;
- params->info = 0; - params->fifo_size = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) - params->msbits = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { - params->rate_num = 0; - params->rate_den = 0; - } - - changed = constrain_mask_params(constrs, params); - if (changed < 0) - return changed; - - changed = constrain_interval_params(constrs, params); - if (changed < 0) - return changed; +#ifdef RULES_DEBUG + struct snd_mask *m = NULL; + struct snd_interval *i = NULL; +#endif
for (k = 0; k < constrs->rules_num; k++) rstamps[k] = 0; @@ -445,6 +429,40 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, stamp++; } } while (again); + + return 0; +} + +int snd_pcm_hw_refine(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_hardware *hw; + struct snd_mask *m = NULL; + struct snd_interval *i = NULL; + struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; + int changed; + + params->info = 0; + params->fifo_size = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) + params->msbits = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { + params->rate_num = 0; + params->rate_den = 0; + } + + changed = constrain_mask_params(constrs, params); + if (changed < 0) + return changed; + + changed = constrain_interval_params(constrs, params); + if (changed < 0) + return changed; + + changed = constrain_params_by_rules(constrs, params); + if (changed < 0) + return changed; + if (!params->msbits) { i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); if (snd_interval_single(i))
When working for devices which have complicated mode for its data transmission or which consists of several components, developers are likely to use rules of parameters of PCM substream. However, there's no infrastructure to assist their work.
In old days, ALSA PCM core got a local 'RULES_DEBUG' macro to debug refinement of parameters for PCM substream. Although this is merely a makeshift, with some modifications, we get the infrastructure.
This commit is for the purpose. Refinement of mask/interval type of PCM parameters is recorded as tracepoint events as 'hw_params_mask' and 'hw_params_interval' on existent 'snd_pcm' subsystem.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/Makefile | 1 + sound/core/pcm_native.c | 146 ++++++++++++++++-------------------------- sound/core/pcm_params_trace.h | 142 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+), 90 deletions(-) create mode 100644 sound/core/pcm_params_trace.h
diff --git a/sound/core/Makefile b/sound/core/Makefile index e85d9dd..a8514b3 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -22,6 +22,7 @@ snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
# for trace-points CFLAGS_pcm_lib.o := -I$(src) +CFLAGS_pcm_native.o := -I$(src)
snd-pcm-dmaengine-objs := pcm_dmaengine.o
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 78afdf1..5a2703e 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -37,6 +37,9 @@ #include <sound/minors.h> #include <linux/uio.h>
+#define CREATE_TRACE_POINTS +#include "pcm_params_trace.h" + /* * Compatibility */ @@ -255,50 +258,31 @@ static bool hw_support_mmap(struct snd_pcm_substream *substream) return true; }
-#undef RULES_DEBUG - -#ifdef RULES_DEBUG -#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v -static const char * const snd_pcm_hw_param_names[] = { - HW_PARAM(ACCESS), - HW_PARAM(FORMAT), - HW_PARAM(SUBFORMAT), - HW_PARAM(SAMPLE_BITS), - HW_PARAM(FRAME_BITS), - HW_PARAM(CHANNELS), - HW_PARAM(RATE), - HW_PARAM(PERIOD_TIME), - HW_PARAM(PERIOD_SIZE), - HW_PARAM(PERIOD_BYTES), - HW_PARAM(PERIODS), - HW_PARAM(BUFFER_TIME), - HW_PARAM(BUFFER_SIZE), - HW_PARAM(BUFFER_BYTES), - HW_PARAM(TICK_TIME), -}; -#endif - -static int constrain_mask_params(struct snd_pcm_hw_constraints *constrs, +static int constrain_mask_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_constraints *constrs, struct snd_pcm_hw_params *params) { struct snd_mask *m; unsigned int k; int changed;
+ struct snd_mask __maybe_unused old_mask; + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { m = hw_param_mask(params, k); if (snd_mask_empty(m)) return -EINVAL; if (!(params->rmask & (1 << k))) continue; -#ifdef RULES_DEBUG - pr_debug("%s = ", snd_pcm_hw_param_names[k]); - pr_cont("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); -#endif + + /* Keep the parameter to trace. */ + if (trace_hw_params_mask_enabled()) + old_mask = *m; + changed = snd_mask_refine(m, constrs_mask(constrs, k)); -#ifdef RULES_DEBUG - pr_cont("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); -#endif + + trace_hw_params_mask(substream, k, -1, &old_mask, m); + if (changed) params->cmask |= 1 << k; if (changed < 0) @@ -308,38 +292,31 @@ static int constrain_mask_params(struct snd_pcm_hw_constraints *constrs, return 0; }
-static int constrain_interval_params(struct snd_pcm_hw_constraints *constrs, +static int constrain_interval_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_constraints *constrs, struct snd_pcm_hw_params *params) { struct snd_interval *i; unsigned int k; int changed;
+ struct snd_interval __maybe_unused old_interval; + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { i = hw_param_interval(params, k); if (snd_interval_empty(i)) return -EINVAL; if (!(params->rmask & (1 << k))) continue; -#ifdef RULES_DEBUG - pr_debug("%s = ", snd_pcm_hw_param_names[k]); - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - pr_cont(" -> "); -#endif + + /* Keep the parameter to trace. */ + if (trace_hw_params_interval_enabled()) + old_interval = *i; + changed = snd_interval_refine(i, constrs_interval(constrs, k)); -#ifdef RULES_DEBUG - if (i->empty) - pr_cont("empty\n"); - else - pr_cont("%c%u %u%c\n", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); -#endif + + trace_hw_params_interval(substream, k, -1, &old_interval, i); + if (changed) params->cmask |= 1 << k; if (changed < 0) @@ -349,7 +326,8 @@ static int constrain_interval_params(struct snd_pcm_hw_constraints *constrs, return 0; }
-static int constrain_params_by_rules(struct snd_pcm_hw_constraints *constrs, +static int constrain_params_by_rules(struct snd_pcm_substream *substream, + struct snd_pcm_hw_constraints *constrs, struct snd_pcm_hw_params *params) { unsigned int k; @@ -358,10 +336,8 @@ static int constrain_params_by_rules(struct snd_pcm_hw_constraints *constrs, unsigned int stamp = 2; int changed, again;
-#ifdef RULES_DEBUG - struct snd_mask *m = NULL; - struct snd_interval *i = NULL; -#endif + struct snd_interval __maybe_unused old_interval; + struct snd_mask __maybe_unused old_mask;
for (k = 0; k < constrs->rules_num; k++) rstamps[k] = 0; @@ -383,41 +359,31 @@ static int constrain_params_by_rules(struct snd_pcm_hw_constraints *constrs, } if (!doit) continue; -#ifdef RULES_DEBUG - pr_debug("Rule %d [%p]: ", k, r->func); - if (r->var >= 0) { - pr_cont("%s = ", snd_pcm_hw_param_names[r->var]); - if (hw_is_mask(r->var)) { - m = hw_param_mask(params, r->var); - pr_cont("%x", *m->bits); - } else { - i = hw_param_interval(params, r->var); - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - } + + /* Keep old parameter to trace. */ + if (trace_hw_params_mask_enabled()) { + if (hw_is_mask(r->var)) + old_mask = *hw_param_mask(params, r->var); } -#endif + if (trace_hw_params_interval_enabled()) { + if (hw_is_interval(r->var)) + old_interval = *hw_param_interval(params, r->var); + } + changed = r->func(params, r); -#ifdef RULES_DEBUG - if (r->var >= 0) { - pr_cont(" -> "); - if (hw_is_mask(r->var)) - pr_cont("%x", *m->bits); - else { - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - } + + /* Trace the parameter. */ + if (hw_is_mask(r->var)) { + trace_hw_params_mask(substream, r->var, k, + &old_mask, + hw_param_mask(params, r->var)); } - pr_cont("\n"); -#endif + if (hw_is_interval(r->var)) { + trace_hw_params_interval(substream, r->var, k, + &old_interval, + hw_param_interval(params, r->var)); + } + rstamps[k] = stamp; if (changed && r->var >= 0) { params->cmask |= (1 << r->var); @@ -451,15 +417,15 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, params->rate_den = 0; }
- changed = constrain_mask_params(constrs, params); + changed = constrain_mask_params(substream, constrs, params); if (changed < 0) return changed;
- changed = constrain_interval_params(constrs, params); + changed = constrain_interval_params(substream, constrs, params); if (changed < 0) return changed;
- changed = constrain_params_by_rules(constrs, params); + changed = constrain_params_by_rules(substream, constrs, params); if (changed < 0) return changed;
diff --git a/sound/core/pcm_params_trace.h b/sound/core/pcm_params_trace.h new file mode 100644 index 0000000..53a2c0f --- /dev/null +++ b/sound/core/pcm_params_trace.h @@ -0,0 +1,142 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM snd_pcm + +#if !defined(_PCM_PARAMS_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _PCM_PARAMS_TRACE_H + +#include <linux/tracepoint.h> + +#define HW_PARAM_ENTRY(param) {SNDRV_PCM_HW_PARAM_##param, #param} +#define hw_param_labels \ + HW_PARAM_ENTRY(ACCESS), \ + HW_PARAM_ENTRY(FORMAT), \ + HW_PARAM_ENTRY(SUBFORMAT), \ + HW_PARAM_ENTRY(SAMPLE_BITS), \ + HW_PARAM_ENTRY(FRAME_BITS), \ + HW_PARAM_ENTRY(CHANNELS), \ + HW_PARAM_ENTRY(RATE), \ + HW_PARAM_ENTRY(PERIOD_TIME), \ + HW_PARAM_ENTRY(PERIOD_SIZE), \ + HW_PARAM_ENTRY(PERIOD_BYTES), \ + HW_PARAM_ENTRY(PERIODS), \ + HW_PARAM_ENTRY(BUFFER_TIME), \ + HW_PARAM_ENTRY(BUFFER_SIZE), \ + HW_PARAM_ENTRY(BUFFER_BYTES), \ + HW_PARAM_ENTRY(TICK_TIME) + +TRACE_EVENT(hw_params_mask, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_hw_param_t type, int index, const struct snd_mask *prev, const struct snd_mask *curr), + TP_ARGS(substream, type, index, prev, curr), + TP_STRUCT__entry( + __field(int, card) + __field(int, device) + __field(int, subdevice) + __field(int, direction) + __field(snd_pcm_hw_param_t, type) + __field(int, index) + __field(int, total) + __array(__u32, prev_bits, 8) + __array(__u32, curr_bits, 8) + ), + TP_fast_assign( + __entry->card = substream->pcm->card->number; + __entry->device = substream->pcm->device; + __entry->subdevice = substream->number; + __entry->direction = substream->stream; + __entry->type = type; + __entry->index = index; + __entry->total = substream->runtime->hw_constraints.rules_num; + memcpy(__entry->prev_bits, prev->bits, sizeof(__u32) * 8); + memcpy(__entry->curr_bits, curr->bits, sizeof(__u32) * 8); + ), + TP_printk("%d,%d,%d,%d: %03d/%03d: %08x%08x%08x%08x: %08x%08x%08x%08x: %s", + __entry->card, + __entry->device, + __entry->subdevice, + __entry->direction, + __entry->index, + __entry->total, + __entry->prev_bits[3], __entry->prev_bits[2], + __entry->prev_bits[1], __entry->prev_bits[0], + __entry->curr_bits[3], __entry->curr_bits[2], + __entry->curr_bits[1], __entry->curr_bits[0], + __print_symbolic(__entry->type, hw_param_labels) + ) +); + +TRACE_EVENT(hw_params_interval, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_hw_param_t type, int index, const struct snd_interval *prev, const struct snd_interval *curr), + TP_ARGS(substream, type, index, prev, curr), + TP_STRUCT__entry( + __field(int, card) + __field(int, device) + __field(int, subdevice) + __field(int, direction) + __field(snd_pcm_hw_param_t, type) + __field(int, index) + __field(int, total) + __field(unsigned int, prev_min) + __field(unsigned int, prev_max) + __field(unsigned int, prev_openmin) + __field(unsigned int, prev_openmax) + __field(unsigned int, prev_integer) + __field(unsigned int, prev_empty) + __field(unsigned int, curr_min) + __field(unsigned int, curr_max) + __field(unsigned int, curr_openmin) + __field(unsigned int, curr_openmax) + __field(unsigned int, curr_integer) + __field(unsigned int, curr_empty) + ), + TP_fast_assign( + __entry->card = substream->pcm->card->number; + __entry->device = substream->pcm->device; + __entry->subdevice = substream->number; + __entry->direction = substream->stream; + __entry->type = type; + __entry->index = index; + __entry->total = substream->runtime->hw_constraints.rules_num; + __entry->prev_min = prev->min; + __entry->prev_max = prev->max; + __entry->prev_openmin = prev->openmin; + __entry->prev_openmax = prev->openmax; + __entry->prev_integer = prev->integer; + __entry->prev_empty = prev->empty; + __entry->curr_min = curr->min; + __entry->curr_max = curr->max; + __entry->curr_openmin = curr->openmin; + __entry->curr_openmax = curr->openmax; + __entry->curr_integer = curr->integer; + __entry->curr_empty = curr->empty; + ), + TP_printk("%d,%d,%d,%d: %03d/%03d: %d %s%u, %u%s %d: %d %s%u, %u%s %d: %s", + __entry->card, + __entry->device, + __entry->subdevice, + __entry->direction, + __entry->index, + __entry->total, + __entry->prev_empty, + __entry->prev_openmin ? "(" : "[", + __entry->prev_min, + __entry->prev_max, + __entry->prev_openmax ? ")" : "]", + __entry->prev_integer, + __entry->curr_empty, + __entry->curr_openmin ? "(" : "[", + __entry->curr_min, + __entry->curr_max, + __entry->curr_openmax ? ")" : "]", + __entry->curr_integer, + __print_symbolic(__entry->type, hw_param_labels) + ) +); + +#endif /* _PCM_PARAMS_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE pcm_params_trace +#include <trace/define_trace.h>
In a previous commit, tracepoints are added for PCM parameter processing. As long as I know, this implementation increases size of relocatable object by 135%. For vendors who are conscious of memory footprint, it brings apparent disadvantage.
This commit uses CONFIG_SND_DEBUG option to enable/disable the tracepoints.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 5a2703e..3fd2a63 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -37,7 +37,9 @@ #include <sound/minors.h> #include <linux/uio.h>
+#ifdef CONFIG_SND_DEBUG #define CREATE_TRACE_POINTS +#endif #include "pcm_params_trace.h"
/*
In a process to calculate parameters of PCM substream, application of all rules is iterated several times till parameter dependencies are satisfied. In current implementation, two loops are used for the design, however this brings two-level indentation and decline readability.
This commit attempts to reduce the indentation by using goto statement, instead of outer while loop.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 95 +++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 46 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 3fd2a63..4de9498 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -345,58 +345,61 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, rstamps[k] = 0; for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; - do { - again = 0; - for (k = 0; k < constrs->rules_num; k++) { - struct snd_pcm_hw_rule *r = &constrs->rules[k]; - unsigned int d; - int doit = 0; - if (r->cond && !(r->cond & params->flags)) - continue; - for (d = 0; r->deps[d] >= 0; d++) { - if (vstamps[r->deps[d]] > rstamps[k]) { - doit = 1; - break; - } - } - if (!doit) - continue;
- /* Keep old parameter to trace. */ - if (trace_hw_params_mask_enabled()) { - if (hw_is_mask(r->var)) - old_mask = *hw_param_mask(params, r->var); - } - if (trace_hw_params_interval_enabled()) { - if (hw_is_interval(r->var)) - old_interval = *hw_param_interval(params, r->var); +retry: + again = 0; + for (k = 0; k < constrs->rules_num; k++) { + struct snd_pcm_hw_rule *r = &constrs->rules[k]; + unsigned int d; + int doit = 0; + if (r->cond && !(r->cond & params->flags)) + continue; + for (d = 0; r->deps[d] >= 0; d++) { + if (vstamps[r->deps[d]] > rstamps[k]) { + doit = 1; + break; } + } + if (!doit) + continue;
- changed = r->func(params, r); + /* Keep old parameter to trace. */ + if (trace_hw_params_mask_enabled()) { + if (hw_is_mask(r->var)) + old_mask = *hw_param_mask(params, r->var); + } + if (trace_hw_params_interval_enabled()) { + if (hw_is_interval(r->var)) + old_interval = *hw_param_interval(params, r->var); + }
- /* Trace the parameter. */ - if (hw_is_mask(r->var)) { - trace_hw_params_mask(substream, r->var, k, - &old_mask, - hw_param_mask(params, r->var)); - } - if (hw_is_interval(r->var)) { - trace_hw_params_interval(substream, r->var, k, - &old_interval, - hw_param_interval(params, r->var)); - } + changed = r->func(params, r);
- rstamps[k] = stamp; - if (changed && r->var >= 0) { - params->cmask |= (1 << r->var); - vstamps[r->var] = stamp; - again = 1; - } - if (changed < 0) - return changed; - stamp++; + /* Trace the parameter. */ + if (hw_is_mask(r->var)) { + trace_hw_params_mask(substream, r->var, k, + &old_mask, + hw_param_mask(params, r->var)); + } + if (hw_is_interval(r->var)) { + trace_hw_params_interval(substream, r->var, k, + &old_interval, + hw_param_interval(params, r->var)); } - } while (again); + + rstamps[k] = stamp; + if (changed && r->var >= 0) { + params->cmask |= (1 << r->var); + vstamps[r->var] = stamp; + again = 1; + } + if (changed < 0) + return changed; + stamp++; + } + + if (again) + goto retry;
return 0; }
A local variable is used to judge whether a parameter should be handled due to reverse dependency of the other rules. However, this can be obsoleted by check of a sentinel in dependency array.
This commit removes the local variable and check the sentinel to reduce stack usage.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4de9498..39ead67 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -351,16 +351,13 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, for (k = 0; k < constrs->rules_num; k++) { struct snd_pcm_hw_rule *r = &constrs->rules[k]; unsigned int d; - int doit = 0; if (r->cond && !(r->cond & params->flags)) continue; for (d = 0; r->deps[d] >= 0; d++) { - if (vstamps[r->deps[d]] > rstamps[k]) { - doit = 1; + if (vstamps[r->deps[d]] > rstamps[k]) break; - } } - if (!doit) + if (r->deps[d] < 0) continue;
/* Keep old parameter to trace. */
This commit modifies current code according to fashion in kernel land: - within 80 characters per line - indentation of function arguments with line break - removal of trailing linear white spaces - use int type variable for loop counter
For readability: - use bool type variable instead of int type variable assigned to 0/1 - move variable definition from loop to top of the function definition
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 39ead67..f644fbe 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -265,12 +265,13 @@ static int constrain_mask_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_mask *m; - unsigned int k; + int k; int changed;
struct snd_mask __maybe_unused old_mask;
- for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; + k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { m = hw_param_mask(params, k); if (snd_mask_empty(m)) return -EINVAL; @@ -299,12 +300,13 @@ static int constrain_interval_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_interval *i; - unsigned int k; + int k; int changed;
struct snd_interval __maybe_unused old_interval;
- for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; + k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { i = hw_param_interval(params, k); if (snd_interval_empty(i)) return -EINVAL; @@ -332,25 +334,27 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, struct snd_pcm_hw_constraints *constrs, struct snd_pcm_hw_params *params) { - unsigned int k; + int k; unsigned int rstamps[constrs->rules_num]; unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; - unsigned int stamp = 2; - int changed, again; + unsigned int stamp; + struct snd_pcm_hw_rule *r; + unsigned int d; + bool again; + int changed;
struct snd_interval __maybe_unused old_interval; struct snd_mask __maybe_unused old_mask;
for (k = 0; k < constrs->rules_num; k++) rstamps[k] = 0; - for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) + for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; - + stamp = 2; retry: - again = 0; + again = false; for (k = 0; k < constrs->rules_num; k++) { - struct snd_pcm_hw_rule *r = &constrs->rules[k]; - unsigned int d; + r = &constrs->rules[k]; if (r->cond && !(r->cond & params->flags)) continue; for (d = 0; r->deps[d] >= 0; d++) { @@ -388,7 +392,7 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, if (changed && r->var >= 0) { params->cmask |= (1 << r->var); vstamps[r->var] = stamp; - again = 1; + again = true; } if (changed < 0) return changed; @@ -407,7 +411,7 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hardware *hw; struct snd_mask *m = NULL; struct snd_interval *i = NULL; - struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; + struct snd_pcm_hw_constraints *constrs; int changed;
params->info = 0; @@ -419,6 +423,7 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, params->rate_den = 0; }
+ constrs = &substream->runtime->hw_constraints; changed = constrain_mask_params(substream, constrs, params); if (changed < 0) return changed;
Drivers add rules of parameters to runtime of PCM substream, when applications open ALSA PCM character device. When applications call ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE or SNDRV_PCM_IOCTL_HW_PARAMS, the rules are applied to the parameters and return the result to user space.
The rule can have dependency between parameters. Additionally, it can have condition flags about application of rules. Userspace applications can indicate the flags to suppress change of parameters.
This commit attempts to describe the mechanism.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index f644fbe..7044ae5 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -275,6 +275,8 @@ static int constrain_mask_params(struct snd_pcm_substream *substream, m = hw_param_mask(params, k); if (snd_mask_empty(m)) return -EINVAL; + + /* This parameter is not requested to change by application. */ if (!(params->rmask & (1 << k))) continue;
@@ -286,6 +288,7 @@ static int constrain_mask_params(struct snd_pcm_substream *substream,
trace_hw_params_mask(substream, k, -1, &old_mask, m);
+ /* Set corresponding flag so that applications get it. */ if (changed) params->cmask |= 1 << k; if (changed < 0) @@ -310,6 +313,8 @@ static int constrain_interval_params(struct snd_pcm_substream *substream, i = hw_param_interval(params, k); if (snd_interval_empty(i)) return -EINVAL; + + /* This parameter is not requested to change by application. */ if (!(params->rmask & (1 << k))) continue;
@@ -321,6 +326,7 @@ static int constrain_interval_params(struct snd_pcm_substream *substream,
trace_hw_params_interval(substream, k, -1, &old_interval, i);
+ /* Set corresponding flag so that applications get it. */ if (changed) params->cmask |= 1 << k; if (changed < 0) @@ -346,17 +352,54 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, struct snd_interval __maybe_unused old_interval; struct snd_mask __maybe_unused old_mask;
+ /* + * Each application of rule has own sequence number. + * + * Each member of 'rstamps' array represents the sequence number of + * recent application of corresponding rule. + */ for (k = 0; k < constrs->rules_num; k++) rstamps[k] = 0; + + /* + * Each member of 'vstamps' array represents the sequence number of + * recent application of rule in which corresponding parameters were + * changed. + * + * In initial state, elements corresponding to parameters requested by + * application is 1. For unrequested parameters, corresponding members + * have 0 so that the parameters are never changed anymore. + */ for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; + + /* Due to the above design, actual sequence number starts at 2. */ stamp = 2; retry: + /* Apply all rules in order. */ again = false; for (k = 0; k < constrs->rules_num; k++) { r = &constrs->rules[k]; + + /* + * Check condition bits of this rule. When the rule has + * some condition bits, parameter without the bits is + * never processed. SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP + * is an example of the condition bits. + */ if (r->cond && !(r->cond & params->flags)) continue; + + /* + * The 'deps' array includes maximum three dependencies + * to SNDRV_PCM_HW_PARAM_XXXs for this rule. The fourth + * member of this array is a sentinel and should be + * negative value. + * + * This rule should be processed in this time when dependent + * parameters were changed at former applications of the other + * rules. + */ for (d = 0; r->deps[d] >= 0; d++) { if (vstamps[r->deps[d]] > rstamps[k]) break; @@ -389,6 +432,12 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, }
rstamps[k] = stamp; + + /* + * When some parameters are changed, notify it to applications + * in user space by returned bits, then preparing for next + * iteration. + */ if (changed && r->var >= 0) { params->cmask |= (1 << r->var); vstamps[r->var] = stamp; @@ -396,9 +445,11 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, } if (changed < 0) return changed; + stamp++; }
+ /* Iterate to evaluate all rules till no parameters are changed. */ if (again) goto retry;
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 o-takashi@sakamocchi.jp --- sound/core/pcm_lib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 5088d4b..0ab6f86 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1733,7 +1733,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, @@ -1744,7 +1744,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)
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 o-takashi@sakamocchi.jp --- include/sound/pcm.h | 1 - sound/core/pcm_lib.c | 40 ---------------------------------------- sound/core/pcm_native.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 41 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 361749e..d68b353 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -984,7 +984,6 @@ 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);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 0ab6f86..a62f1f4 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1718,46 +1718,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_native.c b/sound/core/pcm_native.c index 7044ae5..2c5a4d9 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -526,6 +526,46 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
EXPORT_SYMBOL(snd_pcm_hw_refine);
+/** + * 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_refine_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params __user * _params) {
Results of ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE and SNDRV_PCM_IOCTL_HW_PARAMS are different, because the latter has single value for several parameters; e.g. channels of PCM substream. Selection of the single value is done independently of application of constraints and rules. It's helpful for developers to trace the selection process
This commit adds tracepoints to snd_pcm_hw_params_choose() for the purpose.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 2c5a4d9..47af1a8 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -555,11 +555,35 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, const int *v; int err;
+ struct snd_interval __maybe_unused old_interval; + struct snd_mask __maybe_unused old_mask; + for (v = vars; *v != -1; v++) { + /* Keep old parameter to trace. */ + if (trace_hw_params_mask_enabled()) { + if (hw_is_mask(*v)) + old_mask = *hw_param_mask(params, *v); + } + if (trace_hw_params_interval_enabled()) { + if (hw_is_interval(*v)) + old_interval = *hw_param_interval(params, *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); + + /* Trace the parameter. */ + if (hw_is_mask(*v)) { + trace_hw_params_mask(pcm, *v, -1, &old_mask, + hw_param_mask(params, *v)); + } + if (hw_is_interval(*v)) { + trace_hw_params_interval(pcm, *v, -1, &old_interval, + hw_param_interval(params, *v)); + } + if (snd_BUG_ON(err < 0)) return err; }
A commit 8bea869c5e56 ("ALSA: PCM midlevel: improve fifo_size handling") allows drivers to implement calculation of fifo size in parameter structure. This calculation runs only when two of the other parameters have single value.
In ALSA PCM core, there're some helper functions for the case. This commit applies the functions instead of value comparison.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 47af1a8..028d6a5 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -512,8 +512,7 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!params->fifo_size) { m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (snd_mask_min(m) == snd_mask_max(m) && - snd_interval_min(i) == snd_interval_max(i)) { + if (snd_mask_single(m) && snd_interval_single(i)) { changed = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_FIFO_SIZE, params); if (changed < 0)
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 o-takashi@sakamocchi.jp --- sound/core/oss/pcm_oss.c | 4 ++-- sound/core/pcm_drm_eld.c | 8 ++++---- sound/core/pcm_lib.c | 3 ++- sound/core/pcm_native.c | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 36baf96..2a47351 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -799,7 +799,7 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, static int choose_rate(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, unsigned int best_rate) { - struct snd_interval *it; + const struct snd_interval *it; struct snd_pcm_hw_params *save; unsigned int rate, prev;
@@ -807,7 +807,7 @@ static int choose_rate(struct snd_pcm_substream *substream, if (save == NULL) return -ENOMEM; *save = *params; - it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE); + it = hw_param_interval_c(save, SNDRV_PCM_HW_PARAM_RATE);
/* try multiples of the best rate */ rate = best_rate; diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c index e70379f..9881d08 100644 --- a/sound/core/pcm_drm_eld.c +++ b/sound/core/pcm_drm_eld.c @@ -29,13 +29,13 @@ static int eld_limit_rates(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *r = hw_param_interval(params, rule->var); - struct snd_interval *c; + const struct snd_interval *c; unsigned int rate_mask = 7, i; const u8 *sad, *eld = rule->private;
sad = drm_eld_sad(eld); if (sad) { - c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) { unsigned max_channels = sad_max_channels(sad); @@ -57,7 +57,7 @@ static int eld_limit_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *c = hw_param_interval(params, rule->var); - struct snd_interval *r; + const struct snd_interval *r; struct snd_interval t = { .min = 1, .max = 2, .integer = 1, }; unsigned int i; const u8 *sad, *eld = rule->private; @@ -67,7 +67,7 @@ static int eld_limit_channels(struct snd_pcm_hw_params *params, unsigned int rate_mask = 0;
/* Convert the rate interval to a mask */ - r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); for (i = 0; i < ARRAY_SIZE(eld_rates); i++) if (r->min <= eld_rates[i] && r->max >= eld_rates[i]) rate_mask |= BIT(i); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a62f1f4..9659fa6 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; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 028d6a5..65133c2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2073,7 +2073,8 @@ static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { unsigned int k; - struct snd_interval *i = hw_param_interval(params, rule->deps[0]); + const struct snd_interval *i = + hw_param_interval_c(params, rule->deps[0]); struct snd_mask m; struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); snd_mask_any(&m);
There's a read-only table for each sampling rate, while it doesn't have const qualifier and can be modified.
This commit add the qualifier. As a result, a symbol for the table moves from .data section to .rodata.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 65133c2..ad71eb2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2120,8 +2120,10 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, #error "Change this table" #endif
-static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, - 48000, 64000, 88200, 96000, 176400, 192000 }; +static const unsigned int rates[] = { + 5512, 8000, 11025, 16000, 22050, 32000, 44100, + 48000, 64000, 88200, 96000, 176400, 192000 +};
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { .count = ARRAY_SIZE(rates),
Some functions in compatibility layer for Open Sound System interface has local variable to copy some parameters in runtime of PCM substream, while this can be replaced with reference of pointers to parameter itself. This brings an advantage to reduce usage of kernel stack.
This commit applies this idea.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/oss/pcm_oss.c | 16 ++++++++-------- sound/core/oss/pcm_plugin.c | 5 +++-- sound/core/oss/pcm_plugin.h | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 2a47351..e306f05 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -848,7 +848,7 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, int direct; snd_pcm_format_t format, sformat; int n; - struct snd_mask sformat_mask; + const struct snd_mask *sformat_mask; struct snd_mask mask;
if (trylock) { @@ -891,18 +891,18 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
format = snd_pcm_oss_format_from(runtime->oss.format);
- sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT); + sformat_mask = hw_param_mask_c(sparams, SNDRV_PCM_HW_PARAM_FORMAT); if (direct) sformat = format; else - sformat = snd_pcm_plug_slave_format(format, &sformat_mask); + sformat = snd_pcm_plug_slave_format(format, sformat_mask);
if ((__force int)sformat < 0 || - !snd_mask_test(&sformat_mask, (__force int)sformat)) { + !snd_mask_test(sformat_mask, (__force int)sformat)) { for (sformat = (__force snd_pcm_format_t)0; (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST; sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) { - if (snd_mask_test(&sformat_mask, (__force int)sformat) && + if (snd_mask_test(sformat_mask, (__force int)sformat) && snd_pcm_oss_format_to(sformat) >= 0) break; } @@ -1780,7 +1780,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) int direct; struct snd_pcm_hw_params *params; unsigned int formats = 0; - struct snd_mask format_mask; + const struct snd_mask *format_mask; int fmt;
if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) @@ -1802,12 +1802,12 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) return -ENOMEM; _snd_pcm_hw_params_any(params); err = snd_pcm_hw_refine(substream, params); - format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); kfree(params); if (err < 0) return err; for (fmt = 0; fmt < 32; ++fmt) { - if (snd_mask_test(&format_mask, fmt)) { + if (snd_mask_test(format_mask, fmt)) { int f = snd_pcm_oss_format_to(fmt); if (f >= 0) formats |= f; diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 727ac44..cadc937 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -266,7 +266,8 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc return frames; }
-static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format) +static int snd_pcm_plug_formats(const struct snd_mask *mask, + snd_pcm_format_t format) { struct snd_mask formats = *mask; u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | @@ -309,7 +310,7 @@ static snd_pcm_format_t preferred_formats[] = { };
snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, - struct snd_mask *format_mask) + const struct snd_mask *format_mask) { int i;
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index a5035c2..38e2c14 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -126,7 +126,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *slave_params);
snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, - struct snd_mask *format_mask); + const struct snd_mask *format_mask);
int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin);
On Sun, 14 May 2017 10:57:51 +0200, Takashi Sakamoto wrote:
Some functions in compatibility layer for Open Sound System interface has local variable to copy some parameters in runtime of PCM substream, while this can be replaced with reference of pointers to parameter itself. This brings an advantage to reduce usage of kernel stack.
This commit applies this idea.
Are you sure that this patch won't break anything? Using the local copy is for not changing the values at evaluation before actually trying to set.
thanks,
Takashi
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp
sound/core/oss/pcm_oss.c | 16 ++++++++-------- sound/core/oss/pcm_plugin.c | 5 +++-- sound/core/oss/pcm_plugin.h | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 2a47351..e306f05 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -848,7 +848,7 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, int direct; snd_pcm_format_t format, sformat; int n;
- struct snd_mask sformat_mask;
const struct snd_mask *sformat_mask; struct snd_mask mask;
if (trylock) {
@@ -891,18 +891,18 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
format = snd_pcm_oss_format_from(runtime->oss.format);
- sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
- sformat_mask = hw_param_mask_c(sparams, SNDRV_PCM_HW_PARAM_FORMAT); if (direct) sformat = format; else
sformat = snd_pcm_plug_slave_format(format, &sformat_mask);
sformat = snd_pcm_plug_slave_format(format, sformat_mask);
if ((__force int)sformat < 0 ||
!snd_mask_test(&sformat_mask, (__force int)sformat)) {
for (sformat = (__force snd_pcm_format_t)0; (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST; sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) {!snd_mask_test(sformat_mask, (__force int)sformat)) {
if (snd_mask_test(&sformat_mask, (__force int)sformat) &&
}if (snd_mask_test(sformat_mask, (__force int)sformat) && snd_pcm_oss_format_to(sformat) >= 0) break;
@@ -1780,7 +1780,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) int direct; struct snd_pcm_hw_params *params; unsigned int formats = 0;
- struct snd_mask format_mask;
const struct snd_mask *format_mask; int fmt;
if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
@@ -1802,12 +1802,12 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) return -ENOMEM; _snd_pcm_hw_params_any(params); err = snd_pcm_hw_refine(substream, params);
- format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); kfree(params); if (err < 0) return err; for (fmt = 0; fmt < 32; ++fmt) {
if (snd_mask_test(&format_mask, fmt)) {
if (snd_mask_test(format_mask, fmt)) { int f = snd_pcm_oss_format_to(fmt); if (f >= 0) formats |= f;
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 727ac44..cadc937 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -266,7 +266,8 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc return frames; }
-static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format) +static int snd_pcm_plug_formats(const struct snd_mask *mask,
snd_pcm_format_t format)
{ struct snd_mask formats = *mask; u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | @@ -309,7 +310,7 @@ static snd_pcm_format_t preferred_formats[] = { };
snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
struct snd_mask *format_mask)
const struct snd_mask *format_mask)
{ int i;
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index a5035c2..38e2c14 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -126,7 +126,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *slave_params);
snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
struct snd_mask *format_mask);
const struct snd_mask *format_mask);
int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin);
-- 2.9.3
On Mon, 15 May 2017 11:10:14 +0200, Takashi Iwai wrote:
On Sun, 14 May 2017 10:57:51 +0200, Takashi Sakamoto wrote:
Some functions in compatibility layer for Open Sound System interface has local variable to copy some parameters in runtime of PCM substream, while this can be replaced with reference of pointers to parameter itself. This brings an advantage to reduce usage of kernel stack.
This commit applies this idea.
Are you sure that this patch won't break anything? Using the local copy is for not changing the values at evaluation before actually trying to set.
Never mind, I understand that it's about format_mask and it's consitfied.
Takashi
thanks,
Takashi
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp
sound/core/oss/pcm_oss.c | 16 ++++++++-------- sound/core/oss/pcm_plugin.c | 5 +++-- sound/core/oss/pcm_plugin.h | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 2a47351..e306f05 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -848,7 +848,7 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, int direct; snd_pcm_format_t format, sformat; int n;
- struct snd_mask sformat_mask;
const struct snd_mask *sformat_mask; struct snd_mask mask;
if (trylock) {
@@ -891,18 +891,18 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
format = snd_pcm_oss_format_from(runtime->oss.format);
- sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
- sformat_mask = hw_param_mask_c(sparams, SNDRV_PCM_HW_PARAM_FORMAT); if (direct) sformat = format; else
sformat = snd_pcm_plug_slave_format(format, &sformat_mask);
sformat = snd_pcm_plug_slave_format(format, sformat_mask);
if ((__force int)sformat < 0 ||
!snd_mask_test(&sformat_mask, (__force int)sformat)) {
for (sformat = (__force snd_pcm_format_t)0; (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST; sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) {!snd_mask_test(sformat_mask, (__force int)sformat)) {
if (snd_mask_test(&sformat_mask, (__force int)sformat) &&
}if (snd_mask_test(sformat_mask, (__force int)sformat) && snd_pcm_oss_format_to(sformat) >= 0) break;
@@ -1780,7 +1780,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) int direct; struct snd_pcm_hw_params *params; unsigned int formats = 0;
- struct snd_mask format_mask;
const struct snd_mask *format_mask; int fmt;
if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
@@ -1802,12 +1802,12 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) return -ENOMEM; _snd_pcm_hw_params_any(params); err = snd_pcm_hw_refine(substream, params);
- format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); kfree(params); if (err < 0) return err; for (fmt = 0; fmt < 32; ++fmt) {
if (snd_mask_test(&format_mask, fmt)) {
if (snd_mask_test(format_mask, fmt)) { int f = snd_pcm_oss_format_to(fmt); if (f >= 0) formats |= f;
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 727ac44..cadc937 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -266,7 +266,8 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc return frames; }
-static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format) +static int snd_pcm_plug_formats(const struct snd_mask *mask,
snd_pcm_format_t format)
{ struct snd_mask formats = *mask; u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | @@ -309,7 +310,7 @@ static snd_pcm_format_t preferred_formats[] = { };
snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
struct snd_mask *format_mask)
const struct snd_mask *format_mask)
{ int i;
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index a5035c2..38e2c14 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -126,7 +126,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *slave_params);
snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
struct snd_mask *format_mask);
const struct snd_mask *format_mask);
int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin);
-- 2.9.3
Drivers can register rule of PCM parameters by a call of snd_pcm_hw_rule_add(). The rule includes a target parameter and dependent parameters. Basically, these parameters are the ones classified as mask or interval type. Although the helper function should check the variables, it actually doesn't. This brings extra check in rule application.
This commit adds argument checker to the helper function. Unfortunately, snd_pcm_hw_constraint_msbits() pass -1 as its parameter type. This is because it's for non-mask/non-interval parameter. However, similar helper functions, i.e. snd_pcm_hw_constraint_ratdens(), uses the same value for target/dependent parameter. We can use SNDRV_PCM_HW_PARAM_SAMPLE_BITS for the parameter.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_lib.c | 13 +++++++++---- sound/core/pcm_native.c | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 9659fa6..f699245 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1146,6 +1146,10 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, struct snd_pcm_hw_rule *c; unsigned int k; va_list args; + + if (!hw_is_mask(var) && !hw_is_interval(var)) + return -EINVAL; + va_start(args, dep); if (constrs->rules_num >= constrs->rules_all) { struct snd_pcm_hw_rule *new; @@ -1448,10 +1452,11 @@ int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, unsigned int msbits) { unsigned long l = (msbits << 16) | width; - return snd_pcm_hw_rule_add(runtime, cond, -1, - snd_pcm_hw_rule_msbits, - (void*) l, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + return snd_pcm_hw_rule_add(runtime, cond, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + snd_pcm_hw_rule_msbits, + (void *) l, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); }
EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index ad71eb2..00d5aff 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -438,7 +438,7 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, * in user space by returned bits, then preparing for next * iteration. */ - if (changed && r->var >= 0) { + if (changed) { params->cmask |= (1 << r->var); vstamps[r->var] = stamp; again = true;
A structure for parameters of PCM runtime has parameters which are not classified as mask/interval type. They are decided only when corresponding normal parameters have unique values. * struct snd_pcm_hw_params.msbits * struct snd_pcm_hw_params.rate_num * struct snd_pcm_hw_params.rate_den * struct snd_pcm_hw_params.fifo_size
Current implementation of hw_params ioctl sometimes doesn't decide these parameters even if corresponding parameters are fixed, because these parameters are evaluated before a call of snd_pcm_hw_params_choose().
This commit adds a helper function to process the parameters and call it in proper positions.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 71 ++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 27 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 00d5aff..2875895 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -456,12 +456,45 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, return 0; }
-int snd_pcm_hw_refine(struct snd_pcm_substream *substream, +static int fixup_unreferenced_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + const struct snd_mask *m; + const struct snd_interval *i; + int err; + + if (!params->msbits) { + i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + if (snd_interval_single(i)) + params->msbits = snd_interval_value(i); + } + + if (!params->rate_den) { + i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + if (snd_interval_single(i)) { + params->rate_num = snd_interval_value(i); + params->rate_den = 1; + } + } + + if (!params->fifo_size) { + m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); + i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + if (snd_mask_single(m) && snd_interval_single(i)) { + err = substream->ops->ioctl(substream, + SNDRV_PCM_IOCTL1_FIFO_SIZE, params); + if (err < 0) + return err; + } + } + + return 0; +} + +int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_hardware *hw; - struct snd_mask *m = NULL; - struct snd_interval *i = NULL; struct snd_pcm_hw_constraints *constrs; int changed;
@@ -487,20 +520,6 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (changed < 0) return changed;
- if (!params->msbits) { - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); - if (snd_interval_single(i)) - params->msbits = snd_interval_value(i); - } - - if (!params->rate_den) { - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - if (snd_interval_single(i)) { - params->rate_num = snd_interval_value(i); - params->rate_den = 1; - } - } - hw = &substream->runtime->hw; if (!params->info) { params->info = hw->info & ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | @@ -509,17 +528,9 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, params->info &= ~(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID); } - if (!params->fifo_size) { - m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (snd_mask_single(m) && snd_interval_single(i)) { - changed = substream->ops->ioctl(substream, - SNDRV_PCM_IOCTL1_FIFO_SIZE, params); - if (changed < 0) - return changed; - } - } + params->rmask = 0; + return 0; }
@@ -600,6 +611,8 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, return PTR_ERR(params);
err = snd_pcm_hw_refine(substream, params); + if (err >= 0) + err = fixup_unreferenced_params(substream, params); if (copy_to_user(_params, params, sizeof(*params))) { if (!err) err = -EFAULT; @@ -679,6 +692,10 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, if (err < 0) goto _error;
+ err = fixup_unreferenced_params(substream, params); + if (err < 0) + goto _error; + if (substream->ops->hw_params != NULL) { err = substream->ops->hw_params(substream, params); if (err < 0)
When drivers register no flags about information of PCM hardware, ALSA PCM core fixups it roughly. Currently, this operation places in a function snd_pcm_hw_refine(). It can be moved to a function fixup_unreferenced_params() because it doesn't affects operations between these two functions.
This idea is better to bundle codes with similar purposes and this commit achieves it.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 2875895..92ab8b2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -488,13 +488,21 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream, } }
+ if (!params->info) { + params->info = substream.runtime->hw->info; + params->info &= ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | + SNDRV_PCM_INFO_DRAIN_TRIGGER); + if (!hw_support_mmap(substream)) + params->info &= ~(SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID); + } + return 0; }
int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_hardware *hw; struct snd_pcm_hw_constraints *constrs; int changed;
@@ -520,15 +528,6 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (changed < 0) return changed;
- hw = &substream->runtime->hw; - if (!params->info) { - params->info = hw->info & ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | - SNDRV_PCM_INFO_DRAIN_TRIGGER); - if (!hw_support_mmap(substream)) - params->info &= ~(SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID); - } - params->rmask = 0;
return 0;
When refining mask/interval parameters, helper functions can return error code. This error is not handled immediately, thus there're useless processing.
This commit handles the error immediately.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 92ab8b2..05230f4 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -288,11 +288,13 @@ static int constrain_mask_params(struct snd_pcm_substream *substream,
trace_hw_params_mask(substream, k, -1, &old_mask, m);
+ if (changed < 0) + return changed; + + /* Set corresponding flag so that applications get it. */ if (changed) params->cmask |= 1 << k; - if (changed < 0) - return changed; }
return 0; @@ -326,11 +328,12 @@ static int constrain_interval_params(struct snd_pcm_substream *substream,
trace_hw_params_interval(substream, k, -1, &old_interval, i);
+ if (changed < 0) + return changed; + /* Set corresponding flag so that applications get it. */ if (changed) params->cmask |= 1 << k; - if (changed < 0) - return changed; }
return 0; @@ -431,6 +434,9 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, hw_param_interval(params, r->var)); }
+ if (changed < 0) + return changed; + rstamps[k] = stamp;
/* @@ -443,8 +449,6 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, vstamps[r->var] = stamp; again = true; } - if (changed < 0) - return changed;
stamp++; }
When refining parameters of runtime of PCM substream, helper functions can return error but invalid data is copied to userspace. This copying is useless.
This commit handles the error immediately. Old hw_refine/hw_params implementation include the same issue and this commit modifies them.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/pcm_native.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 05230f4..c8d2b85 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -614,13 +614,16 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, return PTR_ERR(params);
err = snd_pcm_hw_refine(substream, params); - if (err >= 0) - err = fixup_unreferenced_params(substream, params); - if (copy_to_user(_params, params, sizeof(*params))) { - if (!err) - err = -EFAULT; - } + if (err < 0) + goto end;
+ err = fixup_unreferenced_params(substream, params); + if (err < 0) + goto end; + + if (copy_to_user(_params, params, sizeof(*params))) + err = -EFAULT; +end: kfree(params); return err; } @@ -3759,9 +3762,9 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, } snd_pcm_hw_convert_from_old_params(params, oparams); err = snd_pcm_hw_refine(substream, params); - snd_pcm_hw_convert_to_old_params(oparams, params); - if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { - if (!err) + if (err >= 0) { + snd_pcm_hw_convert_to_old_params(oparams, params); + if (copy_to_user(_oparams, oparams, sizeof(*oparams))) err = -EFAULT; }
@@ -3789,9 +3792,9 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, } snd_pcm_hw_convert_from_old_params(params, oparams); err = snd_pcm_hw_params(substream, params); - snd_pcm_hw_convert_to_old_params(oparams, params); - if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { - if (!err) + if (err >= 0) { + snd_pcm_hw_convert_to_old_params(oparams, params); + if (copy_to_user(_oparams, oparams, sizeof(*oparams))) err = -EFAULT; }
On Sun, 14 May 2017 10:57:35 +0200, Takashi Sakamoto wrote:
Hi,
In the last Audio Mini Conference held with Linux Plumber conference 2016[1], I mentioned about tracepoints for PCM params operation. This patchset is for the idea.
In ALSA PCM interface, applications can get hardware capability by ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE/SNDRV_PCM_IOCT_HW_PARAMS in a shape of 'struct snd_pcm_hw_params'. In kernel side, relevant processing is somewhat complicated and developers sometimes have hard time to debug drivers for PCM constraints and rules.
This patchset adds tracepoints for hw_params operations. When CONFIG_SND_DEBUG is enabled, you can see 'trace_hw_params_mask/trace_hw_params_interval' events of 'snd_pcm' subsystem. When applications execute ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE/SNDRV_PCM_IOCTL_HW_PARAMS, these events are probed. Developers can get how many PCM rules are added into runtime of PCM substream and which rule changed which parameters.
This patchset also includes some improvements. The last three commits brings small changes to kernel/userspace interface for error handling.
I'm happy to receive your comment for this patchset. For your information, low level application of SNDRV_PCM_IOCTL_HW_REFINE operation is available in my github repository[2].
The patches look good through a quick glance. The only concern I have is the function regression, since there are lots of code rewrites. How did you test?
thanks,
Takashi
On May 15 2017 17:42, Takashi Iwai wrote:
On Sun, 14 May 2017 10:57:35 +0200, Takashi Sakamoto wrote:
Hi,
In the last Audio Mini Conference held with Linux Plumber conference 2016[1], I mentioned about tracepoints for PCM params operation. This patchset is for the idea.
In ALSA PCM interface, applications can get hardware capability by ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE/SNDRV_PCM_IOCT_HW_PARAMS in a shape of 'struct snd_pcm_hw_params'. In kernel side, relevant processing is somewhat complicated and developers sometimes have hard time to debug drivers for PCM constraints and rules.
This patchset adds tracepoints for hw_params operations. When CONFIG_SND_DEBUG is enabled, you can see 'trace_hw_params_mask/trace_hw_params_interval' events of 'snd_pcm' subsystem. When applications execute ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE/SNDRV_PCM_IOCTL_HW_PARAMS, these events are probed. Developers can get how many PCM rules are added into runtime of PCM substream and which rule changed which parameters.
This patchset also includes some improvements. The last three commits brings small changes to kernel/userspace interface for error handling.
I'm happy to receive your comment for this patchset. For your information, low level application of SNDRV_PCM_IOCTL_HW_REFINE operation is available in my github repository[2].
The patches look good through a quick glance. The only concern I have is the function regression, since there are lots of code rewrites. How did you test?
Currently, I did four things:
1. understand logic to process parameters, constraints and rules 2. add the tracepoints as early as the patchset 3. confirm that probed events include the same data commit by commit 4. do the above with refine-pcm-params.c and got valid results
For the above, I use ALSA Fireweorks/OXFW driver and supported devices, which I know correct behaviour.
Thanks
Takashi Sakamoto
On Mon, 15 May 2017 16:29:47 +0200, Takashi Sakamoto wrote:
On May 15 2017 17:42, Takashi Iwai wrote:
On Sun, 14 May 2017 10:57:35 +0200, Takashi Sakamoto wrote:
Hi,
In the last Audio Mini Conference held with Linux Plumber conference 2016[1], I mentioned about tracepoints for PCM params operation. This patchset is for the idea.
In ALSA PCM interface, applications can get hardware capability by ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE/SNDRV_PCM_IOCT_HW_PARAMS in a shape of 'struct snd_pcm_hw_params'. In kernel side, relevant processing is somewhat complicated and developers sometimes have hard time to debug drivers for PCM constraints and rules.
This patchset adds tracepoints for hw_params operations. When CONFIG_SND_DEBUG is enabled, you can see 'trace_hw_params_mask/trace_hw_params_interval' events of 'snd_pcm' subsystem. When applications execute ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE/SNDRV_PCM_IOCTL_HW_PARAMS, these events are probed. Developers can get how many PCM rules are added into runtime of PCM substream and which rule changed which parameters.
This patchset also includes some improvements. The last three commits brings small changes to kernel/userspace interface for error handling.
I'm happy to receive your comment for this patchset. For your information, low level application of SNDRV_PCM_IOCTL_HW_REFINE operation is available in my github repository[2].
The patches look good through a quick glance. The only concern I have is the function regression, since there are lots of code rewrites. How did you test?
Currently, I did four things:
- understand logic to process parameters, constraints and rules
- add the tracepoints as early as the patchset
- confirm that probed events include the same data commit by commit
- do the above with refine-pcm-params.c and got valid results
For the above, I use ALSA Fireweorks/OXFW driver and supported devices, which I know correct behaviour.
Maybe we can have some test set using dummy driver, too?
Takashi
On May 15 2017 23:34, Takashi Iwai wrote:
On Mon, 15 May 2017 16:29:47 +0200, Takashi Sakamoto wrote:
On May 15 2017 17:42, Takashi Iwai wrote:
On Sun, 14 May 2017 10:57:35 +0200, Takashi Sakamoto wrote:
Hi,
In the last Audio Mini Conference held with Linux Plumber conference 2016[1], I mentioned about tracepoints for PCM params operation. This patchset is for the idea.
In ALSA PCM interface, applications can get hardware capability by ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE/SNDRV_PCM_IOCT_HW_PARAMS in a shape of 'struct snd_pcm_hw_params'. In kernel side, relevant processing is somewhat complicated and developers sometimes have hard time to debug drivers for PCM constraints and rules.
This patchset adds tracepoints for hw_params operations. When CONFIG_SND_DEBUG is enabled, you can see 'trace_hw_params_mask/trace_hw_params_interval' events of 'snd_pcm' subsystem. When applications execute ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE/SNDRV_PCM_IOCTL_HW_PARAMS, these events are probed. Developers can get how many PCM rules are added into runtime of PCM substream and which rule changed which parameters.
This patchset also includes some improvements. The last three commits brings small changes to kernel/userspace interface for error handling.
I'm happy to receive your comment for this patchset. For your information, low level application of SNDRV_PCM_IOCTL_HW_REFINE operation is available in my github repository[2].
The patches look good through a quick glance. The only concern I have is the function regression, since there are lots of code rewrites. How did you test?
Currently, I did four things:
- understand logic to process parameters, constraints and rules
- add the tracepoints as early as the patchset
- confirm that probed events include the same data commit by commit
- do the above with refine-pcm-params.c and got valid results
For the above, I use ALSA Fireweorks/OXFW driver and supported devices, which I know correct behaviour.
Maybe we can have some test set using dummy driver, too?
Yep, it's preferable when this change influences all of ALSA PCM applications. But actually it's perhaps a hard work to me within the rest of my private time in this development period... Anyway, do my best for it.
At present, I'll post some patches from this patchset, which include subtle changes, at first.
Thanks
Takashi Sakamoto
participants (2)
-
Takashi Iwai
-
Takashi Sakamoto