ucm: add info-card substitution (Syntax 9)

Implement ${info-card:} substitution to retrieve specific fields from
a card's information structure by card number or ID. Unlike ${find-card:}
which searches through cards using regex, ${info-card:} directly queries
a specific card.

Arguments:
  card=<STR>   card number or card ID (string identifier)
  field=<STR>  number, id, driver, name, longname, mixername, components

Allow card and field parameters to reference UCM variables by prefixing
with $ character. When the first character is $, the value is resolved
using uc_mgr_get_variable().

Example usage:
  ${info-card:card=$MyCard,field=$MyField}

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Jaroslav Kysela 2026-02-09 16:02:01 +01:00
parent 1823b4cd4b
commit c5d903b0b4
2 changed files with 125 additions and 0 deletions

View file

@ -616,6 +616,7 @@ ${var:\<str\>} | UCM parser variable (set using a _Define_ block)
${eval:\<str\>} | Evaluate expression like *($var+2)/3* [**Syntax 5**]
${find-card:\<str\>} | Find a card - see _Find card substitution_ section
${find-device:\<str\>} | Find a device - see _Find device substitution_ section
${info-card:\<str\>} | Get card information - see _Card info substitution_ section [**Syntax 9**]
General note: If two dollars '$$' instead one dolar '$' are used for the
substitution identification, the error is ignored (e.g. file does not
@ -689,6 +690,39 @@ stream | stream type (playback, capture), playback is default
field | field for the lookup (id, name, subname)
regex | regex string for the field match
#### Card info substitution
This substitution retrieves information about a specific ALSA card by card number
or card ID and returns the requested field value.
Usage examples:
~~~{.html}
${info-card:card=0,field=name}
${info-card:card=acp,field=driver}
${info-card:card=PCH,field=longname}
${info-card:card=$MyCard,field=$MyField}
~~~
Arguments:
Argument | Description
---------------------|--------------------------------------------------
card | card number (integer), card ID (string), or variable name ($var)
field | field to retrieve (number, id, driver, name, longname, mixername, components) or variable name ($var)
The **card** parameter can be either a card number (e.g., 0, 1, 2), a card ID string (e.g., "PCH", "acp", "Intel"),
or a variable name prefixed with $ (e.g., $CardId).
The **field** parameter specifies which card information to return or can be a variable name prefixed with $ (e.g., $FieldName):
- **number**: Card number (integer as string)
- **id**: Card identifier
- **driver**: Card driver name
- **name**: Card short name
- **longname**: Card long name
- **mixername**: Mixer name
- **components**: Card components
### Variable defines

View file

@ -204,6 +204,96 @@ static char *rval_card_id_by_name(snd_use_case_mgr_t *uc_mgr, const char *id)
return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info));
}
static char *rval_card_info(snd_use_case_mgr_t *uc_mgr, const char *query)
{
snd_config_t *config, *d;
const char *card_str, *field_str, *tmp;
struct ctl_list *ctl_list = NULL;
snd_ctl_card_info_t *info;
char *result = NULL;
long card_num;
int err;
if (uc_mgr->conf_format < 9) {
snd_error(UCM, "info-card substitution is supported in v9+ syntax");
return NULL;
}
err = snd_config_load_string(&config, query, 0);
if (err < 0) {
snd_error(UCM, "info-card: invalid arguments '%s'", query);
return NULL;
}
if (snd_config_search(config, "card", &d)) {
snd_error(UCM, "info-card: 'card' parameter is required");
goto __error;
}
if (snd_config_get_string(d, &card_str))
goto __error;
if (card_str[0] == '$') {
tmp = card_str + 1;
card_str = uc_mgr_get_variable(uc_mgr, tmp);
if (card_str == NULL) {
snd_error(UCM, "info-card: variable '%s' not found", tmp);
goto __error;
}
}
if (snd_config_search(config, "field", &d)) {
snd_error(UCM, "info-card: 'field' parameter is required");
goto __error;
}
if (snd_config_get_string(d, &field_str))
goto __error;
if (field_str[0] == '$') {
tmp = field_str + 1;
field_str = uc_mgr_get_variable(uc_mgr, tmp);
if (field_str == NULL) {
snd_error(UCM, "info-card: variable '%s' not found", tmp);
goto __error;
}
}
if (safe_strtol(card_str, &card_num) == 0)
ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, (int)card_num);
if (ctl_list == NULL)
ctl_list = get_ctl_list_by_name(uc_mgr, card_str);
if (ctl_list == NULL) {
snd_error(UCM, "info-card: card '%s' not found", card_str);
goto __error;
}
info = ctl_list->ctl_info;
if (strcasecmp(field_str, "number") == 0) {
char num[16];
snprintf(num, sizeof(num), "%d", snd_ctl_card_info_get_card(info));
result = strdup(num);
} else if (strcasecmp(field_str, "id") == 0) {
result = strdup(snd_ctl_card_info_get_id(info));
} else if (strcasecmp(field_str, "driver") == 0) {
result = strdup(snd_ctl_card_info_get_driver(info));
} else if (strcasecmp(field_str, "name") == 0) {
result = strdup(snd_ctl_card_info_get_name(info));
} else if (strcasecmp(field_str, "longname") == 0) {
result = strdup(snd_ctl_card_info_get_longname(info));
} else if (strcasecmp(field_str, "mixername") == 0) {
result = strdup(snd_ctl_card_info_get_mixername(info));
} else if (strcasecmp(field_str, "components") == 0) {
result = strdup(snd_ctl_card_info_get_components(info));
} else {
snd_error(UCM, "info-card: unknown field '%s'", field_str);
result = NULL;
}
__error:
snd_config_delete(config);
return result;
}
#ifndef DOC_HIDDEN
typedef struct lookup_iterate *(*lookup_iter_fcn_t)
(snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter);
@ -913,6 +1003,7 @@ __std:
MATCH_VARIABLE2(value, "${eval:", rval_eval, false);
MATCH_VARIABLE2(value, "${find-card:", rval_card_lookup, false);
MATCH_VARIABLE2(value, "${find-device:", rval_device_lookup, false);
MATCH_VARIABLE2(value, "${info-card:", rval_card_info, false);
MATCH_VARIABLE2(value, "${CardNumberByName:", rval_card_number_by_name, false);
MATCH_VARIABLE2(value, "${CardIdByName:", rval_card_id_by_name, false);
__merr: