From e42bd8613d169034a53f5277d0bcfc9179c08e94 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 31 Aug 2021 09:55:42 +0200 Subject: [PATCH] ucm fixes for 1.2.5.1 --- alsa-git.patch | 1916 ++++++++++++++++++++++++++++++++++++++++++++++++ alsa-lib.spec | 5 +- 2 files changed, 1920 insertions(+), 1 deletion(-) diff --git a/alsa-git.patch b/alsa-git.patch index e69de29..f88de1b 100644 --- a/alsa-git.patch +++ b/alsa-git.patch @@ -0,0 +1,1916 @@ +From 81e7923fbfad45b2f353a4d6e3053af51f5f7d0b Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Tue, 15 Jun 2021 23:21:42 +0200 +Subject: [PATCH 01/20] control: empty - fix the static build + +Reported-by: Jan Palus +Fixes: https://github.com/alsa-project/alsa-lib/issues/157 +Signed-off-by: Jaroslav Kysela +--- + src/control/control_empty.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/control/control_empty.c b/src/control/control_empty.c +index 49d1026c..c9b048c1 100644 +--- a/src/control/control_empty.c ++++ b/src/control/control_empty.c +@@ -30,7 +30,7 @@ + + #ifndef PIC + /* entry for static linking */ +-const char *_snd_module_ctl_empty = ""; ++const char *_snd_module_control_empty = ""; + #endif + + /*! \page control_plugins +-- +2.31.1 + + +From 5089358aa99d698bd846b05c1890a09613d740b3 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 17 Jun 2021 10:20:25 +0200 +Subject: [PATCH 02/20] pcm: rate: Refactoring temporary buffer allocations + +Introduce common helpers to allocate and release the temporary buffers +and the associated snd_pcm_channel. Now two allocated objects are +used instead of one malloc to be split. + +Also, change the snd_pcm_channel set up to be in interleaved mode. +This will be necessary in the following change in the rate plugin. + +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm_rate.c | 80 ++++++++++++++++++++++++++++------------------ + 1 file changed, 49 insertions(+), 31 deletions(-) + +diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c +index 770aafea..1e996134 100644 +--- a/src/pcm/pcm_rate.c ++++ b/src/pcm/pcm_rate.c +@@ -76,6 +76,45 @@ struct _snd_pcm_rate { + + #endif /* DOC_HIDDEN */ + ++/* allocate a channel area and a temporary buffer for the given size */ ++static snd_pcm_channel_area_t * ++rate_alloc_tmp_buf(snd_pcm_rate_t *rate, snd_pcm_format_t format, ++ unsigned int channels, unsigned int frames) ++{ ++ snd_pcm_channel_area_t *ap; ++ int width = snd_pcm_format_physical_width(format); ++ int i; ++ ++ ap = malloc(sizeof(*ap) * channels); ++ if (!ap) ++ return NULL; ++ ap->addr = malloc(frames * channels * width / 8); ++ if (!ap->addr) { ++ free(ap); ++ return NULL; ++ } ++ ++ /* set up in interleaved format */ ++ for (i = 0; i < channels; i++) { ++ ap[i].addr = ap[0].addr + (i * width) / 8; ++ ap[i].first = 0; ++ ap[i].step = width * channels; ++ } ++ ++ return ap; ++} ++ ++static void rate_free_tmp_buf(snd_pcm_channel_area_t **ptr) ++{ ++ snd_pcm_channel_area_t *c = *ptr; ++ ++ if (c) { ++ free(c->addr); ++ free(c); ++ *ptr = NULL; ++ } ++} ++ + static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params) + { + snd_pcm_rate_t *rate = pcm->private_data; +@@ -286,28 +325,13 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + if (err < 0) + return err; + +- rate->pareas = malloc(2 * channels * sizeof(*rate->pareas)); +- if (rate->pareas == NULL) ++ rate->pareas = rate_alloc_tmp_buf(rate, cinfo->format, channels, ++ cinfo->period_size); ++ rate->sareas = rate_alloc_tmp_buf(rate, sinfo->format, channels, ++ sinfo->period_size); ++ if (!rate->pareas || !rate->sareas) + goto error; + +- cwidth = snd_pcm_format_physical_width(cinfo->format); +- swidth = snd_pcm_format_physical_width(sinfo->format); +- rate->pareas[0].addr = malloc(((cwidth * channels * cinfo->period_size) / 8) + +- ((swidth * channels * sinfo->period_size) / 8)); +- if (rate->pareas[0].addr == NULL) +- goto error; +- +- rate->sareas = rate->pareas + channels; +- rate->sareas[0].addr = (char *)rate->pareas[0].addr + ((cwidth * channels * cinfo->period_size) / 8); +- for (chn = 0; chn < channels; chn++) { +- rate->pareas[chn].addr = (char *)rate->pareas[0].addr + (cwidth * chn * cinfo->period_size) / 8; +- rate->pareas[chn].first = 0; +- rate->pareas[chn].step = cwidth; +- rate->sareas[chn].addr = (char *)rate->sareas[0].addr + (swidth * chn * sinfo->period_size) / 8; +- rate->sareas[chn].first = 0; +- rate->sareas[chn].step = swidth; +- } +- + if (rate->ops.convert_s16) { + rate->get_idx = snd_pcm_linear_get_index(rate->info.in.format, SND_PCM_FORMAT_S16); + rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, rate->info.out.format); +@@ -322,11 +346,8 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + return 0; + + error: +- if (rate->pareas) { +- free(rate->pareas[0].addr); +- free(rate->pareas); +- rate->pareas = NULL; +- } ++ rate_free_tmp_buf(&rate->pareas); ++ rate_free_tmp_buf(&rate->sareas); + if (rate->ops.free) + rate->ops.free(rate->obj); + return -ENOMEM; +@@ -335,12 +356,9 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + static int snd_pcm_rate_hw_free(snd_pcm_t *pcm) + { + snd_pcm_rate_t *rate = pcm->private_data; +- if (rate->pareas) { +- free(rate->pareas[0].addr); +- free(rate->pareas); +- rate->pareas = NULL; +- rate->sareas = NULL; +- } ++ ++ rate_free_tmp_buf(&rate->pareas); ++ rate_free_tmp_buf(&rate->sareas); + if (rate->ops.free) + rate->ops.free(rate->obj); + free(rate->src_buf); +-- +2.31.1 + + +From 119d9c1678b1193f8b969a6483cae1f7bf95e609 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 17 Jun 2021 10:03:17 +0200 +Subject: [PATCH 03/20] pcm: rate: Improve the support multiple formats + +This patch extends the PCM rate plugin for allowing its converter +plugin to deal with multiple formats. Currently, the converter plugin +is allowed to take different formats only when convert callback is +defined. And for this way (so far only the standard linear rate +plugin does), all linear formats have to be handled, and it's +cumbersome. + +OTOH, most other rate plugins are implemented with convert_s16 +callback, which accepts only S16 format. This is often not ideal +because many converter engines can handle 32bit formats. Also, the +target format is often 32bit format, hence this would require +additional conversion even if the converter engine can output 32bit +natively. + +In this patch, for addressing the problems above, the rate plugin API +is extended in the following way: + +- The new get_supported_formats callback is added; this stores the bit + masks of the supported input and output formats, as well as the + behavior flags. Currently only linear formats are allowed. +- When the plugin accepts only the interleaved stream, set + SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to + handle snd_pcm_channel_area at each call. +- When both input and output formats have to be identical, pass + SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. +- When the converter wants to process different formats, use convert + callback instead of convert_s16. You can put both in the ops for + compatibility, too. + The input and output formats are found in the info argument of init + callback. +- Now the PCM rate plugin core will skip the temporary buffer + allocation and conversions for pre- and post-process if not needed + (i.e. matching with the requested input or output format). + +The rate plugin API version is bumped to 0x010003. + +Signed-off-by: Takashi Iwai +--- + include/pcm_rate.h | 36 ++++- + src/pcm/pcm_rate.c | 358 +++++++++++++++++++++++++++------------------ + 2 files changed, 251 insertions(+), 143 deletions(-) + +diff --git a/include/pcm_rate.h b/include/pcm_rate.h +index 4171fb9d..48473ed4 100644 +--- a/include/pcm_rate.h ++++ b/include/pcm_rate.h +@@ -38,7 +38,7 @@ extern "C" { + /** + * Protocol version + */ +-#define SND_PCM_RATE_PLUGIN_VERSION 0x010002 ++#define SND_PCM_RATE_PLUGIN_VERSION 0x010003 + + /** hw_params information for a single side */ + typedef struct snd_pcm_rate_side_info { +@@ -55,6 +55,11 @@ typedef struct snd_pcm_rate_info { + unsigned int channels; + } snd_pcm_rate_info_t; + ++enum { ++ SND_PCM_RATE_FLAG_INTERLEAVED = (1U << 0), /** only interleaved format */ ++ SND_PCM_RATE_FLAG_SYNC_FORMATS = (1U << 1), /** both input and output formats have to be identical */ ++}; ++ + /** Callback table of rate-converter */ + typedef struct snd_pcm_rate_ops { + /** +@@ -114,6 +119,13 @@ typedef struct snd_pcm_rate_ops { + * new ops since version 0x010002 + */ + void (*dump)(void *obj, snd_output_t *out); ++ /** ++ * get the supported input and output formats (optional); ++ * new ops since version 0x010003 ++ */ ++ int (*get_supported_formats)(void *obj, uint64_t *in_formats, ++ uint64_t *out_formats, ++ unsigned int *flags); + } snd_pcm_rate_ops_t; + + /** open function type */ +@@ -147,6 +159,28 @@ typedef struct snd_pcm_rate_old_ops { + snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames); + snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames); + } snd_pcm_rate_old_ops_t; ++ ++/* old rate_ops for protocol version 0x010002 */ ++typedef struct snd_pcm_rate_v2_ops { ++ void (*close)(void *obj); ++ int (*init)(void *obj, snd_pcm_rate_info_t *info); ++ void (*free)(void *obj); ++ void (*reset)(void *obj); ++ int (*adjust_pitch)(void *obj, snd_pcm_rate_info_t *info); ++ void (*convert)(void *obj, ++ const snd_pcm_channel_area_t *dst_areas, ++ snd_pcm_uframes_t dst_offset, unsigned int dst_frames, ++ const snd_pcm_channel_area_t *src_areas, ++ snd_pcm_uframes_t src_offset, unsigned int src_frames); ++ void (*convert_s16)(void *obj, int16_t *dst, unsigned int dst_frames, ++ const int16_t *src, unsigned int src_frames); ++ snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames); ++ snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames); ++ unsigned int version; ++ int (*get_supported_rates)(void *obj, unsigned int *rate_min, ++ unsigned int *rate_max); ++ void (*dump)(void *obj, snd_output_t *out); ++} snd_pcm_rate_v2_ops_t; + #endif + + #ifdef __cplusplus +diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c +index 1e996134..13111d29 100644 +--- a/src/pcm/pcm_rate.c ++++ b/src/pcm/pcm_rate.c +@@ -62,18 +62,22 @@ struct _snd_pcm_rate { + void *open_func; + void *obj; + snd_pcm_rate_ops_t ops; +- unsigned int get_idx; +- unsigned int put_idx; +- int16_t *src_buf; +- int16_t *dst_buf; ++ unsigned int src_conv_idx; ++ unsigned int dst_conv_idx; ++ snd_pcm_channel_area_t *src_buf; ++ snd_pcm_channel_area_t *dst_buf; + int start_pending; /* start is triggered but not commited to slave */ + snd_htimestamp_t trigger_tstamp; + unsigned int plugin_version; + unsigned int rate_min, rate_max; ++ snd_pcm_format_t orig_in_format; ++ snd_pcm_format_t orig_out_format; ++ uint64_t in_formats; ++ uint64_t out_formats; ++ unsigned int format_flags; + }; + + #define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001 /* old rate plugin */ +- + #endif /* DOC_HIDDEN */ + + /* allocate a channel area and a temporary buffer for the given size */ +@@ -274,12 +278,84 @@ static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm, + snd_pcm_generic_hw_refine); + } + ++/* evaluate the best matching available format to the given format */ ++static int get_best_format(uint64_t mask, snd_pcm_format_t orig) ++{ ++ int pwidth = snd_pcm_format_physical_width(orig); ++ int width = snd_pcm_format_width(orig); ++ int signd = snd_pcm_format_signed(orig); ++ int best_score = -1; ++ int match = -1; ++ int f, score; ++ ++ for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) { ++ if (!(mask & (1ULL << f))) ++ continue; ++ score = 0; ++ if (snd_pcm_format_linear(f)) { ++ if (snd_pcm_format_physical_width(f) == pwidth) ++ score++; ++ if (snd_pcm_format_physical_width(f) >= pwidth) ++ score++; ++ if (snd_pcm_format_width(f) == width) ++ score++; ++ if (snd_pcm_format_signed(f) == signd) ++ score++; ++ } ++ if (score > best_score) { ++ match = f; ++ best_score = score; ++ } ++ } ++ ++ return match; ++} ++ ++/* set up the input and output formats from the available lists */ ++static int choose_preferred_format(snd_pcm_rate_t *rate) ++{ ++ uint64_t in_mask = rate->in_formats; ++ uint64_t out_mask = rate->out_formats; ++ int in, out; ++ ++ if (!in_mask || !out_mask) ++ return 0; ++ ++ if (rate->orig_in_format == rate->orig_out_format) ++ if (in_mask & out_mask & (1ULL << rate->orig_in_format)) ++ return 0; /* nothing changed */ ++ ++ repeat: ++ in = get_best_format(in_mask, rate->orig_in_format); ++ out = get_best_format(out_mask, rate->orig_out_format); ++ if (in < 0 || out < 0) ++ return -ENOENT; ++ ++ if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) && ++ in != out) { ++ if (out_mask & (1ULL << in)) ++ out = in; ++ else if (in_mask & (1ULL << out)) ++ in = out; ++ else { ++ in_mask &= ~(1ULL << in); ++ out_mask &= ~(1ULL << out); ++ goto repeat; ++ } ++ } ++ ++ rate->info.in.format = in; ++ rate->info.out.format = out; ++ return 0; ++} ++ + static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + { + snd_pcm_rate_t *rate = pcm->private_data; + snd_pcm_t *slave = rate->gen.slave; + snd_pcm_rate_side_info_t *sinfo, *cinfo; +- unsigned int channels, cwidth, swidth, chn; ++ unsigned int channels, cwidth, swidth, chn, acc; ++ int need_src_buf, need_dst_buf; + int err = snd_pcm_hw_params_slave(pcm, params, + snd_pcm_rate_hw_refine_cchange, + snd_pcm_rate_hw_refine_sprepare, +@@ -308,6 +384,9 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + if (err < 0) + return err; + err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels); ++ if (err < 0) ++ return err; ++ err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc); + if (err < 0) + return err; + +@@ -321,36 +400,80 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + SNDMSG("rate plugin already in use"); + return -EBUSY; + } +- err = rate->ops.init(rate->obj, &rate->info); +- if (err < 0) +- return err; + + rate->pareas = rate_alloc_tmp_buf(rate, cinfo->format, channels, + cinfo->period_size); + rate->sareas = rate_alloc_tmp_buf(rate, sinfo->format, channels, + sinfo->period_size); +- if (!rate->pareas || !rate->sareas) +- goto error; +- +- if (rate->ops.convert_s16) { +- rate->get_idx = snd_pcm_linear_get_index(rate->info.in.format, SND_PCM_FORMAT_S16); +- rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, rate->info.out.format); +- free(rate->src_buf); +- rate->src_buf = malloc(channels * rate->info.in.period_size * 2); +- free(rate->dst_buf); +- rate->dst_buf = malloc(channels * rate->info.out.period_size * 2); +- if (! rate->src_buf || ! rate->dst_buf) ++ if (!rate->pareas || !rate->sareas) { ++ err = -ENOMEM; ++ goto error_pareas; ++ } ++ ++ rate->orig_in_format = rate->info.in.format; ++ rate->orig_out_format = rate->info.out.format; ++ if (choose_preferred_format(rate) < 0) { ++ SNDERR("No matching format in rate plugin"); ++ err = -EINVAL; ++ goto error_pareas; ++ } ++ ++ err = rate->ops.init(rate->obj, &rate->info); ++ if (err < 0) ++ goto error_init; ++ ++ rate_free_tmp_buf(&rate->src_buf); ++ rate_free_tmp_buf(&rate->dst_buf); ++ ++ need_src_buf = need_dst_buf = 0; ++ ++ if ((rate->format_flags & SND_PCM_RATE_FLAG_INTERLEAVED) && ++ !(acc == SND_PCM_ACCESS_MMAP_INTERLEAVED || ++ acc == SND_PCM_ACCESS_RW_INTERLEAVED)) { ++ need_src_buf = need_dst_buf = 1; ++ } else { ++ if (rate->orig_in_format != rate->info.in.format) ++ need_src_buf = 1; ++ if (rate->orig_out_format != rate->info.out.format) ++ need_dst_buf = 1; ++ } ++ ++ if (need_src_buf) { ++ rate->src_conv_idx = ++ snd_pcm_linear_convert_index(rate->orig_in_format, ++ rate->info.in.format); ++ rate->src_buf = rate_alloc_tmp_buf(rate, rate->info.in.format, ++ channels, rate->info.in.period_size); ++ if (!rate->src_buf) { ++ err = -ENOMEM; + goto error; ++ } ++ } ++ ++ if (need_dst_buf) { ++ rate->dst_conv_idx = ++ snd_pcm_linear_convert_index(rate->info.out.format, ++ rate->orig_out_format); ++ rate->dst_buf = rate_alloc_tmp_buf(rate, rate->info.out.format, ++ channels, rate->info.out.period_size); ++ if (!rate->dst_buf) { ++ err = -ENOMEM; ++ goto error; ++ } + } + + return 0; + + error: +- rate_free_tmp_buf(&rate->pareas); +- rate_free_tmp_buf(&rate->sareas); ++ rate_free_tmp_buf(&rate->src_buf); ++ rate_free_tmp_buf(&rate->dst_buf); ++ error_init: + if (rate->ops.free) + rate->ops.free(rate->obj); +- return -ENOMEM; ++ error_pareas: ++ rate_free_tmp_buf(&rate->pareas); ++ rate_free_tmp_buf(&rate->sareas); ++ return err; + } + + static int snd_pcm_rate_hw_free(snd_pcm_t *pcm) +@@ -361,9 +484,8 @@ static int snd_pcm_rate_hw_free(snd_pcm_t *pcm) + rate_free_tmp_buf(&rate->sareas); + if (rate->ops.free) + rate->ops.free(rate->obj); +- free(rate->src_buf); +- free(rate->dst_buf); +- rate->src_buf = rate->dst_buf = NULL; ++ rate_free_tmp_buf(&rate->src_buf); ++ rate_free_tmp_buf(&rate->dst_buf); + return snd_pcm_hw_free(rate->gen.slave); + } + +@@ -444,82 +566,6 @@ static int snd_pcm_rate_init(snd_pcm_t *pcm) + return 0; + } + +-static void convert_to_s16(snd_pcm_rate_t *rate, int16_t *buf, +- const snd_pcm_channel_area_t *areas, +- snd_pcm_uframes_t offset, unsigned int frames, +- unsigned int channels) +-{ +-#ifndef DOC_HIDDEN +-#define GET16_LABELS +-#include "plugin_ops.h" +-#undef GET16_LABELS +-#endif /* DOC_HIDDEN */ +- void *get = get16_labels[rate->get_idx]; +- const char *src; +- int16_t sample; +- const char *srcs[channels]; +- int src_step[channels]; +- unsigned int c; +- +- for (c = 0; c < channels; c++) { +- srcs[c] = snd_pcm_channel_area_addr(areas + c, offset); +- src_step[c] = snd_pcm_channel_area_step(areas + c); +- } +- +- while (frames--) { +- for (c = 0; c < channels; c++) { +- src = srcs[c]; +- goto *get; +-#ifndef DOC_HIDDEN +-#define GET16_END after_get +-#include "plugin_ops.h" +-#undef GET16_END +-#endif /* DOC_HIDDEN */ +- after_get: +- *buf++ = sample; +- srcs[c] += src_step[c]; +- } +- } +-} +- +-static void convert_from_s16(snd_pcm_rate_t *rate, const int16_t *buf, +- const snd_pcm_channel_area_t *areas, +- snd_pcm_uframes_t offset, unsigned int frames, +- unsigned int channels) +-{ +-#ifndef DOC_HIDDEN +-#define PUT16_LABELS +-#include "plugin_ops.h" +-#undef PUT16_LABELS +-#endif /* DOC_HIDDEN */ +- void *put = put16_labels[rate->put_idx]; +- char *dst; +- int16_t sample; +- char *dsts[channels]; +- int dst_step[channels]; +- unsigned int c; +- +- for (c = 0; c < channels; c++) { +- dsts[c] = snd_pcm_channel_area_addr(areas + c, offset); +- dst_step[c] = snd_pcm_channel_area_step(areas + c); +- } +- +- while (frames--) { +- for (c = 0; c < channels; c++) { +- dst = dsts[c]; +- sample = *buf++; +- goto *put; +-#ifndef DOC_HIDDEN +-#define PUT16_END after_put +-#include "plugin_ops.h" +-#undef PUT16_END +-#endif /* DOC_HIDDEN */ +- after_put: +- dsts[c] += dst_step[c]; +- } +- } +-} +- + static void do_convert(const snd_pcm_channel_area_t *dst_areas, + snd_pcm_uframes_t dst_offset, unsigned int dst_frames, + const snd_pcm_channel_area_t *src_areas, +@@ -527,28 +573,40 @@ static void do_convert(const snd_pcm_channel_area_t *dst_areas, + unsigned int channels, + snd_pcm_rate_t *rate) + { +- if (rate->ops.convert_s16) { +- const int16_t *src; +- int16_t *dst; +- if (! rate->src_buf) +- src = (int16_t *)src_areas->addr + src_offset * channels; +- else { +- convert_to_s16(rate, rate->src_buf, src_areas, src_offset, +- src_frames, channels); +- src = rate->src_buf; +- } +- if (! rate->dst_buf) +- dst = (int16_t *)dst_areas->addr + dst_offset * channels; +- else +- dst = rate->dst_buf; +- rate->ops.convert_s16(rate->obj, dst, dst_frames, src, src_frames); +- if (dst == rate->dst_buf) +- convert_from_s16(rate, rate->dst_buf, dst_areas, dst_offset, +- dst_frames, channels); ++ const snd_pcm_channel_area_t *out_areas; ++ snd_pcm_uframes_t out_offset; ++ ++ if (rate->dst_buf) { ++ out_areas = rate->dst_buf; ++ out_offset = 0; + } else { +- rate->ops.convert(rate->obj, dst_areas, dst_offset, dst_frames, +- src_areas, src_offset, src_frames); ++ out_areas = dst_areas; ++ out_offset = dst_offset; ++ } ++ ++ if (rate->src_buf) { ++ snd_pcm_linear_convert(rate->src_buf, 0, ++ src_areas, src_offset, ++ channels, src_frames, ++ rate->src_conv_idx); ++ src_areas = rate->src_buf; ++ src_offset = 0; + } ++ ++ if (rate->ops.convert) ++ rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames, ++ src_areas, src_offset, src_frames); ++ else ++ rate->ops.convert_s16(rate->obj, ++ snd_pcm_channel_area_addr(out_areas, out_offset), ++ dst_frames, ++ snd_pcm_channel_area_addr(src_areas, src_offset), ++ src_frames); ++ if (rate->dst_buf) ++ snd_pcm_linear_convert(dst_areas, dst_offset, ++ rate->dst_buf, 0, ++ channels, dst_frames, ++ rate->dst_conv_idx); + } + + static inline void +@@ -1276,6 +1334,30 @@ const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root) + return NULL; + } + ++static void rate_initial_setup(snd_pcm_rate_t *rate) ++{ ++ if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION) ++ rate->plugin_version = rate->ops.version; ++ ++ if (rate->plugin_version >= 0x010002 && ++ rate->ops.get_supported_rates) ++ rate->ops.get_supported_rates(rate->obj, ++ &rate->rate_min, ++ &rate->rate_max); ++ ++ if (rate->plugin_version >= 0x010003 && ++ rate->ops.get_supported_formats) { ++ rate->ops.get_supported_formats(rate->obj, ++ &rate->in_formats, ++ &rate->out_formats, ++ &rate->format_flags); ++ } else if (!rate->ops.convert && rate->ops.convert_s16) { ++ rate->in_formats = rate->out_formats = ++ 1ULL << SND_PCM_FORMAT_S16; ++ rate->format_flags = SND_PCM_RATE_FLAG_INTERLEAVED; ++ } ++} ++ + #ifdef PIC + static int is_builtin_plugin(const char *type) + { +@@ -1301,20 +1383,11 @@ static int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_conf + lib = lib_name; + } + +- rate->rate_min = SND_PCM_PLUGIN_RATE_MIN; +- rate->rate_max = SND_PCM_PLUGIN_RATE_MAX; +- rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION; +- + open_conf_func = snd_dlobj_cache_get(lib, open_conf_name, NULL, verbose && converter_conf != NULL); + if (open_conf_func) { + err = open_conf_func(SND_PCM_RATE_PLUGIN_VERSION, + &rate->obj, &rate->ops, converter_conf); + if (!err) { +- rate->plugin_version = rate->ops.version; +- if (rate->ops.get_supported_rates) +- rate->ops.get_supported_rates(rate->obj, +- &rate->rate_min, +- &rate->rate_max); + rate->open_func = open_conf_func; + return 0; + } else { +@@ -1330,23 +1403,18 @@ static int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_conf + rate->open_func = open_func; + + err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops); +- if (!err) { +- rate->plugin_version = rate->ops.version; +- if (rate->ops.get_supported_rates) +- rate->ops.get_supported_rates(rate->obj, +- &rate->rate_min, +- &rate->rate_max); ++ if (!err) + return 0; +- } + + /* try to open with the old protocol version */ + rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD; + err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD, + &rate->obj, &rate->ops); +- if (err) { +- snd_dlobj_cache_put(open_func); +- rate->open_func = NULL; +- } ++ if (!err) ++ return 0; ++ ++ snd_dlobj_cache_put(open_func); ++ rate->open_func = NULL; + return err; + } + #endif +@@ -1417,6 +1485,10 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, + rate->srate = srate; + rate->sformat = sformat; + ++ rate->rate_min = SND_PCM_PLUGIN_RATE_MIN; ++ rate->rate_max = SND_PCM_PLUGIN_RATE_MAX; ++ rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION; ++ + err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode); + if (err < 0) { + free(rate); +@@ -1496,6 +1568,8 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, + return err; + } + ++ rate_initial_setup(rate); ++ + pcm->ops = &snd_pcm_rate_ops; + pcm->fast_ops = &snd_pcm_rate_fast_ops; + pcm->private_data = rate; +-- +2.31.1 + + +From 3f737a2a2c8d20e78dea3ea836997f9d74f602a0 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Fri, 18 Jun 2021 11:35:30 +0200 +Subject: [PATCH 04/20] pcm: improve docs for snd_pcm_sw_params_get_avail_min() + and snd_pcm_status_get_avail_max() + +Fixes: https://github.com/alsa-project/alsa-lib/issues/44 +Signed-off-by: Jaroslav Kysela +--- + src/pcm/pcm.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c +index 09df0f12..892abf98 100644 +--- a/src/pcm/pcm.c ++++ b/src/pcm/pcm.c +@@ -6500,6 +6500,9 @@ int snd_pcm_sw_params_set_avail_min(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, + * \param params Software configuration container + * \param val returned minimum available frames to consider PCM ready + * \return 0 otherwise a negative error code ++ * ++ * This is a threshold value when the PCM stream is considered as ready for ++ * another read/write operation or poll event. + */ + #ifndef DOXYGEN + EXPORT_SYMBOL int INTERNAL(snd_pcm_sw_params_get_avail_min)(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val) +@@ -6960,6 +6963,8 @@ snd_pcm_uframes_t snd_pcm_status_get_avail(const snd_pcm_status_t *obj) + /** + * \brief Get maximum number of frames available from a PCM status container after last #snd_pcm_status call + * \return Maximum number of frames ready to be read/written ++ * ++ * This value returns the peak for the available frames between #snd_pcm_status calls. + */ + snd_pcm_uframes_t snd_pcm_status_get_avail_max(const snd_pcm_status_t *obj) + { +-- +2.31.1 + + +From 212c6c18c4317af48c007a0866efc029b9c3a593 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Mon, 21 Jun 2021 09:23:02 +0200 +Subject: [PATCH 05/20] pcm: dmix - use pcm_frame_diff() in + snd_pcm_dmix_sync_ptr0() + +Signed-off-by: Jaroslav Kysela +--- + src/pcm/pcm_dmix.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c +index 608593f1..0d0d0bff 100644 +--- a/src/pcm/pcm_dmix.c ++++ b/src/pcm/pcm_dmix.c +@@ -393,17 +393,13 @@ static int snd_pcm_dmix_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr + + old_slave_hw_ptr = dmix->slave_hw_ptr; + dmix->slave_hw_ptr = slave_hw_ptr; +- diff = slave_hw_ptr - old_slave_hw_ptr; ++ diff = pcm_frame_diff(slave_hw_ptr, old_slave_hw_ptr, dmix->slave_boundary); + if (diff == 0) /* fast path */ + return 0; + if (dmix->state != SND_PCM_STATE_RUNNING && + dmix->state != SND_PCM_STATE_DRAINING) + /* not really started yet - don't update hw_ptr */ + return 0; +- if (diff < 0) { +- slave_hw_ptr += dmix->slave_boundary; +- diff = slave_hw_ptr - old_slave_hw_ptr; +- } + dmix->hw_ptr += diff; + dmix->hw_ptr %= pcm->boundary; + if (pcm->stop_threshold >= pcm->boundary) /* don't care */ +-- +2.31.1 + + +From dd609ef9684987d3ca61d5c5cc3c77589ff9c29f Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Mon, 21 Jun 2021 09:28:41 +0200 +Subject: [PATCH 06/20] pcm: direct plugins - fix hw_ptr in the status callback + +The parent hw_ptr may be in another range (boundary limit). +Set the correct value for the caller. + +BugLink: https://github.com/alsa-project/alsa-lib/issues/155 +Signed-off-by: Jaroslav Kysela +--- + src/pcm/pcm_dmix.c | 1 + + src/pcm/pcm_dshare.c | 1 + + src/pcm/pcm_dsnoop.c | 1 + + 3 files changed, 3 insertions(+) + +diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c +index 0d0d0bff..94dbb1e0 100644 +--- a/src/pcm/pcm_dmix.c ++++ b/src/pcm/pcm_dmix.c +@@ -491,6 +491,7 @@ static int snd_pcm_dmix_status(snd_pcm_t *pcm, snd_pcm_status_t * status) + } + + status->state = snd_pcm_dmix_state(pcm); ++ status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */ + status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */ + status->trigger_tstamp = dmix->trigger_tstamp; + status->avail = snd_pcm_mmap_playback_avail(pcm); +diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c +index a918512b..01814dc8 100644 +--- a/src/pcm/pcm_dshare.c ++++ b/src/pcm/pcm_dshare.c +@@ -243,6 +243,7 @@ static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status) + break; + } + status->state = snd_pcm_dshare_state(pcm); ++ status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */ + status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */ + status->trigger_tstamp = dshare->trigger_tstamp; + status->avail = snd_pcm_mmap_playback_avail(pcm); +diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c +index 2c3b9f43..3f28df99 100644 +--- a/src/pcm/pcm_dsnoop.c ++++ b/src/pcm/pcm_dsnoop.c +@@ -193,6 +193,7 @@ static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status) + snd_pcm_status(dsnoop->spcm, status); + state = snd_pcm_state(dsnoop->spcm); + status->state = state == SND_PCM_STATE_RUNNING ? dsnoop->state : state; ++ status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */ + status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */ + status->trigger_tstamp = dsnoop->trigger_tstamp; + status->avail = snd_pcm_mmap_capture_avail(pcm); +-- +2.31.1 + + +From a5e11f9a810391777ea7750f04ba66f9c9e624de Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Mon, 21 Jun 2021 14:21:26 +0200 +Subject: [PATCH 07/20] pcm: Move snd_pcm_channel_area_addr() and _step() to + public header + +Used in the rate plugins commonly. + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 23 +++++++++++++++++++++++ + src/pcm/pcm_local.h | 13 ------------- + 2 files changed, 23 insertions(+), 13 deletions(-) + +diff --git a/include/pcm.h b/include/pcm.h +index e300b951..b5a514fa 100644 +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -1173,6 +1173,29 @@ int snd_pcm_areas_copy_wrap(const snd_pcm_channel_area_t *dst_channels, + snd_pcm_uframes_t frames, + const snd_pcm_format_t format); + ++/** ++ * \brief get the address of the given PCM channel area ++ * \param area PCM channel area ++ * \param offset Offset in frames ++ * ++ * Returns the pointer corresponding to the given offset on the channel area. ++ */ ++static inline void *snd_pcm_channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset) ++{ ++ return (char *)area->addr + (area->first + area->step * offset) / 8; ++} ++ ++/** ++ * \brief get the step size of the given PCM channel area in bytes ++ * \param area PCM channel area ++ * ++ * Returns the step size in bytes from the given channel area. ++ */ ++static inline unsigned int snd_pcm_channel_area_step(const snd_pcm_channel_area_t *area) ++{ ++ return area->step / 8; ++} ++ + /** \} */ + + /** +diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h +index a63f4be0..6f03365c 100644 +--- a/src/pcm/pcm_local.h ++++ b/src/pcm/pcm_local.h +@@ -632,19 +632,6 @@ static inline snd_pcm_sframes_t snd_pcm_mmap_delay(snd_pcm_t *pcm) + return snd_pcm_mmap_capture_delay(pcm); + } + +-static inline void *snd_pcm_channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset) +-{ +- unsigned int bitofs = area->first + area->step * offset; +- assert(bitofs % 8 == 0); +- return (char *) area->addr + bitofs / 8; +-} +- +-static inline unsigned int snd_pcm_channel_area_step(const snd_pcm_channel_area_t *area) +-{ +- assert(area->step % 8 == 0); +- return area->step / 8; +-} +- + static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) + { + /* lock handled in the callback */ +-- +2.31.1 + + +From e0e084659083c2ab75d5c894f24227ea2f67010f Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Mon, 21 Jun 2021 15:14:18 +0200 +Subject: [PATCH 08/20] pcm: direct plugins - fix bad memory access when + channel bindings do not match hw + +Fix and cleanup snd_pcm_direct_check_interleave() function. +Add requested / hardware channel check and use goto when the interleaved + +Fixes: https://github.com/alsa-project/alsa-lib/issues/117 +Signed-off-by: Jaroslav Kysela +--- + src/pcm/pcm_direct.c | 40 +++++++++++++++++----------------------- + 1 file changed, 17 insertions(+), 23 deletions(-) + +diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c +index d50503e3..90417b2f 100644 +--- a/src/pcm/pcm_direct.c ++++ b/src/pcm/pcm_direct.c +@@ -1627,43 +1627,37 @@ int snd_pcm_direct_set_timer_params(snd_pcm_direct_t *dmix) + int snd_pcm_direct_check_interleave(snd_pcm_direct_t *dmix, snd_pcm_t *pcm) + { + unsigned int chn, channels; +- int bits, interleaved = 1; ++ int bits; + const snd_pcm_channel_area_t *dst_areas; + const snd_pcm_channel_area_t *src_areas; + + bits = snd_pcm_format_physical_width(pcm->format); + if ((bits % 8) != 0) +- interleaved = 0; ++ goto __nointerleaved; + channels = dmix->channels; ++ if (channels != dmix->spcm->channels) ++ goto __nointerleaved; + dst_areas = snd_pcm_mmap_areas(dmix->spcm); + src_areas = snd_pcm_mmap_areas(pcm); + for (chn = 1; chn < channels; chn++) { +- if (dst_areas[chn-1].addr != dst_areas[chn].addr) { +- interleaved = 0; +- break; +- } +- if (src_areas[chn-1].addr != src_areas[chn].addr) { +- interleaved = 0; +- break; +- } ++ if (dst_areas[chn-1].addr != dst_areas[chn].addr) ++ goto __nointerleaved; ++ if (src_areas[chn-1].addr != src_areas[chn].addr) ++ goto __nointerleaved; + } + for (chn = 0; chn < channels; chn++) { +- if (dmix->bindings && dmix->bindings[chn] != chn) { +- interleaved = 0; +- break; +- } ++ if (dmix->bindings && dmix->bindings[chn] != chn) ++ goto __nointerleaved; + if (dst_areas[chn].first != chn * bits || +- dst_areas[chn].step != channels * bits) { +- interleaved = 0; +- break; +- } ++ dst_areas[chn].step != channels * bits) ++ goto __nointerleaved; + if (src_areas[chn].first != chn * bits || +- src_areas[chn].step != channels * bits) { +- interleaved = 0; +- break; +- } ++ src_areas[chn].step != channels * bits) ++ goto __nointerleaved; + } +- return dmix->interleaved = interleaved; ++ return dmix->interleaved = 1; ++__nointerleaved: ++ return dmix->interleaved = 0; + } + + /* +-- +2.31.1 + + +From ccc14ae897d170156f1c2905ea5d18a3295e7b36 Mon Sep 17 00:00:00 2001 +From: "Tanjeff-N. Moos" +Date: Thu, 17 Jun 2021 10:36:38 +0200 +Subject: [PATCH 09/20] control: Add documentation for snd_ctl_card_* and + friends. + +In this patch series, I added a description about control interface +handling and how control interfaces are identified. + +In addition, I added/improved Doxygen documentation for the +snd_ctl_card_info_t type and related corresponding functions, +e.g. snd_ctl_card_info(). I also documented other card-related like +snd_card_next(). + +Along the way I did minor documentation improvements. + +Signed-off-by: Tanjeff-N. Moos +Signed-off-by: Takashi Iwai +--- + include/control.h | 40 +++++++++- + src/control/cards.c | 56 +++++++++----- + src/control/control.c | 175 +++++++++++++++++++++++++++++++----------- + 3 files changed, 202 insertions(+), 69 deletions(-) + +diff --git a/include/control.h b/include/control.h +index ccf906e2..9ebb4905 100644 +--- a/include/control.h ++++ b/include/control.h +@@ -50,7 +50,32 @@ typedef struct snd_aes_iec958 { + unsigned char dig_subframe[4]; /**< AES/IEC958 subframe bits */ + } snd_aes_iec958_t; + +-/** CTL card info container */ ++/** \brief CTL card info container. ++ * ++ * This type contains meta information about a sound card, such as the index, ++ * name, longname, etc. ++ * ++ * \par Memory management ++ * ++ * Before using a snd_ctl_card_info_t object, it must be allocated using ++ * snd_ctl_card_info_alloca() or snd_ctl_card_info_malloc(). When using the ++ * latter, it must be freed again using snd_ctl_card_info_free(). ++ * ++ * A card info object can be zeroed out using snd_ctl_card_info_clear(). ++ * ++ * A card info object can be copied to another one using ++ * snd_ctl_card_info_copy(). ++ * ++ * \par Obtaining the Information ++ * ++ * To obtain the card information, it must first be opened using ++ * snd_ctl_open(), and a snd_ctl_card_info_t container must be ++ * allocated. Then, the information can be read using ++ * snd_ctl_card_info_get_card(). ++ * ++ * Thereafter, the card properties can be read using the ++ * snd_ctl_card_info_get_*() functions. ++ */ + typedef struct _snd_ctl_card_info snd_ctl_card_info_t; + + /** CTL element identifier container */ +@@ -442,11 +467,20 @@ void snd_ctl_elem_id_set_name(snd_ctl_elem_id_t *obj, const char *val); + void snd_ctl_elem_id_set_index(snd_ctl_elem_id_t *obj, unsigned int val); + + size_t snd_ctl_card_info_sizeof(void); ++ + /** \hideinitializer +- * \brief allocate an invalid #snd_ctl_card_info_t using standard alloca +- * \param ptr returned pointer ++ * \brief Allocate an invalid #snd_ctl_card_info_t on the stack. ++ * ++ * Allocate space for a card info object on the stack. The allocated ++ * memory need not be freed, because it is on the stack. ++ * ++ * See snd_ctl_card_info_t for details. ++ * ++ * \param ptr Pointer to a snd_ctl_elem_value_t pointer. The address ++ * of the allocated space will returned here. + */ + #define snd_ctl_card_info_alloca(ptr) __snd_alloca(ptr, snd_ctl_card_info) ++ + int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr); + void snd_ctl_card_info_free(snd_ctl_card_info_t *obj); + void snd_ctl_card_info_clear(snd_ctl_card_info_t *obj); +diff --git a/src/control/cards.c b/src/control/cards.c +index e57457c2..8226c42d 100644 +--- a/src/control/cards.c ++++ b/src/control/cards.c +@@ -77,8 +77,8 @@ static int snd_card_load1(int card) + + /** + * \brief Try to load the driver for a card. +- * \param card Card number. +- * \return 1 if driver is present, zero if driver is not present ++ * \param card Card index. ++ * \return 1 if driver is present, zero if driver is not present. + */ + int snd_card_load(int card) + { +@@ -86,14 +86,24 @@ int snd_card_load(int card) + } + + /** +- * \brief Try to determine the next card. +- * \param rcard pointer to card number +- * \result zero if success, otherwise a negative error code ++ * \brief Iterate over physical sound cards. ++ * ++ * This function takes the index of a physical sound card and sets it to the ++ * index of the next card. If index is -1, it is set to the index of the first ++ * card. After the last card, the index is set to -1. ++ * ++ * For example, if you have 2 sound cards (with index 0 and 1), the index will ++ * be modified as follows: ++ * ++ * - -1 --> 0 ++ * - 0 --> 1 ++ * - 1 --> -1 ++ * ++ * This does not work for virtual sound cards. + * +- * Tries to determine the next card from given card number. +- * If card number is -1, then the first available card is +- * returned. If the result card number is -1, no more cards +- * are available. ++ * \param rcard Index of current card. The index of the next card is stored ++ * here. ++ * \result zero if success, otherwise a negative error code. + */ + int snd_card_next(int *rcard) + { +@@ -114,13 +124,18 @@ int snd_card_next(int *rcard) + } + + /** +- * \brief Convert card string to an integer value. +- * \param string String containing card identifier +- * \return zero if success, otherwise a negative error code ++ * \brief Convert a card string to the card index. + * +- * The accepted format is an integer value in ASCII representation +- * or the card identifier (the id parameter for sound-card drivers). +- * The control device name like /dev/snd/controlC0 is accepted, too. ++ * This works only for physical sound cards, not for virtual cards. ++ * ++ * \param string A string identifying the card. ++ * \return The index of the card. On error, a a negative error code ++ * is returned. ++ * ++ * The accepted formats for "string" are: ++ * - The index of the card (as listed in /proc/asound/cards), given as string ++ * - The ID of the card (as listed in /proc/asound/cards) ++ * - The control device name (like /dev/snd/controlC0) + */ + int snd_card_get_index(const char *string) + { +@@ -163,8 +178,9 @@ int snd_card_get_index(const char *string) + + /** + * \brief Obtain the card name. +- * \param card Card number +- * \param name Result - card name corresponding to card number ++ * ++ * \param card The index of the card. ++ * \param name Result - card name corresponding to card index. + * \result zero if success, otherwise a negative error code + * + * The value returned in name is allocated with strdup and should be +@@ -193,9 +209,9 @@ int snd_card_get_name(int card, char **name) + + /** + * \brief Obtain the card long name. +- * \param card Card number +- * \param name Result - card long name corresponding to card number +- * \result zero if success, otherwise a negative error code ++ * \param card Index of the card. ++ * \param name Result - card long name corresponding to card index. ++ * \result Zero if success, otherwise a negative error code. + * + * The value returned in name is allocated with strdup and should be + * freed when no longer used. +diff --git a/src/control/control.c b/src/control/control.c +index ed986e54..7cf4decb 100644 +--- a/src/control/control.c ++++ b/src/control/control.c +@@ -33,8 +33,50 @@ +

