OSDN Git Service

ucm: add support for multiple control devices, more aggresive caching
authorJaroslav Kysela <perex@perex.cz>
Mon, 4 Nov 2019 13:42:45 +0000 (14:42 +0100)
committerJaroslav Kysela <perex@perex.cz>
Sun, 10 Nov 2019 12:11:35 +0000 (13:11 +0100)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
src/ucm/main.c
src/ucm/parser.c
src/ucm/ucm_local.h
src/ucm/utils.c

index 99b1cd0..0c4e2dc 100644 (file)
@@ -138,37 +138,6 @@ int snd_use_case_free_list(const char *list[], int items)
        return 0;
 }
 
-static int open_ctl(snd_use_case_mgr_t *uc_mgr,
-                   snd_ctl_t **ctl,
-                   const char *ctl_dev)
-{
-       int err;
-
-       /* FIXME: add a list of ctl devices to uc_mgr structure and
-           cache accesses for multiple opened ctl devices */
-       if (uc_mgr->ctl_dev != NULL && strcmp(ctl_dev, uc_mgr->ctl_dev) == 0) {
-               *ctl = uc_mgr->ctl;
-               return 0;
-       }
-       if (uc_mgr->ctl_dev) {
-               free(uc_mgr->ctl_dev);
-               uc_mgr->ctl_dev = NULL;
-               snd_ctl_close(uc_mgr->ctl);
-               uc_mgr->ctl = NULL;
-       
-       }
-       err = snd_ctl_open(ctl, ctl_dev, 0);
-       if (err < 0)
-               return err;
-       uc_mgr->ctl_dev = strdup(ctl_dev);
-       if (uc_mgr->ctl_dev == NULL) {
-               snd_ctl_close(*ctl);
-               return -ENOMEM;
-       }
-       uc_mgr->ctl = *ctl;
-       return 0;
-}
-
 static int read_tlv_file(unsigned int **res,
                         const char *filepath)
 {
@@ -427,7 +396,7 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
                                        cdev = capture_ctl;
                        }
                        if (ctl == NULL) {
-                               err = open_ctl(uc_mgr, &ctl, cdev);
+                               err = uc_mgr_open_ctl(uc_mgr, &ctl, cdev);
                                if (err < 0) {
                                        uc_error("unable to open ctl device '%s'", cdev);
                                        goto __fail;
@@ -932,6 +901,7 @@ int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr,
        INIT_LIST_HEAD(&mgr->value_list);
        INIT_LIST_HEAD(&mgr->active_modifiers);
        INIT_LIST_HEAD(&mgr->active_devices);
+       INIT_LIST_HEAD(&mgr->ctl_list);
        pthread_mutex_init(&mgr->mutex, NULL);
 
        mgr->card_name = strdup(card_name);
index 34dfe29..97b2501 100644 (file)
@@ -1412,22 +1412,27 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
 }
 
 /* get the card info */
-static int get_card_info(const char *ctl_name, snd_ctl_card_info_t *info)
+static int get_card_info(snd_use_case_mgr_t *mgr,
+                        const char *ctl_name,
+                        snd_ctl_t **_handle,
+                        snd_ctl_card_info_t *info)
 {
        snd_ctl_t *handle;
        int err;
 
-       err = snd_ctl_open(&handle, ctl_name, 0);
-       if (err < 0) {
-               uc_error("control open (%s): %s", ctl_name, snd_strerror(err));
+       *_handle = NULL;
+
+       err = uc_mgr_open_ctl(mgr, &handle, ctl_name);
+       if (err < 0)
                return err;
-       }
 
        err = snd_ctl_card_info(handle, info);
-       if (err < 0)
+       if (err < 0) {
                uc_error("control hardware info (%s): %s", ctl_name, snd_strerror(err));
+       } else {
+               *_handle = handle;
+       }
 
-       snd_ctl_close(handle);
        return err;
 }
 
@@ -1436,6 +1441,7 @@ static int get_card_long_name(snd_use_case_mgr_t *mgr)
 {
        const char *card_name = mgr->card_name;
        int card, err;
+       snd_ctl_t *ctl;
        snd_ctl_card_info_t *info;
        const char *_name, *_long_name;
 
@@ -1451,7 +1457,7 @@ static int get_card_long_name(snd_use_case_mgr_t *mgr)
                char name[32];
 
                sprintf(name, "hw:%d", card);
-               err = get_card_info(name, info);
+               err = get_card_info(mgr, name, &ctl, info);
 
                if (err == 0) {
                        _name = snd_ctl_card_info_get_name(info);
@@ -1475,13 +1481,14 @@ static int get_card_long_name(snd_use_case_mgr_t *mgr)
 /* set the driver name and long name by the card ctl name */
 static int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name)
 {
+       snd_ctl_t *ctl;
        snd_ctl_card_info_t *info;
        const char *_name, *_long_name;
        int err;
 
        snd_ctl_card_info_alloca(&info);
 
-       err = get_card_info(ctl_name, info);
+       err = get_card_info(mgr, ctl_name, &ctl, info);
        if (err)
                return err;
 
@@ -1490,6 +1497,7 @@ static int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name)
 
        snd_strlcpy(mgr->card_long_name, _long_name, sizeof(mgr->card_long_name));
        snd_strlcpy(mgr->conf_file_name, _name, sizeof(mgr->conf_file_name));
+
        return 0;
 }
 
@@ -1572,6 +1580,7 @@ __parse:
        return err;
 
 __error:
+       uc_mgr_free_ctl_list(uc_mgr);
        uc_mgr->conf_file_name[0] = '\0';
        return err;
 }
