#GDB="gdb --args"
#GDB="strace"
#GDB="valgrind --leak-check=yes --show-reachable=yes"
-GDB="perf stat"
+#GDB="perf stat"
+PROG=./alsaucm
+PROG=/home/perex/git/pipewire/builddir/spa/plugins/alsa/spa-acp-tool
+PROG="$HOME/git/pulseaudio/build/src/daemon/pulseaudio -n -F $HOME/git/pulseaudio/build/src/daemon/default.pa -p $HOME/git/pulseaudio/build/src/modules/"
+#PROG=pulseaudio
#ALSA_CONFIG_UCM="$HOME/alsa/alsa-ucm-conf/ucm" \
ALSA_CONFIG_UCM2="$HOME/alsa/alsa-ucm-conf/ucm2" \
LD_PRELOAD="$HOME/alsa/alsa-lib/src/.libs/libasound.so" \
-$GDB ./alsaucm "$@"
+$GDB $PROG "$@"
#include <time.h>
#include <locale.h>
#include <alsa/asoundlib.h>
+#include <alsa/use-case.h>
#include <assert.h>
#include <termios.h>
#include <signal.h>
return offset;
}
+static int open_ucm(snd_use_case_mgr_t **uc_mgr, char **pcm_name, const char *name)
+{
+ char *s, *p;
+ int err;
+
+ s = strdup(name);
+ if (s == NULL)
+ return -ENOMEM;
+ p = strchr(s, '.');
+ if (p == NULL)
+ return -EINVAL;
+ *p = '\0';
+ err = snd_use_case_mgr_open(uc_mgr, s);
+ if (err < 0)
+ return err;
+ err = snd_use_case_get(*uc_mgr, p + 1, (const char **)pcm_name);
+ if (err < 0) {
+ error(_("UCM value '%s' error: %s"), p + 1, snd_strerror(err));
+ snd_use_case_mgr_close(*uc_mgr);
+ return err;
+ }
+ return err;
+}
+
static long parse_long(const char *str, int *err)
{
long val;
int do_device_list = 0, do_pcm_list = 0, force_sample_format = 0;
snd_pcm_info_t *info;
FILE *direction;
+ snd_use_case_mgr_t *uc_mgr = NULL;
#ifdef ENABLE_NLS
setlocale(LC_ALL, "");
goto __end;
}
+ if (strncmp(pcm_name, "ucm.", 4) == 0) {
+ err = open_ucm(&uc_mgr, &pcm_name, pcm_name + 4);
+ if (err < 0) {
+ error(_("UCM open error: %s"), snd_strerror(err));
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, _("Found UCM PCM device: %s\n"), pcm_name);
+ }
+
err = snd_pcm_open(&handle, pcm_name, stream, open_mode);
if (err < 0) {
error(_("audio open error: %s"), snd_strerror(err));
if (verbose==2)
putchar('\n');
snd_pcm_close(handle);
+ if (uc_mgr)
+ snd_use_case_mgr_close(uc_mgr);
handle = NULL;
free(audiobuf);
__end:
free(sink_widget_name);
return ret;
}
-
-static int tplg_get_sample_size_from_format(const char *format)
-{
- if (!strcmp(format, "s32le") || !strcmp(format, "s24le") || !strcmp(format, "float"))
- return 4;
-
- if (!strcmp(format, "s16le"))
- return 2;
-
- SNDERR("Unsupported format: %s\n", format);
- return -EINVAL;
-}
-
-int tplg_update_buffer_auto_attr(struct tplg_pre_processor *tplg_pp,
- snd_config_t *buffer_cfg, snd_config_t *parent)
-{
- snd_config_iterator_t i, next;
- snd_config_t *n, *pipeline_cfg, *child;
- const char *buffer_id, *format;
- long periods, channels, sample_size;
- long sched_period, rate, frames;
- long buffer_size;
- int err;
-
- if (snd_config_get_id(buffer_cfg, &buffer_id) < 0)
- return -EINVAL;
-
- if (!parent) {
- SNDERR("No parent for buffer %s\n", buffer_id);
- return -EINVAL;
- }
-
- /* acquire attributes from buffer config */
- snd_config_for_each(i, next, buffer_cfg) {
- const char *id;
-
- n = snd_config_iterator_entry(i);
- if (snd_config_get_id(n, &id) < 0)
- continue;
-
- if (!strcmp(id, "periods")) {
- if (snd_config_get_integer(n, &periods)) {
- SNDERR("Invalid number of periods for buffer %s\n", buffer_id);
- return -EINVAL;
- }
- }
-
- if (!strcmp(id, "channels")) {
- if (snd_config_get_integer(n, &channels)) {
- SNDERR("Invalid number of channels for buffer %s\n", buffer_id);
- return -EINVAL;
- }
- }
-
- if (!strcmp(id, "format")) {
- if (snd_config_get_string(n, &format)) {
- SNDERR("Invalid format for buffer %s\n", buffer_id);
- return -EINVAL;
- }
- }
- }
-
- pipeline_cfg = tplg_object_get_instance_config(tplg_pp, parent);
-
- /* acquire some other attributes from parent pipeline config */
- snd_config_for_each(i, next, pipeline_cfg) {
- const char *id;
-
- n = snd_config_iterator_entry(i);
- if (snd_config_get_id(n, &id) < 0)
- continue;
-
- if (!strcmp(id, "period")) {
- if (snd_config_get_integer(n, &sched_period)) {
- SNDERR("Invalid period for buffer %s\n", buffer_id);
- return -EINVAL;
- }
- }
-
- if (!strcmp(id, "rate")) {
- if (snd_config_get_integer(n, &rate)) {
- SNDERR("Invalid rate for buffer %s\n", buffer_id);
- return -EINVAL;
- }
- }
- }
-
- /* calculate buffer size */
- sample_size = tplg_get_sample_size_from_format(format);
- if (sample_size < 0) {
- SNDERR("Invalid sample size value for %s\n", buffer_id);
- return sample_size;
- }
- frames = (rate * sched_period) / 1000000;
- buffer_size = periods * sample_size * channels * frames;
-
- /* add size child config to buffer config */
- err = tplg_config_make_add(&child, "size", SND_CONFIG_TYPE_INTEGER, buffer_cfg);
- if (err < 0) {
- SNDERR("Error creating size config for %s\n", buffer_id);
- return err;
- }
-
- err = snd_config_set_integer(child, buffer_size);
- if (err < 0)
- SNDERR("Error setting size config for %s\n", buffer_id);
-
- return err;
-}
&hwcfg_config},
{"Base", "fe_dai", "dai", &tplg_build_fe_dai_object, NULL, &fe_dai_config},
{"Base", "route", "SectionGraph", &tplg_build_dapm_route_object, NULL, NULL},
- {"Widget", "buffer", "SectionWidget", &tplg_build_generic_object,
- tplg_update_buffer_auto_attr, &widget_config},
+ {"Widget", "buffer", "SectionWidget", &tplg_build_generic_object, NULL, &widget_config},
{"Widget", "", "SectionWidget", &tplg_build_generic_object, NULL, &widget_config},
{"Control", "mixer", "SectionControlMixer", &tplg_build_mixer_control, NULL,
&mixer_control_config},
snd_config_t *attr_cfg,
snd_config_t *search_config)
{
- snd_config_t *attr, *new;
+ snd_config_iterator_t first = snd_config_iterator_first(obj);
+ snd_config_t *attr, *new, *first_cfg;
const char *id, *search_id;
int ret;
+ first_cfg = snd_config_iterator_entry(first);
+
if (snd_config_get_id(attr_cfg, &id) < 0)
return 0;
return ret;
}
- ret = snd_config_add(obj, new);
- if (ret < 0) {
- snd_config_delete(new);
- SNDERR("error adding attribute '%s' value to %s\n", id, search_id);
+ if (first_cfg) {
+ /* prepend the new config */
+ ret = snd_config_add_before(first_cfg, new);
+ if (ret < 0) {
+ snd_config_delete(new);
+ SNDERR("error prepending attribute '%s' value to %s\n", id, search_id);
+ }
+ } else {
+ ret = snd_config_add(obj, new);
+ if (ret < 0) {
+ snd_config_delete(new);
+ SNDERR("error adding attribute '%s' value to %s\n", id, search_id);
+ }
}
return ret;
return snd_config_iterator_entry(first);
}
+#if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION
+static int pre_process_find_variable(snd_config_t **dst, const char *str, snd_config_t *config)
+{
+ snd_config_iterator_t i, next;
+
+ snd_config_for_each(i, next, config) {
+ snd_config_t *n;
+ const char *id;
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ if (strcmp(id, str))
+ continue;
+
+ /* found definition, copy config */
+ return snd_config_copy(dst, n);
+ }
+
+ return -EINVAL;
+}
+static int
+pre_process_object_variables_expand_fcn(snd_config_t **dst, const char *str, void *private_data)
+{
+
+ struct tplg_pre_processor *tplg_pp = private_data;
+ snd_config_t *object_cfg = tplg_pp->current_obj_cfg;
+ snd_config_t *conf_defines;
+ const char *object_id;
+ int ret;
+
+ ret = snd_config_search(tplg_pp->input_cfg, "Define", &conf_defines);
+ if (ret < 0)
+ return 0;
+
+ /* find variable from global definitions first */
+ ret = pre_process_find_variable(dst, str, conf_defines);
+ if (ret >= 0)
+ return ret;
+
+ if (snd_config_get_id(object_cfg, &object_id) < 0)
+ return -EINVAL;
+
+ /* find variable from object attribute values if not found in global definitions */
+ ret = pre_process_find_variable(dst, str, object_cfg);
+ if (ret < 0)
+ SNDERR("Failed to find definition for attribute %s in '%s' object\n",
+ str, object_id);
+
+ return ret;
+}
+#endif
+
/* build object config and its child objects recursively */
static int tplg_build_object(struct tplg_pre_processor *tplg_pp, snd_config_t *new_obj,
snd_config_t *parent)
{
snd_config_t *obj_local, *class_cfg;
const struct build_function_map *map;
+ snd_config_iterator_t i, next;
build_func builder;
update_auto_attr_func auto_attr_updater;
const char *id, *class_id;
return ret;
}
+#if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION
+ tplg_pp_config_debug(tplg_pp, obj_local);
+
+ /* expand all non-compound type child configs in object */
+ snd_config_for_each(i, next, obj_local) {
+ snd_config_t *n, *new;
+ const char *id, *s;
+
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND)
+ continue;
+
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ if (snd_config_get_string(n, &s) < 0)
+ continue;
+
+ if (*s != '$')
+ continue;
+
+ tplg_pp->current_obj_cfg = obj_local;
+
+ /* expand config */
+ ret = snd_config_evaluate_string(&new, s, pre_process_object_variables_expand_fcn,
+ tplg_pp);
+ if (ret < 0) {
+ SNDERR("Failed to evaluate attributes %s in %s\n", id, class_id);
+ return ret;
+ }
+
+ snd_config_set_id(new, id);
+
+ ret = snd_config_merge(n, new, true);
+ if (ret < 0)
+ return ret;
+ }
+#endif
+
/*
* Build objects if object type is supported.
* If not, process object attributes and add to parent's data section
return 0;
}
-static int pre_process_variables_expand_fcn(snd_config_t **dst, const char *str,
- void *private_data)
-{
- struct tplg_pre_processor *tplg_pp = private_data;
- snd_config_iterator_t i, next;
- snd_config_t *conf_defines;
- int ret;
-
- ret = snd_config_search(tplg_pp->input_cfg, "Define", &conf_defines);
- if (ret < 0)
- return 0;
-
- /* find variable definition */
- snd_config_for_each(i, next, conf_defines) {
- snd_config_t *n;
- const char *id;
-
- n = snd_config_iterator_entry(i);
- if (snd_config_get_id(n, &id) < 0)
- continue;
-
- if (strcmp(id, str))
- continue;
-
- /* found definition. Match type and return appropriate config */
- if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) {
- const char *s;
-
- if (snd_config_get_string(n, &s) < 0)
- continue;
-
- return snd_config_imake_string(dst, NULL, s);
- }
-
- if (snd_config_get_type(n) == SND_CONFIG_TYPE_INTEGER) {
- long v;
-
- if (snd_config_get_integer(n, &v) < 0)
- continue;
-
- ret = snd_config_imake_integer(dst, NULL, v);
- return ret;
- }
-
- }
-
- fprintf(stderr, "No definition for variable %s\n", str);
-
- return -EINVAL;
-}
-
static int pre_process_includes(struct tplg_pre_processor *tplg_pp, snd_config_t *top,
const char *pre_processor_defs, const char *inc_path);
fprintf(stderr, "Failed to process conditional includes in input config\n");
goto err;
}
-
- /* expand pre-processor variables */
- err = snd_config_expand_custom(tplg_pp->input_cfg, tplg_pp->input_cfg, pre_process_variables_expand_fcn,
- tplg_pp, &tplg_pp->input_cfg);
- if (err < 0) {
- fprintf(stderr, "Failed to expand pre-processor definitions in input config\n");
- goto err;
- }
#endif
err = pre_process_config(tplg_pp, tplg_pp->input_cfg);
snd_config_t *obj_cfg, snd_config_t *parent);
int tplg_parent_update(struct tplg_pre_processor *tplg_pp, snd_config_t *parent,
const char *section_name, const char *item_name);
-int tplg_update_buffer_auto_attr(struct tplg_pre_processor *tplg_pp,
- snd_config_t *buffer_cfg, snd_config_t *parent);
int tplg_add_object_data(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
snd_config_t *top, const char *array_name);
snd_config_t *output_cfg;
snd_output_t *output;
snd_output_t *dbg_output;
+ snd_config_t *current_obj_cfg;
};
int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size,