Control interface is designed to access primitive controls. There is + also an interface for notifying about control and structure changes. + ++ + \section control_general_overview General overview + ++In Alsa, there are physical sound cards, such as USB headsets, and ++virtual sound cards, such as "pulse", which represents the PulseAudio ++Sound system. Each sound card offers a control interface, making its ++settings (e.g. volume knobs) available. The complete list of available ++control interfaces can be obtained using snd_device_name_hint(), ++giving -1 as card index and "ctl" as interface type. Each returned ++NAME hint identifies a control interface. ++ ++Sound cards have an ID (a string), an index (an int, sometimes called ++the "card number"), a name, a longname, a mixername and a "components" ++property. The file /proc/asound/cards lists most of these properties ++for physical sound cards. Virtual sound cards are not listed in that ++file. The format is: ++ ++\verbatim ++index [ID ] Driver - name ++ longname ++\endverbatim ++ ++Note that the mixername and components are not listed. ++ ++ ++\subsection control_cards_id Identifying and Opening Control Interfaces ++ ++To work with a control interface, is must be opened first, using ++snd_ctl_open(). This function takes the interface name. ++ ++For physical sound cards, the control interface can be identified ++using the string "hw:" (e.g. `hw:2`). The NAME hint - which is ++"hw:CARD=" - can also be used. Further, its device file (something ++like `/dev/snd/controlC0`) is also acceptable. Either of them can be ++given to snd_ctl_open(). ++ ++For virtual sound cards, the NAME hint is given to snd_ctl_open(). ++ ++The functions snd_card_get_index(), snd_card_get_name() and ++snd_card_get_longname() can be used to find an identifying property ++when another one is already known. ++ ++\section control_elements Elements ++ + In ALSA control feature, each sound card can have control elements. The elements + are managed according to below model. + +@@ -65,7 +107,7 @@ are managed according to below model. + of userspace applications and drivers in kernel. + + +-\section identifying_elements Identifying Elements ++\subsection identifying_elements Identifying Elements + + Each element has the following identifying properties: + +@@ -84,7 +126,7 @@ but in practice this is rare). The numid can change on each boot. + In case of an USB sound card, the numid can also change when it + is reconnected. The short numid is used to reduce the lookup time. + +-\section element_lists Element Lists ++\subsection element_lists Element Lists + + An element list can be used to obtain a list of all elements of the + sound card. The list contains generic information (e.g. how many +@@ -93,7 +135,7 @@ elements the card has), and the identifying properties of the elements + element lists. + + +-\section working_with_elements Working with Elements ++\subsection working_with_elements Working with Elements + + It is possible to obtain information about an element using the + snd_ctl_elem_info_*() functions. For enums, the allowed values can be +@@ -108,7 +150,7 @@ actual values or settings. It is also possible to get and set the ID + values (such as the numid or the name). + + +-\section element_sets Element Sets ++\subsection element_sets Element Sets + + The type of element set is one of integer, integer64, boolean, enumerators, + bytes and IEC958 structure. This indicates the type of value for each member in +@@ -329,10 +371,15 @@ int snd_ctl_subscribe_events(snd_ctl_t *ctl, int subscribe) + + + /** +- * \brief Get card related information +- * \param ctl CTL handle +- * \param info Card info pointer +- * \return 0 on success otherwise a negative error code ++ * \brief Get information about the sound card. ++ * ++ * Obtain information about the sound card previously opened using ++ * snd_ctl_open(). The object "info" must be allocated prior to calling this ++ * function. See snd_ctl_card_info_t for details. ++ * ++ * \param ctl The CTL handle. ++ * \param info The card information is stored here. ++ * \return 0 on success, otherwise a negative error code. + */ + int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info) + { +@@ -1508,11 +1555,13 @@ int _snd_ctl_open_named_child(snd_ctl_t **pctl, const char *name, + #endif + + /** +- * \brief Opens a CTL +- * \param ctlp Returned CTL handle +- * \param name ASCII identifier of the CTL handle +- * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC) +- * \return 0 on success otherwise a negative error code ++ * \brief Opens a sound card. ++ * ++ * \param ctlp Returned CTL handle. ++ * \param name A string identifying the card (See \ref control_cards_id). ++ * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC). ++ * ++ * \return 0 on success otherwise a negative error code. + */ + int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode) + { +@@ -2027,8 +2076,8 @@ void snd_ctl_elem_id_set_index(snd_ctl_elem_id_t *obj, unsigned int val) + } + + /** +- * \brief get size of #snd_ctl_card_info_t +- * \return size in bytes ++ * \brief get size of #snd_ctl_card_info_t. ++ * \return Size in bytes. + */ + size_t snd_ctl_card_info_sizeof() + { +@@ -2036,9 +2085,16 @@ size_t snd_ctl_card_info_sizeof() + } + + /** +- * \brief allocate an invalid #snd_ctl_card_info_t using standard malloc +- * \param ptr returned pointer +- * \return 0 on success otherwise negative error code ++ * \brief Allocate an invalid #snd_ctl_card_info_t on the heap. ++ * ++ * Allocate space for a card info object on the heap. The allocated memory ++ * must be freed using snd_ctl_card_info_free(). ++ * ++ * See snd_ctl_card_info_t for details. ++ * ++ * \param ptr Pointer to a snd_ctl_card_info_t pointer. The address ++ * of the allocated space will be returned here. ++ * \return 0 on success, otherwise a negative error code. + */ + int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr) + { +@@ -2050,8 +2106,10 @@ int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr) + } + + /** +- * \brief frees a previously allocated #snd_ctl_card_info_t +- * \param obj pointer to object to free ++ * \brief Free an #snd_ctl_card_info_t previously allocated using ++ * snd_ctl_card_info_malloc(). ++ * ++ * \param obj Pointer to the snd_ctl_card_info_t. + */ + void snd_ctl_card_info_free(snd_ctl_card_info_t *obj) + { +@@ -2059,8 +2117,11 @@ void snd_ctl_card_info_free(snd_ctl_card_info_t *obj) + } + + /** +- * \brief clear given #snd_ctl_card_info_t object +- * \param obj pointer to object to clear ++ * \brief Clear given card info object. ++ * ++ * See snd_ctl_elem_value_t for details. ++ * ++ * \param obj Card info object. + */ + void snd_ctl_card_info_clear(snd_ctl_card_info_t *obj) + { +@@ -2068,9 +2129,10 @@ void snd_ctl_card_info_clear(snd_ctl_card_info_t *obj) + } + + /** +- * \brief copy one #snd_ctl_card_info_t to another +- * \param dst pointer to destination +- * \param src pointer to source ++ * \brief Bitwise copy of a #snd_ctl_card_info_t object. ++ * ++ * \param dst Pointer to destination. ++ * \param src Pointer to source. + */ + void snd_ctl_card_info_copy(snd_ctl_card_info_t *dst, const snd_ctl_card_info_t *src) + { +@@ -2079,9 +2141,12 @@ void snd_ctl_card_info_copy(snd_ctl_card_info_t *dst, const snd_ctl_card_info_t + } + + /** +- * \brief Get card number from a CTL card info +- * \param obj CTL card info +- * \return card number ++ * \brief Get the sound card index from the given info object. ++ * ++ * See snd_ctl_card_info_t for more details. ++ * ++ * \param obj The card info object. ++ * \return Sound card index. + */ + int snd_ctl_card_info_get_card(const snd_ctl_card_info_t *obj) + { +@@ -2090,9 +2155,12 @@ int snd_ctl_card_info_get_card(const snd_ctl_card_info_t *obj) + } + + /** +- * \brief Get card identifier from a CTL card info +- * \param obj CTL card info +- * \return card identifier ++ * \brief Get the sound card ID from the given info object. ++ * ++ * See snd_ctl_card_info_t for more details. ++ * ++ * \param obj The card info object. ++ * \return Sound card ID. + */ + const char *snd_ctl_card_info_get_id(const snd_ctl_card_info_t *obj) + { +@@ -2101,9 +2169,12 @@ const char *snd_ctl_card_info_get_id(const snd_ctl_card_info_t *obj) + } + + /** +- * \brief Get card driver name from a CTL card info +- * \param obj CTL card info +- * \return card driver name ++ * \brief Get the sound card driver from the given info object. ++ * ++ * See snd_ctl_card_info_t for more details. ++ * ++ * \param obj The card info object. ++ * \return The sound card driver. + */ + const char *snd_ctl_card_info_get_driver(const snd_ctl_card_info_t *obj) + { +@@ -2112,9 +2183,12 @@ const char *snd_ctl_card_info_get_driver(const snd_ctl_card_info_t *obj) + } + + /** +- * \brief Get card name from a CTL card info +- * \param obj CTL card info +- * \return card name ++ * \brief Get the sound card name from the given info object. ++ * ++ * See snd_ctl_card_info_t for more details. ++ * ++ * \param obj The card info object. ++ * \return Sound card name. + */ + const char *snd_ctl_card_info_get_name(const snd_ctl_card_info_t *obj) + { +@@ -2123,9 +2197,12 @@ const char *snd_ctl_card_info_get_name(const snd_ctl_card_info_t *obj) + } + + /** +- * \brief Get card long name from a CTL card info +- * \param obj CTL card info +- * \return card long name ++ * \brief Get the sound cards long name from the given info object. ++ * ++ * See snd_ctl_card_info_t for more details. ++ * ++ * \param obj The card info object. ++ * \return Sound cards long name. + */ + const char *snd_ctl_card_info_get_longname(const snd_ctl_card_info_t *obj) + { +@@ -2134,9 +2211,12 @@ const char *snd_ctl_card_info_get_longname(const snd_ctl_card_info_t *obj) + } + + /** +- * \brief Get card mixer name from a CTL card info +- * \param obj CTL card info +- * \return card mixer name ++ * \brief Get the sound card mixer name from the given info object. ++ * ++ * See snd_ctl_card_info_t for more details. ++ * ++ * \param obj The card info object. ++ * \return Sound card mixer name. + */ + const char *snd_ctl_card_info_get_mixername(const snd_ctl_card_info_t *obj) + { +@@ -2145,9 +2225,12 @@ const char *snd_ctl_card_info_get_mixername(const snd_ctl_card_info_t *obj) + } + + /** +- * \brief Get card component list from a CTL card info +- * \param obj CTL card info +- * \return card mixer identifier ++ * \brief Get the sound cards "components" property from the given info object. ++ * ++ * See snd_ctl_card_info_t for more details. ++ * ++ * \param obj The card info object. ++ * \return Sound cards "components" property. + */ + const char *snd_ctl_card_info_get_components(const snd_ctl_card_info_t *obj) + { +-- +2.31.1 + + +From 7ba3f888d07cafbad04391b915db23408c663dad Mon Sep 17 00:00:00 2001 +From: "Tanjeff-N. Moos" +Date: Thu, 17 Jun 2021 10:36:39 +0200 +Subject: [PATCH 10/20] control: Minor documentation fixes. + +Signed-off-by: Tanjeff-N. Moos +Signed-off-by: Takashi Iwai +--- + include/control.h | 9 +++++++-- + src/control/cards.c | 5 ++++- + src/control/control.c | 2 +- + 3 files changed, 12 insertions(+), 4 deletions(-) + +diff --git a/include/control.h b/include/control.h +index 9ebb4905..e386ecec 100644 +--- a/include/control.h ++++ b/include/control.h +@@ -155,7 +155,7 @@ typedef struct _snd_ctl_elem_list snd_ctl_elem_list_t; + /** CTL element info container */ + typedef struct _snd_ctl_elem_info snd_ctl_elem_info_t; + +-/** CTL element value container ++/** CTL element value container. + * + * Contains the value(s) (i.e. members) of a single element. All + * values of a given element are of the same type. +@@ -167,6 +167,11 @@ typedef struct _snd_ctl_elem_info snd_ctl_elem_info_t; + * using the latter, it must be freed again using + * snd_ctl_elem_value_free(). + * ++ * A value object can be zeroed out using snd_ctl_elem_value_clear(). ++ * ++ * A value object can be copied to another one using ++ * snd_ctl_elem_value_copy(). ++ * + * \par Identifier + * + * Then, the ID must be filled. It is sufficient to fill only the +@@ -621,7 +626,7 @@ size_t snd_ctl_elem_value_sizeof(void); + * \brief Allocate an invalid #snd_ctl_elem_value_t on the stack. + * + * Allocate space for a value object on the stack. The allocated +- * memory need not be freed, because is on the stack. ++ * memory need not be freed, because it is on the stack. + * + * See snd_ctl_elem_value_t for details. + * +diff --git a/src/control/cards.c b/src/control/cards.c +index 8226c42d..6145ebcd 100644 +--- a/src/control/cards.c ++++ b/src/control/cards.c +@@ -147,6 +147,7 @@ int snd_card_get_index(const char *string) + return -EINVAL; + if ((isdigit(*string) && *(string + 1) == 0) || + (isdigit(*string) && isdigit(*(string + 1)) && *(string + 2) == 0)) { ++ /* We got an index */ + if (sscanf(string, "%i", &card) != 1) + return -EINVAL; + if (card < 0 || card >= SND_MAX_CARDS) +@@ -156,8 +157,10 @@ int snd_card_get_index(const char *string) + return card; + return err; + } +- if (string[0] == '/') /* device name */ ++ if (string[0] == '/') ++ /* We got a device name */ + return snd_card_load2(string); ++ /* We got in ID */ + for (card = 0; card < SND_MAX_CARDS; card++) { + #ifdef SUPPORT_ALOAD + if (! snd_card_load(card)) +diff --git a/src/control/control.c b/src/control/control.c +index 7cf4decb..91415b51 100644 +--- a/src/control/control.c ++++ b/src/control/control.c +@@ -3127,7 +3127,7 @@ size_t snd_ctl_elem_value_sizeof() + /** + * \brief Allocate an invalid #snd_ctl_elem_value_t on the heap. + * +- * Allocate space for a value object on the head. The allocated memory ++ * Allocate space for a value object on the heap. The allocated memory + * must be freed using snd_ctl_elem_value_free(). + * + * See snd_ctl_elem_value_t for details. +-- +2.31.1 + + +From f4f29d42be8b8ad60ea4c5697374adad4bfe6868 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Mon, 28 Jun 2021 12:08:53 +0200 +Subject: [PATCH 11/20] fix build with --disable-ucm + +Link: https://mailman.alsa-project.org/pipermail/alsa-devel/2021-June/186729.html +Reported-by: Michael Forney +Signed-off-by: Jaroslav Kysela +--- + include/local.h | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/include/local.h b/include/local.h +index 4e7d88a0..7cfcec53 100644 +--- a/include/local.h ++++ b/include/local.h +@@ -374,6 +374,8 @@ int _snd_config_load_with_include(snd_config_t *config, snd_input_t *in, + void *INTERNAL(snd_dlopen)(const char *name, int mode, char *errbuf, size_t errbuflen); + #endif + ++#ifdef BUILD_UCM ++ + const char *uc_mgr_alibcfg_by_device(snd_config_t **config, const char *name); + + static inline int _snd_is_ucm_device(const char *name) +@@ -381,4 +383,12 @@ static inline int _snd_is_ucm_device(const char *name) + return name && name[0] == '_' && name[1] == 'u' && name[2] == 'c' && name[3] == 'm'; + } + ++#else ++ ++static inline const char *uc_mgr_alibcfg_by_device(snd_config_t **config, const char *name) { return NULL; } ++static inline int _snd_is_ucm_device(const char *name) { return 0; } ++ ++ ++#endif ++ + #endif +-- +2.31.1 + + +From 1a1f0fb244c477c430e156da878475ef57d198f9 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Mon, 28 Jun 2021 12:11:54 +0200 +Subject: [PATCH 12/20] pcm: rate - fix some gcc warnings + +Signed-off-by: Jaroslav Kysela +--- + src/pcm/pcm_rate.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c +index 13111d29..c45895a9 100644 +--- a/src/pcm/pcm_rate.c ++++ b/src/pcm/pcm_rate.c +@@ -82,12 +82,12 @@ struct _snd_pcm_rate { + + /* allocate a channel area and a temporary buffer for the given size */ + static snd_pcm_channel_area_t * +-rate_alloc_tmp_buf(snd_pcm_rate_t *rate, snd_pcm_format_t format, ++rate_alloc_tmp_buf(snd_pcm_format_t format, + unsigned int channels, unsigned int frames) + { + snd_pcm_channel_area_t *ap; + int width = snd_pcm_format_physical_width(format); +- int i; ++ unsigned int i; + + ap = malloc(sizeof(*ap) * channels); + if (!ap) +@@ -354,7 +354,7 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + snd_pcm_rate_t *rate = pcm->private_data; + snd_pcm_t *slave = rate->gen.slave; + snd_pcm_rate_side_info_t *sinfo, *cinfo; +- unsigned int channels, cwidth, swidth, chn, acc; ++ unsigned int channels, acc; + int need_src_buf, need_dst_buf; + int err = snd_pcm_hw_params_slave(pcm, params, + snd_pcm_rate_hw_refine_cchange, +@@ -401,9 +401,9 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + return -EBUSY; + } + +- rate->pareas = rate_alloc_tmp_buf(rate, cinfo->format, channels, ++ rate->pareas = rate_alloc_tmp_buf(cinfo->format, channels, + cinfo->period_size); +- rate->sareas = rate_alloc_tmp_buf(rate, sinfo->format, channels, ++ rate->sareas = rate_alloc_tmp_buf(sinfo->format, channels, + sinfo->period_size); + if (!rate->pareas || !rate->sareas) { + err = -ENOMEM; +@@ -442,7 +442,7 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + rate->src_conv_idx = + snd_pcm_linear_convert_index(rate->orig_in_format, + rate->info.in.format); +- rate->src_buf = rate_alloc_tmp_buf(rate, rate->info.in.format, ++ rate->src_buf = rate_alloc_tmp_buf(rate->info.in.format, + channels, rate->info.in.period_size); + if (!rate->src_buf) { + err = -ENOMEM; +@@ -454,7 +454,7 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + rate->dst_conv_idx = + snd_pcm_linear_convert_index(rate->info.out.format, + rate->orig_out_format); +- rate->dst_buf = rate_alloc_tmp_buf(rate, rate->info.out.format, ++ rate->dst_buf = rate_alloc_tmp_buf(rate->info.out.format, + channels, rate->info.out.period_size); + if (!rate->dst_buf) { + err = -ENOMEM; +-- +2.31.1 + + +From e47c11822d6b459a9b3704b3ee6a4a5c9a1b85be Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Tue, 29 Jun 2021 18:02:27 +0200 +Subject: [PATCH 13/20] control: remap - assign right name to the child handle + for no-op + +Fixes: https://github.com/alsa-project/alsa-utils/issues/100 +Signed-off-by: Jaroslav Kysela +--- + src/control/control_remap.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/control/control_remap.c b/src/control/control_remap.c +index a85c1725..81524014 100644 +--- a/src/control/control_remap.c ++++ b/src/control/control_remap.c +@@ -1173,6 +1173,10 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema + /* no-op check, remove the plugin */ + if (priv->map_items == 0 && priv->remap_items == 0) { + remap_free(priv); ++ free(child->name); ++ child->name = name ? strdup(name) : NULL; ++ if (name && !child->name) ++ return -ENOMEM; + *handlep = child; + return 0; + } +-- +2.31.1 + + +From 23a191a82c693456e61431ab699cddc1e5782a26 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Tue, 29 Jun 2021 19:31:28 +0200 +Subject: [PATCH 14/20] control: remap - assign right name to the child handle + for no-op (2nd case) + +Fixes: https://github.com/alsa-project/alsa-utils/issues/100 +Signed-off-by: Jaroslav Kysela +--- + src/control/control_remap.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/control/control_remap.c b/src/control/control_remap.c +index 81524014..4914f960 100644 +--- a/src/control/control_remap.c ++++ b/src/control/control_remap.c +@@ -1154,6 +1154,10 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema + snd_ctl_t *ctl; + int result, err; + ++ /* no-op, remove the plugin */ ++ if (!remap && !map) ++ goto _noop; ++ + priv = calloc(1, sizeof(*priv)); + if (priv == NULL) + return -ENOMEM; +@@ -1173,6 +1177,7 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema + /* no-op check, remove the plugin */ + if (priv->map_items == 0 && priv->remap_items == 0) { + remap_free(priv); ++ _noop: + free(child->name); + child->name = name ? strdup(name) : NULL; + if (name && !child->name) +@@ -1316,11 +1321,6 @@ int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd + err = _snd_ctl_open_child(&cctl, root, child, mode, conf); + if (err < 0) + return err; +- /* no-op, remove the plugin */ +- if (!remap && !map) { +- *handlep = cctl; +- return 0; +- } + err = snd_ctl_remap_open(handlep, name, remap, map, cctl, mode); + if (err < 0) + snd_ctl_close(cctl); +-- +2.31.1 + + +From 7d40a76ef5494e08af00fa4e7bfefbd43aba4827 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Tue, 31 Aug 2021 09:25:12 +0200 +Subject: [PATCH 19/20] ucm: avoid zero card instance number + +Signed-off-by: Jaroslav Kysela +--- + src/ucm/utils.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/ucm/utils.c b/src/ucm/utils.c +index f3a8c9ba..10b21c34 100644 +--- a/src/ucm/utils.c ++++ b/src/ucm/utils.c +@@ -780,6 +780,9 @@ int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr) + while (uc_mgr_card_find(ucm_card_assign)) { + ucm_card_assign++; + ucm_card_assign &= 0xffff; ++ /* avoid zero card instance number */ ++ if (ucm_card_assign == 0) ++ ucm_card_assign++; + if (ucm_card_assign == prev) { + pthread_mutex_unlock(&ucm_cards_mutex); + return -ENOMEM; +-- +2.31.1 + + +From 4a52ae4c329ae17117375a4b85b80f37994a4044 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Tue, 31 Aug 2021 09:40:42 +0200 +Subject: [PATCH 20/20] ucm: fix the parsing of the hexadecimal prefix + +The safe_strtol() function use strtol() which expects +to have the '0x' prefix for the hexadecimal number (when +base argument is zero). + +BugLink: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/1553 +Signed-off-by: Jaroslav Kysela +--- + src/ucm/utils.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/src/ucm/utils.c b/src/ucm/utils.c +index 10b21c34..2fbc4c8c 100644 +--- a/src/ucm/utils.c ++++ b/src/ucm/utils.c +@@ -811,7 +811,7 @@ void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr) + */ + const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name) + { +- char buf[5]; ++ char buf[7]; + long card_num; + snd_config_t *config; + snd_use_case_mgr_t *uc_mgr; +@@ -819,8 +819,10 @@ const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name) + + if (strncmp(name, "_ucm", 4) || strlen(name) < 12 || name[8] != '.') + return NULL; +- strncpy(buf, name + 4, 4); +- buf[4] = '\0'; ++ buf[0] = '0'; ++ buf[1] = 'x'; ++ strncpy(buf + 2, name + 4, 4); ++ buf[6] = '\0'; + err = safe_strtol(buf, &card_num); + if (err < 0 || card_num < 0 || card_num > 0xffff) + return NULL; +-- +2.31.1 + diff --git a/alsa-lib.spec b/alsa-lib.spec index eff5b83..fe19b44 100644 --- a/alsa-lib.spec +++ b/alsa-lib.spec @@ -9,7 +9,7 @@ Summary: The Advanced Linux Sound Architecture (ALSA) library Name: alsa-lib Version: %{version_alsa_lib} -Release: 2%{?prever_dot}%{?dist} +Release: 3%{?prever_dot}%{?dist} License: LGPLv2+ URL: http://www.alsa-project.org/ @@ -167,6 +167,9 @@ rm %{buildroot}/%{_includedir}/asoundlib.h %{_datadir}/alsa/topology %changelog +* Tue Aug 31 2021 Jaroslav Kysela - 1.2.5.1-3 +- add UCM related fixes + * Wed Jul 21 2021 Fedora Release Engineering - 1.2.5.1-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild