mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
ucm: implemented card list feature
- also added some test files to test/ucm tree Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
3a34394508
commit
e820866637
6 changed files with 134 additions and 27 deletions
|
|
@ -561,16 +561,6 @@ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get list of cards in pair cardname+comment
|
|
||||||
* \param list Returned list
|
|
||||||
* \return Number of list entries if success, otherwise a negative error code
|
|
||||||
*/
|
|
||||||
static int get_card_list(const char **list[])
|
|
||||||
{
|
|
||||||
return -ENXIO; /* Not Yet Implemented */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Get list of verbs in pair verbname+comment
|
* \brief Get list of verbs in pair verbname+comment
|
||||||
* \param list Returned list
|
* \param list Returned list
|
||||||
|
|
@ -780,7 +770,7 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr,
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (uc_mgr == NULL || identifier == NULL)
|
if (uc_mgr == NULL || identifier == NULL)
|
||||||
return get_card_list(list);
|
return uc_mgr_scan_master_configs(list);
|
||||||
pthread_mutex_lock(&uc_mgr->mutex);
|
pthread_mutex_lock(&uc_mgr->mutex);
|
||||||
if (strcmp(identifier, "_verbs") == 0)
|
if (strcmp(identifier, "_verbs") == 0)
|
||||||
err = get_verb_list(uc_mgr, list);
|
err = get_verb_list(uc_mgr, list);
|
||||||
|
|
|
||||||
142
src/ucm/parser.c
142
src/ucm/parser.c
|
|
@ -31,6 +31,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ucm_local.h"
|
#include "ucm_local.h"
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
|
static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
|
||||||
struct list_head *base,
|
struct list_head *base,
|
||||||
|
|
@ -827,7 +828,7 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse and execute controls
|
* parse controls
|
||||||
*/
|
*/
|
||||||
static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
||||||
{
|
{
|
||||||
|
|
@ -851,6 +852,8 @@ static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
||||||
* #Example master file for blah sound card
|
* #Example master file for blah sound card
|
||||||
* #By Joe Blogs <joe@bloggs.org>
|
* #By Joe Blogs <joe@bloggs.org>
|
||||||
*
|
*
|
||||||
|
* Comment "Nice Abstracted Soundcard"
|
||||||
|
*
|
||||||
* # The file is divided into Use case sections. One section per use case verb.
|
* # The file is divided into Use case sections. One section per use case verb.
|
||||||
*
|
*
|
||||||
* SectionUseCase."Voice Call" {
|
* SectionUseCase."Voice Call" {
|
||||||
|
|
@ -882,7 +885,8 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
||||||
{
|
{
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
snd_config_t *n;
|
snd_config_t *n;
|
||||||
int ret;
|
const char *id;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
||||||
uc_error("compound type expected for master file");
|
uc_error("compound type expected for master file");
|
||||||
|
|
@ -891,26 +895,35 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
||||||
|
|
||||||
/* parse master config sections */
|
/* parse master config sections */
|
||||||
snd_config_for_each(i, next, cfg) {
|
snd_config_for_each(i, next, cfg) {
|
||||||
const char *id;
|
|
||||||
n = snd_config_iterator_entry(i);
|
n = snd_config_iterator_entry(i);
|
||||||
if (snd_config_get_id(n, &id) < 0)
|
if (snd_config_get_id(n, &id) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (strcmp(id, "Comment") == 0) {
|
||||||
|
err = parse_string(n, &uc_mgr->comment);
|
||||||
|
if (err < 0) {
|
||||||
|
uc_error("error: failed to get master comment");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* find use case section and parse it */
|
/* find use case section and parse it */
|
||||||
if (strcmp(id, "SectionUseCase") == 0) {
|
if (strcmp(id, "SectionUseCase") == 0) {
|
||||||
ret = parse_compound(uc_mgr, n,
|
err = parse_compound(uc_mgr, n,
|
||||||
parse_master_section,
|
parse_master_section,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
if (ret < 0)
|
if (err < 0)
|
||||||
return ret;
|
return err;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find default control values section and parse it */
|
/* find default control values section and parse it */
|
||||||
if (strcmp(id, "SectionDefaults") == 0) {
|
if (strcmp(id, "SectionDefaults") == 0) {
|
||||||
ret = parse_controls(uc_mgr, n);
|
err = parse_controls(uc_mgr, n);
|
||||||
if (ret < 0)
|
if (err < 0)
|
||||||
return ret;
|
return err;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
uc_error("uknown master file field %s", id);
|
uc_error("uknown master file field %s", id);
|
||||||
|
|
@ -918,25 +931,39 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* load master use case file for sound card */
|
/** The name of the environment variable containing the UCM directory */
|
||||||
int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
|
#define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM"
|
||||||
|
|
||||||
|
static int load_master_config(const char *card_name, snd_config_t **cfg)
|
||||||
{
|
{
|
||||||
char filename[MAX_FILE];
|
char filename[MAX_FILE];
|
||||||
snd_config_t *cfg;
|
char *env = getenv(ALSA_CONFIG_UCM_VAR);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
snprintf(filename, sizeof(filename)-1,
|
snprintf(filename, sizeof(filename)-1,
|
||||||
"%s/%s/%s.conf", ALSA_USE_CASE_DIR,
|
"%s/%s/%s.conf", env ? env : ALSA_USE_CASE_DIR,
|
||||||
uc_mgr->card_name, uc_mgr->card_name);
|
card_name, card_name);
|
||||||
filename[MAX_FILE-1] = '\0';
|
filename[MAX_FILE-1] = '\0';
|
||||||
|
|
||||||
err = uc_mgr_config_load(filename, &cfg);
|
err = uc_mgr_config_load(filename, cfg);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
uc_error("error: could not parse configuration for card %s",
|
uc_error("error: could not parse configuration for card %s",
|
||||||
uc_mgr->card_name);
|
card_name);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* load master use case file for sound card */
|
||||||
|
int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
|
||||||
|
{
|
||||||
|
snd_config_t *cfg;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = load_master_config(uc_mgr->card_name, &cfg);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
err = parse_master_file(uc_mgr, cfg);
|
err = parse_master_file(uc_mgr, cfg);
|
||||||
snd_config_delete(cfg);
|
snd_config_delete(cfg);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
@ -944,3 +971,86 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int filename_filter(const struct dirent *dirent)
|
||||||
|
{
|
||||||
|
if (dirent == NULL)
|
||||||
|
return 0;
|
||||||
|
if (dirent->d_type == DT_DIR) {
|
||||||
|
if (dirent->d_name[0] == '.') {
|
||||||
|
if (dirent->d_name[1] == '\0')
|
||||||
|
return 0;
|
||||||
|
if (dirent->d_name[1] == '.' &&
|
||||||
|
dirent->d_name[2] == '\0')
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* scan all cards and comments */
|
||||||
|
int uc_mgr_scan_master_configs(const char **_list[])
|
||||||
|
{
|
||||||
|
char filename[MAX_FILE];
|
||||||
|
char *env = getenv(ALSA_CONFIG_UCM_VAR);
|
||||||
|
const char **list;
|
||||||
|
snd_config_t *cfg, *c;
|
||||||
|
int i, cnt, err;
|
||||||
|
struct dirent **namelist;
|
||||||
|
|
||||||
|
snprintf(filename, sizeof(filename)-1,
|
||||||
|
"%s", env ? env : ALSA_USE_CASE_DIR);
|
||||||
|
filename[MAX_FILE-1] = '\0';
|
||||||
|
|
||||||
|
err = scandir(filename, &namelist, filename_filter, alphasort);
|
||||||
|
if (err < 0) {
|
||||||
|
err = -errno;
|
||||||
|
uc_error("error: could not scan directory %s: %s",
|
||||||
|
filename, strerror(-err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
cnt = err;
|
||||||
|
|
||||||
|
list = calloc(1, cnt * 2 * sizeof(char *));
|
||||||
|
if (list == NULL) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto __err;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
err = load_master_config(namelist[i]->d_name, &cfg);
|
||||||
|
if (err < 0)
|
||||||
|
goto __err;
|
||||||
|
err = snd_config_search(cfg, "Comment", &c);
|
||||||
|
if (err >= 0) {
|
||||||
|
err = parse_string(c, (char **)&list[i*2+1]);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_config_delete(cfg);
|
||||||
|
goto __err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snd_config_delete(cfg);
|
||||||
|
list[i * 2] = strdup(namelist[i]->d_name);
|
||||||
|
if (list[i * 2] == NULL) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto __err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = cnt * 2;
|
||||||
|
|
||||||
|
__err:
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
free(namelist[i]);
|
||||||
|
if (err < 0) {
|
||||||
|
free((void *)list[i * 2]);
|
||||||
|
free((void *)list[i * 2 + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(namelist);
|
||||||
|
|
||||||
|
if (err >= 0)
|
||||||
|
*_list = list;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,7 @@ void uc_mgr_stdout(const char *fmt, ...);
|
||||||
|
|
||||||
int uc_mgr_config_load(const char *file, snd_config_t **cfg);
|
int uc_mgr_config_load(const char *file, snd_config_t **cfg);
|
||||||
int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr);
|
int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr);
|
||||||
|
int uc_mgr_scan_master_configs(const char **_list[]);
|
||||||
|
|
||||||
void uc_mgr_free_sequence_element(struct sequence_element *seq);
|
void uc_mgr_free_sequence_element(struct sequence_element *seq);
|
||||||
void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr);
|
void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr);
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,13 @@ int uc_mgr_config_load(const char *file, snd_config_t **cfg)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
fp = fopen(file, "r");
|
fp = fopen(file, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
err = -errno;
|
||||||
|
goto __err;
|
||||||
|
}
|
||||||
err = snd_input_stdio_attach(&in, fp, 1);
|
err = snd_input_stdio_attach(&in, fp, 1);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
__err:
|
||||||
uc_error("could not open configuration file %s", file);
|
uc_error("could not open configuration file %s", file);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
test/ucm/anothercard/anothercard.conf
Normal file
1
test/ucm/anothercard/anothercard.conf
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Comment "Another Card"
|
||||||
0
test/ucm/testcard1/testcard1.conf
Normal file
0
test/ucm/testcard1/testcard1.conf
Normal file
Loading…
Add table
Add a link
Reference in a new issue