summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@perex.cz>2010-10-13 11:48:52 +0200
committerJaroslav Kysela <perex@perex.cz>2010-10-13 11:48:52 +0200
commite8208666379e10bbd56365b0a6d17f3daab730e3 (patch)
tree676cbb81e317b0d4850552942fcbf4b246ae47c2
parent3a3439450864c8706e4e20d392fd43ecf935153f (diff)
downloadalsa-lib-e8208666379e10bbd56365b0a6d17f3daab730e3.tar.gz
ucm: implemented card list feature
- also added some test files to test/ucm tree Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--src/ucm/main.c12
-rw-r--r--src/ucm/parser.c142
-rw-r--r--src/ucm/ucm_local.h1
-rw-r--r--src/ucm/utils.c5
-rw-r--r--test/ucm/anothercard/anothercard.conf1
-rw-r--r--test/ucm/testcard1/testcard1.conf0
6 files changed, 134 insertions, 27 deletions
diff --git a/src/ucm/main.c b/src/ucm/main.c
index 88389764..e233f414 100644
--- a/src/ucm/main.c
+++ b/src/ucm/main.c
@@ -562,16 +562,6 @@ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr)
}
/**
- * \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
* \param list Returned list
* \param verbname For verb (NULL = current)
@@ -780,7 +770,7 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr,
int err;
if (uc_mgr == NULL || identifier == NULL)
- return get_card_list(list);
+ return uc_mgr_scan_master_configs(list);
pthread_mutex_lock(&uc_mgr->mutex);
if (strcmp(identifier, "_verbs") == 0)
err = get_verb_list(uc_mgr, list);
diff --git a/src/ucm/parser.c b/src/ucm/parser.c
index e540c20d..4e2a9d74 100644
--- a/src/ucm/parser.c
+++ b/src/ucm/parser.c
@@ -31,6 +31,7 @@
*/
#include "ucm_local.h"
+#include <dirent.h>
static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
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)
{
@@ -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
* #By Joe Blogs <joe@bloggs.org>
*
+ * Comment "Nice Abstracted Soundcard"
+ *
* # The file is divided into Use case sections. One section per use case verb.
*
* 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_t *n;
- int ret;
+ const char *id;
+ int err;
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
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 */
snd_config_for_each(i, next, cfg) {
- const char *id;
+
n = snd_config_iterator_entry(i);
if (snd_config_get_id(n, &id) < 0)
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 */
if (strcmp(id, "SectionUseCase") == 0) {
- ret = parse_compound(uc_mgr, n,
+ err = parse_compound(uc_mgr, n,
parse_master_section,
NULL, NULL);
- if (ret < 0)
- return ret;
+ if (err < 0)
+ return err;
continue;
}
/* find default control values section and parse it */
if (strcmp(id, "SectionDefaults") == 0) {
- ret = parse_controls(uc_mgr, n);
- if (ret < 0)
- return ret;
+ err = parse_controls(uc_mgr, n);
+ if (err < 0)
+ return err;
continue;
}
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;
}
-/* load master use case file for sound card */
-int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
+/** The name of the environment variable containing the UCM directory */
+#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];
- snd_config_t *cfg;
+ char *env = getenv(ALSA_CONFIG_UCM_VAR);
int err;
snprintf(filename, sizeof(filename)-1,
- "%s/%s/%s.conf", ALSA_USE_CASE_DIR,
- uc_mgr->card_name, uc_mgr->card_name);
+ "%s/%s/%s.conf", env ? env : ALSA_USE_CASE_DIR,
+ card_name, card_name);
filename[MAX_FILE-1] = '\0';
- err = uc_mgr_config_load(filename, &cfg);
+ err = uc_mgr_config_load(filename, cfg);
if (err < 0) {
uc_error("error: could not parse configuration for card %s",
- uc_mgr->card_name);
+ card_name);
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);
snd_config_delete(cfg);
if (err < 0)
@@ -944,3 +971,86 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
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;
+}
diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h
index 06a1e5f6..7dae5b6a 100644
--- a/src/ucm/ucm_local.h
+++ b/src/ucm/ucm_local.h
@@ -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_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_verb(snd_use_case_mgr_t *uc_mgr);
diff --git a/src/ucm/utils.c b/src/ucm/utils.c
index 83926da3..0f8207e8 100644
--- a/src/ucm/utils.c
+++ b/src/ucm/utils.c
@@ -57,8 +57,13 @@ int uc_mgr_config_load(const char *file, snd_config_t **cfg)
int err;
fp = fopen(file, "r");
+ if (fp == NULL) {
+ err = -errno;
+ goto __err;
+ }
err = snd_input_stdio_attach(&in, fp, 1);
if (err < 0) {
+ __err:
uc_error("could not open configuration file %s", file);
return err;
}
diff --git a/test/ucm/anothercard/anothercard.conf b/test/ucm/anothercard/anothercard.conf
new file mode 100644
index 00000000..3d9ed7de
--- /dev/null
+++ b/test/ucm/anothercard/anothercard.conf
@@ -0,0 +1 @@
+Comment "Another Card"
diff --git a/test/ucm/testcard1/testcard1.conf b/test/ucm/testcard1/testcard1.conf
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/ucm/testcard1/testcard1.conf