summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ucm/parser.c107
-rw-r--r--src/ucm/ucm_local.h11
-rw-r--r--src/ucm/utils.c1
3 files changed, 118 insertions, 1 deletions
diff --git a/src/ucm/parser.c b/src/ucm/parser.c
index 5c99ab49..c98373a9 100644
--- a/src/ucm/parser.c
+++ b/src/ucm/parser.c
@@ -254,6 +254,82 @@ static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
return 0;
}
+/* Find a component device by its name, and remove it from machine device
+ * list.
+ *
+ * Component devices are defined by machine components (usually off-soc
+ * codes or DSP embeded in SoC). Since alsaconf imports their configuration
+ * files automatically, we don't know which devices are component devices
+ * until they are referenced by a machine device sequence. So here when we
+ * find a referenced device, we move it from the machine device list to the
+ * component device list. Component devices will not be exposed to applications
+ * by the original API to list devices for backward compatibility. So sound
+ * servers can only see the machine devices.
+ */
+struct use_case_device *find_component_dev(snd_use_case_mgr_t *uc_mgr,
+ const char *name)
+{
+ struct list_head *pos, *posdev, *_posdev;
+ struct use_case_verb *verb;
+ struct use_case_device *dev;
+
+ list_for_each(pos, &uc_mgr->verb_list) {
+ verb = list_entry(pos, struct use_case_verb, list);
+
+ /* search in the component device list */
+ list_for_each(posdev, &verb->cmpt_device_list) {
+ dev = list_entry(posdev, struct use_case_device, list);
+ if (!strcmp(dev->name, name))
+ return dev;
+ }
+
+ /* search the machine device list */
+ list_for_each_safe(posdev, _posdev, &verb->device_list) {
+ dev = list_entry(posdev, struct use_case_device, list);
+ if (!strcmp(dev->name, name)) {
+ /* find the component device, move it from the
+ * machine device list to the component device
+ * list.
+ */
+ list_del(&dev->list);
+ list_add_tail(&dev->list,
+ &verb->cmpt_device_list);
+ return dev;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* parse sequence of a component device
+ *
+ * This function will find the component device and mark if its enable or
+ * disable sequence is needed by its parenet device.
+ */
+static int parse_component_seq(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *n, int enable,
+ struct component_sequence *cmpt_seq)
+{
+ const char *val;
+ int err;
+
+ err = snd_config_get_string(n, &val);
+ if (err < 0)
+ return err;
+
+ cmpt_seq->device = find_component_dev(uc_mgr, val);
+ if (!cmpt_seq->device) {
+ uc_error("error: Cannot find component device %s", val);
+ return -EINVAL;
+ }
+
+ /* Parent needs its enable or disable sequence */
+ cmpt_seq->enable = enable;
+
+ return 0;
+}
+
/*
* Parse sequences.
*
@@ -263,12 +339,16 @@ static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
* cset "element_id_syntax value_syntax"
* usleep time
* exec "any unix command with arguments"
+ * enadev "component device name"
+ * disdev "component device name"
*
* e.g.
* cset "name='Master Playback Switch' 0,0"
* cset "iface=PCM,name='Disable HDMI',index=1 0"
+ * enadev "rt286:Headphones"
+ * disdev "rt286:Speaker"
*/
-static int parse_sequence(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
+static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
struct list_head *base,
snd_config_t *cfg)
{
@@ -325,6 +405,30 @@ static int parse_sequence(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
continue;
}
+ if (strcmp(cmd, "enadev") == 0) {
+ /* need to enable a component device */
+ curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ;
+ err = parse_component_seq(uc_mgr, n, 1,
+ &curr->data.cmpt_seq);
+ if (err < 0) {
+ uc_error("error: enadev requires a valid device!");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(cmd, "disdev") == 0) {
+ /* need to disable a component device */
+ curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ;
+ err = parse_component_seq(uc_mgr, n, 0,
+ &curr->data.cmpt_seq);
+ if (err < 0) {
+ uc_error("error: disdev requires a valid device!");
+ return err;
+ }
+ continue;
+ }
+
if (strcmp(cmd, "cset-bin-file") == 0) {
curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE;
err = parse_string(n, &curr->data.cset);
@@ -957,6 +1061,7 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
INIT_LIST_HEAD(&verb->disable_list);
INIT_LIST_HEAD(&verb->transition_list);
INIT_LIST_HEAD(&verb->device_list);
+ INIT_LIST_HEAD(&verb->cmpt_device_list);
INIT_LIST_HEAD(&verb->modifier_list);
INIT_LIST_HEAD(&verb->value_list);
list_add_tail(&verb->list, &uc_mgr->verb_list);
diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h
index b89de2a1..3bfdd67c 100644
--- a/src/ucm/ucm_local.h
+++ b/src/ucm/ucm_local.h
@@ -49,6 +49,7 @@
#define SEQUENCE_ELEMENT_TYPE_EXEC 4
#define SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE 5
#define SEQUENCE_ELEMENT_TYPE_CSET_TLV 6
+#define SEQUENCE_ELEMENT_TYPE_CMPT_SEQ 7
struct ucm_value {
struct list_head list;
@@ -56,6 +57,12 @@ struct ucm_value {
char *data;
};
+/* sequence of a component device */
+struct component_sequence {
+ struct use_case_device *device; /* component device */
+ int enable; /* flag to choose enable or disable list of the device */
+};
+
struct sequence_element {
struct list_head list;
unsigned int type;
@@ -64,6 +71,7 @@ struct sequence_element {
char *cdev;
char *cset;
char *exec;
+ struct component_sequence cmpt_seq; /* component sequence */
} data;
};
@@ -167,6 +175,9 @@ struct use_case_verb {
/* hardware devices that can be used with this use case */
struct list_head device_list;
+ /* component device list */
+ struct list_head cmpt_device_list;
+
/* modifiers that can be used with this use case */
struct list_head modifier_list;
diff --git a/src/ucm/utils.c b/src/ucm/utils.c
index 45307b02..0fba85ae 100644
--- a/src/ucm/utils.c
+++ b/src/ucm/utils.c
@@ -210,6 +210,7 @@ void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
uc_mgr_free_transition(&verb->transition_list);
uc_mgr_free_value(&verb->value_list);
uc_mgr_free_device(&verb->device_list);
+ uc_mgr_free_device(&verb->cmpt_device_list);
uc_mgr_free_modifier(&verb->modifier_list);
list_del(&verb->list);
free(verb);