index ee15d38..3da747e 100644 (file)
@@ -105,6 +105,18 @@ struct dev_list {
        struct list_head list;
 };
 
+struct ctl_dev {
+       struct list_head list;
+       char *device;
+};
+
+struct ctl_list {
+       struct list_head list;
+       struct list_head dev_list;
+       char *ctl_id;
+       snd_ctl_t *ctl;
+};
+
 /*
  * Describes a Use Case Modifier and it's enable and disable sequences.
  * A use case verb can have N modifiers.
@@ -214,9 +226,8 @@ struct snd_use_case_mgr {
        /* locking */
        pthread_mutex_t mutex;
 
-       /* change to list of ctl handles */
-       snd_ctl_t *ctl;
-       char *ctl_dev;
+       /* list of opened control devices */
+       struct list_head ctl_list;
 
        /* Components don't define cdev, the card device. When executing
         * a sequence of a component device, ucm manager enters component
@@ -247,6 +258,12 @@ void uc_mgr_free_transition_element(struct transition_sequence *seq);
 void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr);
 void uc_mgr_free(snd_use_case_mgr_t *uc_mgr);
 
+int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr,
+                    snd_ctl_t **ctl,
+                    const char *device);
+
+void uc_mgr_free_ctl_list(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"
 
index 84ad5ac..e81f875 100644 (file)
@@ -49,6 +49,177 @@ void uc_mgr_stdout(const char *fmt,...)
        va_end(va);
 }
 
+static void uc_mgr_free_ctl(struct ctl_list *ctl_list)
+{
+       struct list_head *pos, *npos;
+       struct ctl_dev *ctl_dev;
+
+       list_for_each_safe(pos, npos, &ctl_list->dev_list) {
+               ctl_dev = list_entry(pos, struct ctl_dev, list);
+               free(ctl_dev->device);
+               free(ctl_dev);
+       }
+       free(ctl_list->ctl_id);
+       free(ctl_list);
+}
+
+void uc_mgr_free_ctl_list(snd_use_case_mgr_t *uc_mgr)
+{
+       struct list_head *pos, *npos;
+       struct ctl_list *ctl_list;
+
+       list_for_each_safe(pos, npos, &uc_mgr->ctl_list) {
+               ctl_list = list_entry(pos, struct ctl_list, list);
+               snd_ctl_close(ctl_list->ctl);
+               list_del(&ctl_list->list);
+               uc_mgr_free_ctl(ctl_list);
+       }
+}
+
+static int uc_mgr_ctl_add_dev(struct ctl_list *ctl_list, const char *device)
+{
+       struct list_head *pos;
+       struct ctl_dev *ctl_dev;
+
+       /* skip duplicates */
+       list_for_each(pos, &ctl_list->dev_list) {
+               ctl_dev = list_entry(pos, struct ctl_dev, list);
+               if (strcmp(ctl_dev->device, device) == 0)
+                       return 0;
+       }
+
+       /* allocate new device name */
+       ctl_dev = malloc(sizeof(*ctl_dev));
+       if (ctl_dev == NULL)
+               return -ENOMEM;
+       ctl_dev->device = strdup(device);
+       if (ctl_dev->device == NULL) {
+               free(ctl_dev);
+               return -ENOMEM;
+       }
+       list_add_tail(&ctl_dev->list, &ctl_list->dev_list);
+       return 0;
+}
+
+static int uc_mgr_ctl_add(snd_use_case_mgr_t *uc_mgr,
+                         struct ctl_list *ctl_list,
+                         snd_ctl_t *ctl, int card, snd_ctl_card_info_t *info,
+                         const char *device)
+{
+       struct ctl_list *cl = NULL;
+       const char *id = snd_ctl_card_info_get_id(info);
+       char dev[MAX_CARD_LONG_NAME];
+       int err, hit = 0;
+
+       if (id == NULL || id[0] == '\0')
+               return -ENOENT;
+       if (!ctl_list) {
+               cl = malloc(sizeof(*cl));
+               if (cl == NULL)
+                       return -ENOMEM;
+               INIT_LIST_HEAD(&cl->dev_list);
+               cl->ctl = ctl;
+               cl->ctl_id = strdup(id);
+               if (cl->ctl_id == NULL) {
+                       free(cl);
+                       return -ENOMEM;
+               }
+               ctl_list = cl;
+       }
+       if (card >= 0) {
+               snprintf(dev, sizeof(dev), "hw:%d", card);
+               hit |= !!(device && (strcmp(dev, device) == 0));
+               err = uc_mgr_ctl_add_dev(ctl_list, dev);
+               if (err < 0)
+                       goto __nomem;
+       }
+       snprintf(dev, sizeof(dev), "hw:%s", id);
+       hit |= !!(device && (strcmp(dev, device) == 0));
+       err = uc_mgr_ctl_add_dev(ctl_list, dev);
+       if (err < 0)
+               goto __nomem;
+       /* the UCM name not based on the card name / id */
+       if (!hit && device) {
+               err = uc_mgr_ctl_add_dev(ctl_list, device);
+               if (err < 0)
+                       goto __nomem;
+       }
+
+       list_add_tail(&ctl_list->list, &uc_mgr->ctl_list);
+       return 0;
+
+__nomem:
+       if (ctl_list == cl)
+               uc_mgr_free_ctl(cl);
+       return -ENOMEM;
+}
+
+int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr,
+                   snd_ctl_t **ctl,
+                   const char *device)
+{
+       struct list_head *pos1, *pos2;
+       struct ctl_list *ctl_list;
+       struct ctl_dev *ctl_dev;
+       snd_ctl_card_info_t *info;
+       const char *id;
+       int err, card;
+
+       snd_ctl_card_info_alloca(&info);
+
+       /* cache lookup */
+       list_for_each(pos1, &uc_mgr->ctl_list) {
+               ctl_list = list_entry(pos1, struct ctl_list, list);
+               list_for_each(pos2, &ctl_list->dev_list) {
+                       ctl_dev = list_entry(pos2, struct ctl_dev, list);
+                       if (strcmp(ctl_dev->device, device) == 0) {
+                               *ctl = ctl_list->ctl;
+                               return 0;
+                       }
+               }
+       }
+
+       err = snd_ctl_open(ctl, device, 0);
+       if (err < 0)
+               return err;
+
+       id = NULL;
+       err = snd_ctl_card_info(*ctl, info);
+       if (err == 0)
+               id = snd_ctl_card_info_get_id(info);
+       if (err < 0 || id == NULL || id[0] == '\0') {
+               uc_error("control hardware info (%s): %s", device, snd_strerror(err));
+               snd_ctl_close(*ctl);
+               *ctl = NULL;
+               return err;
+       }
+
+       /* insert to cache, if just name differs */
+       list_for_each(pos1, &uc_mgr->ctl_list) {
+               ctl_list = list_entry(pos1, struct ctl_list, list);
+               if (strcmp(id, ctl_list->ctl_id) == 0) {
+                       card = snd_card_get_index(id);
+                       err = uc_mgr_ctl_add(uc_mgr, ctl_list, *ctl, card, info, device);
+                       if (err < 0)
+                               goto __nomem;
+                       snd_ctl_close(*ctl);
+                       *ctl = ctl_list->ctl;
+                       return 0;
+               }
+       }
+
+       err = uc_mgr_ctl_add(uc_mgr, NULL, *ctl, -1, info, device);
+       if (err < 0)
+               goto __nomem;
+
+       return 0;
+
+__nomem:
+       snd_ctl_close(*ctl);
+       *ctl = NULL;
+       return -ENOMEM;
+}
+
 int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
 {
        FILE *fp;
@@ -241,17 +412,12 @@ void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
        uc_mgr->active_verb = NULL;
        INIT_LIST_HEAD(&uc_mgr->active_devices);
        INIT_LIST_HEAD(&uc_mgr->active_modifiers);
-       if (uc_mgr->ctl != NULL) {
-               snd_ctl_close(uc_mgr->ctl);
-               uc_mgr->ctl = NULL;
-       }
-       free(uc_mgr->ctl_dev);
-       uc_mgr->ctl_dev = NULL;
 }
 
 void uc_mgr_free(snd_use_case_mgr_t *uc_mgr)
 {
        uc_mgr_free_verb(uc_mgr);
+       uc_mgr_free_ctl_list(uc_mgr);
        free(uc_mgr->card_name);
        free(uc_mgr);
 }