tizen 2.3.1 release
[external/alsa-lib.git] / src / ucm / main.c
old mode 100644 (file)
new mode 100755 (executable)
index 42fd373..a4fa765
@@ -9,9 +9,9 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  *  Support for the verb/device/modifier core logic and API,
  *  command line tool and file parser was kindly sponsored by
@@ -62,7 +62,7 @@ static int list_count(struct list_head *list)
 {
         struct list_head *pos;
         int count = 0;
-        
+
         list_for_each(pos, list) {
                 count += 1;
         }
@@ -73,10 +73,12 @@ static int alloc_str_list(struct list_head *list, int mult, char **result[])
 {
         char **res;
         int cnt;
-        
+
         cnt = list_count(list) * mult;
-        if (cnt == 0)
+        if (cnt == 0) {
+               *result = NULL;
                 return cnt;
+       }
         res = calloc(mult, cnt * sizeof(char *));
         if (res == NULL)
                 return -ENOMEM;
@@ -142,7 +144,7 @@ static int open_ctl(snd_use_case_mgr_t *uc_mgr,
                free(uc_mgr->ctl_dev);
                uc_mgr->ctl_dev = NULL;
                snd_ctl_close(uc_mgr->ctl);
-       
+
        }
        err = snd_ctl_open(ctl, ctl_dev, 0);
        if (err < 0)
@@ -171,7 +173,8 @@ static int execute_cset(snd_ctl_t *ctl, char *cset)
        pos = strrchr(cset, ' ');
        if (pos == NULL) {
                uc_error("undefined value for cset >%s<", cset);
-               return -EINVAL;
+               err = -EINVAL;
+               goto __fail;
        }
        *pos = '\0';
        err = snd_ctl_ascii_elem_id_parse(id, cset);
@@ -193,7 +196,16 @@ static int execute_cset(snd_ctl_t *ctl, char *cset)
                goto __fail;
        err = 0;
       __fail:
-       *pos = ' ';
+       if (pos != NULL)
+               *pos = ' ';
+
+       if (id != NULL)
+               free(id);
+       if (value != NULL)
+               free(value);
+       if (info != NULL)
+               free(info);
+
        return err;
 }
 
@@ -269,9 +281,11 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
                        usleep(s->data.sleep);
                        break;
                case SEQUENCE_ELEMENT_TYPE_EXEC:
-                       err = system(s->data.exec);
+                       /* Remove because security issues */
+                       /* err = system(s->data.exec);
                        if (err < 0)
-                               goto __fail;
+                               goto __fail; */
+                       uc_error("unsupported known sequence command %i", s->type);
                        break;
                default:
                        uc_error("unknown sequence command %i", s->type);
@@ -296,7 +310,7 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
 static int import_master_config(snd_use_case_mgr_t *uc_mgr)
 {
        int err;
-       
+
        err = uc_mgr_import_master_config(uc_mgr);
        if (err < 0)
                return err;
@@ -452,6 +466,51 @@ static inline struct use_case_verb *find_verb(snd_use_case_mgr_t *uc_mgr,
                    verb_name);
 }
 
+static int is_devlist_supported(snd_use_case_mgr_t *uc_mgr,
+       struct dev_list *dev_list)
+{
+       struct dev_list_node *device;
+       struct use_case_device *adev;
+       struct list_head *pos, *pos1;
+       int found_ret;
+
+       switch (dev_list->type) {
+       case DEVLIST_NONE:
+       default:
+               return 1;
+       case DEVLIST_SUPPORTED:
+               found_ret = 1;
+               break;
+       case DEVLIST_CONFLICTING:
+               found_ret = 0;
+               break;
+       }
+
+       list_for_each(pos, &dev_list->list) {
+               device = list_entry(pos, struct dev_list_node, list);
+
+               list_for_each(pos1, &uc_mgr->active_devices) {
+                       adev = list_entry(pos1, struct use_case_device,
+                                           active_list);
+                       if (!strcmp(device->name, adev->name))
+                               return found_ret;
+               }
+       }
+       return 1 - found_ret;
+}
+
+static inline int is_modifier_supported(snd_use_case_mgr_t *uc_mgr,
+       struct use_case_modifier *modifier)
+{
+       return is_devlist_supported(uc_mgr, &modifier->dev_list);
+}
+
+static inline int is_device_supported(snd_use_case_mgr_t *uc_mgr,
+       struct use_case_device *device)
+{
+       return is_devlist_supported(uc_mgr, &device->dev_list);
+}
+
 /**
  * \brief Find device
  * \param verb Use case verb
@@ -459,50 +518,25 @@ static inline struct use_case_verb *find_verb(snd_use_case_mgr_t *uc_mgr,
  * \return structure on success, otherwise a NULL (not found)
  */
 static inline struct use_case_device *
-        find_device(struct use_case_verb *verb,
-                      const char *device_name)
+        find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
+                   const char *device_name, int check_supported)
 {
-       return find(&verb->device_list,
-                   struct use_case_device, list, name,
-                   device_name);
-}
+       struct use_case_device *device;
+       struct list_head *pos;
 
-static int is_modifier_supported(snd_use_case_mgr_t *uc_mgr, 
-       struct use_case_modifier *modifier)
-{
-       struct dev_list *device;
-       struct use_case_device *adev;
-       struct list_head *pos, *pos1;
-       char *cpos;
-       int dlen, len;
-
-       list_for_each(pos, &modifier->dev_list) {
-               device = list_entry(pos, struct dev_list, list);
-               cpos = strchr(device->name, '.');
-               if (cpos) {
-                       if (find(&uc_mgr->active_devices,
-                                       struct use_case_device, active_list,
-                                       name, device->name))
-                               return 1;
-               } else {
-                       dlen = strlen(device->name);
-                       list_for_each(pos1, &uc_mgr->active_devices) {
-                               adev = list_entry(pos1, struct use_case_device,
-                                                 active_list);
-                               cpos = strchr(adev->name, '.');
-                               if (cpos)
-                                       len = cpos - adev->name;
-                               else
-                                       len = strlen(adev->name);
-                               if (len != dlen)
-                                       continue;
-                               if (memcmp(adev->name, device->name, len))
-                                       continue;
-                               return 1;
-                       }
-               }
+       list_for_each(pos, &verb->device_list) {
+               device = list_entry(pos, struct use_case_device, list);
+
+               if (strcmp(device_name, device->name))
+                       continue;
+
+               if (check_supported &&
+                   !is_device_supported(uc_mgr, device))
+                       continue;
+
+               return device;
        }
-       return 0;
+       return NULL;
 }
 
 /**
@@ -512,28 +546,23 @@ static int is_modifier_supported(snd_use_case_mgr_t *uc_mgr,
  * \return structure on success, otherwise a NULL (not found)
  */
 static struct use_case_modifier *
-        find_modifier(snd_use_case_mgr_t *uc_mgr, const char *modifier_name)
+        find_modifier(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
+                     const char *modifier_name, int check_supported)
 {
        struct use_case_modifier *modifier;
-       struct use_case_verb *verb = uc_mgr->active_verb;
        struct list_head *pos;
-       char name[64], *cpos;
 
        list_for_each(pos, &verb->modifier_list) {
                modifier = list_entry(pos, struct use_case_modifier, list);
 
-               strncpy(name, modifier->name, sizeof(name));
-               name[sizeof(name)-1] = '\0';
-               cpos = strchr(name, '.');
-               if (!cpos)
+               if (strcmp(modifier->name, modifier_name))
                        continue;
-               *cpos= '\0';
 
-               if (strcmp(name, modifier_name))
+               if (check_supported &&
+                   !is_modifier_supported(uc_mgr, modifier))
                        continue;
 
-               if (is_modifier_supported(uc_mgr, modifier))
-                       return modifier;
+               return modifier;
        }
        return NULL;
 }
@@ -747,7 +776,7 @@ static int dismantle_use_case(snd_use_case_mgr_t *uc_mgr)
 
        err = execute_sequence(uc_mgr, &uc_mgr->default_list,
                               &uc_mgr->value_list, NULL, NULL);
-       
+
        return err;
 }
 
@@ -793,7 +822,7 @@ static int get_device_list(snd_use_case_mgr_t *uc_mgr, const char **list[],
                            char *verbname)
 {
         struct use_case_verb *verb;
-        
+
         if (verbname) {
                 verb = find_verb(uc_mgr, verbname);
         } else {
@@ -816,7 +845,7 @@ static int get_modifier_list(snd_use_case_mgr_t *uc_mgr, const char **list[],
                              char *verbname)
 {
         struct use_case_verb *verb;
-        
+
         if (verbname) {
                 verb = find_verb(uc_mgr, verbname);
         } else {
@@ -829,6 +858,82 @@ static int get_modifier_list(snd_use_case_mgr_t *uc_mgr, const char **list[],
                          name, comment);
 }
 
+/**
+ * \brief Get list of supported/conflicting devices
+ * \param list Returned list
+ * \param name Name of modifier or verb to query
+ * \param type Type of device list entries to return
+ * \return Number of list entries if success, otherwise a negative error code
+ */
+static int get_supcon_device_list(snd_use_case_mgr_t *uc_mgr,
+                                 const char **list[], char *name,
+                                 enum dev_list_type type)
+{
+       char *str;
+       struct use_case_verb *verb;
+       struct use_case_modifier *modifier;
+       struct use_case_device *device;
+
+       if (!name)
+               return -ENOENT;
+
+       str = strchr(name, '/');
+       if (str) {
+               *str = '\0';
+               verb = find_verb(uc_mgr, str + 1);
+       }
+       else {
+               verb = uc_mgr->active_verb;
+       }
+       if (!verb)
+               return -ENOENT;
+
+       modifier = find_modifier(uc_mgr, verb, name, 0);
+       if (modifier) {
+               if (modifier->dev_list.type != type)
+                       return 0;
+               return get_list(&modifier->dev_list.list, list,
+                               struct dev_list_node, list,
+                               name);
+       }
+
+       device = find_device(uc_mgr, verb, name, 0);
+       if (device) {
+               if (device->dev_list.type != type)
+                       return 0;
+               return get_list(&device->dev_list.list, list,
+                               struct dev_list_node, list,
+                               name);
+       }
+
+       return -ENOENT;
+
+}
+
+/**
+ * \brief Get list of supported devices
+ * \param list Returned list
+ * \param name Name of verb or modifier to query
+ * \return Number of list entries if success, otherwise a negative error code
+ */
+static int get_supported_device_list(snd_use_case_mgr_t *uc_mgr,
+                                    const char **list[], char *name)
+{
+       return get_supcon_device_list(uc_mgr, list, name, DEVLIST_SUPPORTED);
+}
+
+/**
+ * \brief Get list of conflicting devices
+ * \param list Returned list
+ * \param name Name of verb or modifier to query
+ * \return Number of list entries if success, otherwise a negative error code
+ */
+static int get_conflicting_device_list(snd_use_case_mgr_t *uc_mgr,
+                                      const char **list[], char *name)
+{
+       return get_supcon_device_list(uc_mgr, list, name, DEVLIST_CONFLICTING);
+}
+
 struct myvalue {
         struct list_head list;
         char *value;
@@ -842,7 +947,7 @@ static int add_values(struct list_head *list,
         struct myvalue *val;
         struct list_head *pos, *pos1;
         int match;
-        
+
         list_for_each(pos, source) {
                 v = list_entry(pos, struct ucm_value, list);
                 if (check_identifier(identifier, v->name)) {
@@ -858,6 +963,7 @@ static int add_values(struct list_head *list,
                                 val = malloc(sizeof(struct myvalue));
                                 if (val == NULL)
                                         return -ENOMEM;
+                               val->value = v->data;
                                 list_add_tail(&val->list, list);
                         }
                 }
@@ -883,7 +989,7 @@ static int get_value_list(snd_use_case_mgr_t *uc_mgr,
         struct use_case_modifier *mod;
         char **res;
         int err;
-        
+
         if (verbname) {
                 verb = find_verb(uc_mgr, verbname);
         } else {
@@ -911,8 +1017,8 @@ static int get_value_list(snd_use_case_mgr_t *uc_mgr,
                         goto __fail;
         }
         err = alloc_str_list(&mylist, 1, &res);
-        *list = (const char **)res;
         if (err >= 0) {
+               *list = (const char **)res;
                 list_for_each(pos, &mylist) {
                         val = list_entry(pos, struct myvalue, list);
                         *res = strdup(val->value);
@@ -1003,6 +1109,12 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr,
                        err = get_device_list(uc_mgr, list, str);
                 else if (check_identifier(identifier, "_modifiers"))
                         err = get_modifier_list(uc_mgr, list, str);
+                else if (check_identifier(identifier, "_supporteddevs"))
+                        err = get_supported_device_list(uc_mgr, list, str);
+                else if (check_identifier(identifier, "_conflictingdevs"))
+                        err = get_conflicting_device_list(uc_mgr, list, str);
+               else if (identifier[0] == '_')
+                       err = -ENOENT;
                 else
                         err = get_value_list(uc_mgr, identifier, list, str);
                if (str)
@@ -1018,7 +1130,7 @@ static int get_value1(const char **value, struct list_head *value_list,
 {
         struct ucm_value *val;
         struct list_head *pos;
-        
+
        if (!value_list)
                return -ENOENT;
 
@@ -1059,49 +1171,86 @@ static int get_value3(const char **value,
  * \param uc_mgr Use case manager
  * \param identifier Value identifier (string)
  * \param value Returned value string
- * \param modifier modifier name (string)
+ * \param item Modifier or Device name (string)
  * \return Zero on success (value is filled), otherwise a negative error code
  */
 static int get_value(snd_use_case_mgr_t *uc_mgr,
-                     const char *identifier,
-                     const char **value,
-                     const char *modifier)
+                       const char *identifier,
+                       const char **value,
+                       const char *mod_dev_name,
+                       const char *verb_name,
+                       int exact)
 {
-        struct use_case_modifier *mod;
+       struct use_case_verb *verb;
+       struct use_case_modifier *mod;
+       struct use_case_device *dev;
        int err;
 
-       if (modifier != NULL) {
-               mod = find_modifier(uc_mgr, modifier);
-               if (mod != NULL) {
-                       err = get_value1(value, &mod->value_list, identifier);
+       if (mod_dev_name || verb_name || !exact) {
+               if (verb_name && strlen(verb_name)) {
+                       verb = find_verb(uc_mgr, verb_name);
+               } else {
+                       verb = uc_mgr->active_verb;
+               }
+               if (verb) {
+                       if (mod_dev_name) {
+                               mod = find_modifier(uc_mgr, verb,
+                                                   mod_dev_name, 0);
+                               if (mod) {
+                                       err = get_value1(value,
+                                                        &mod->value_list,
+                                                        identifier);
+                                       if (err >= 0 || err != -ENOENT)
+                                               return err;
+                               }
+
+                               dev = find_device(uc_mgr, verb,
+                                                 mod_dev_name, 0);
+                               if (dev) {
+                                       err = get_value1(value,
+                                                        &dev->value_list,
+                                                        identifier);
+                                       if (err >= 0 || err != -ENOENT)
+                                               return err;
+                               }
+
+                               if (exact)
+                                       return -ENOENT;
+                       }
+
+                       err = get_value1(value, &verb->value_list, identifier);
                        if (err >= 0 || err != -ENOENT)
                                return err;
                }
+
+               if (exact)
+                       return -ENOENT;
        }
-       err = get_value1(value, &uc_mgr->active_verb->value_list, identifier);
-       if (err >= 0 || err != -ENOENT)
-               return err;
+
        err = get_value1(value, &uc_mgr->value_list, identifier);
        if (err >= 0 || err != -ENOENT)
                return err;
+
        return -ENOENT;
 }
 
 /**
  * \brief Get current - string
  * \param uc_mgr Use case manager
- * \param identifier 
+ * \param identifier
  * \param value Value pointer
  * \return Zero if success, otherwise a negative error code
  *
  * Note: String is dynamically allocated, use free() to
  * deallocate this string.
- */      
+ */
 int snd_use_case_get(snd_use_case_mgr_t *uc_mgr,
                     const char *identifier,
                     const char **value)
 {
-        char *str, *str1;
+       const char *slash1, *slash2, *mod_dev_after;
+       const char *ident, *mod_dev, *verb;
+       int exact = 0;
         int err;
 
        pthread_mutex_lock(&uc_mgr->mutex);
@@ -1113,28 +1262,56 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr,
                 }
                 err = 0;
         } else if (strcmp(identifier, "_verb") == 0) {
-                if (uc_mgr->active_verb == NULL)
-                        return -ENOENT;
+                if (uc_mgr->active_verb == NULL) {
+                        err = -ENOENT;
+                       goto __end;
+               }
                 *value = strdup(uc_mgr->active_verb->name);
                 if (*value == NULL) {
                         err = -ENOMEM;
                         goto __end;
                 }
                err = 0;
+       } else if (identifier[0] == '_') {
+               err = -ENOENT;
+               goto __end;
         } else {
-                str1 = strchr(identifier, '/');
-                if (str1) {
-                        str = strdup(str1 + 1);
-                       if (str == NULL) {
-                               err = -ENOMEM;
-                               goto __end;
-                        }
-                } else {
-                        str = NULL;
-                }
-                err = get_value(uc_mgr, identifier, value, str);
-                if (str)
-                        free(str);
+               if (identifier[0] == '=') {
+                       exact = 1;
+                       identifier++;
+               }
+
+               slash1 = strchr(identifier, '/');
+               if (slash1) {
+                       ident = strndup(identifier, slash1 - identifier);
+
+                       slash2 = strchr(slash1 + 1, '/');
+                       if (slash2) {
+                               mod_dev_after = slash2;
+                               verb = slash2 + 1;
+                       }
+                       else {
+                               mod_dev_after = slash1 + strlen(slash1);
+                               verb = NULL;
+                       }
+
+                       if (mod_dev_after == slash1 + 1)
+                               mod_dev = NULL;
+                       else
+                               mod_dev = strndup(slash1 + 1,
+                                                 mod_dev_after - (slash1 + 1));
+               }
+               else {
+                       ident = identifier;
+                       mod_dev = NULL;
+                       verb = NULL;
+               }
+
+               err = get_value(uc_mgr, ident, value, mod_dev, verb, exact);
+               if (ident != identifier)
+                       free((void *)ident);
+               if (mod_dev)
+                       free((void *)mod_dev);
         }
       __end:
        pthread_mutex_unlock(&uc_mgr->mutex);
@@ -1146,7 +1323,7 @@ long device_status(snd_use_case_mgr_t *uc_mgr,
 {
         struct use_case_device *dev;
         struct list_head *pos;
-        
+
         list_for_each(pos, &uc_mgr->active_devices) {
                 dev = list_entry(pos, struct use_case_device, active_list);
                 if (strcmp(dev->name, device_name) == 0)
@@ -1160,7 +1337,7 @@ long modifier_status(snd_use_case_mgr_t *uc_mgr,
 {
         struct use_case_modifier *mod;
         struct list_head *pos;
-        
+
         list_for_each(pos, &uc_mgr->active_modifiers) {
                 mod = list_entry(pos, struct use_case_modifier, active_list);
                 if (strcmp(mod->name, modifier_name) == 0)
@@ -1173,8 +1350,8 @@ long modifier_status(snd_use_case_mgr_t *uc_mgr,
 /**
  * \brief Get current - integer
  * \param uc_mgr Use case manager
- * \param identifier 
- * \return Value if success, otherwise a negative error code 
+ * \param identifier
+ * \return Value if success, otherwise a negative error code
  */
 int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr,
                      const char *identifier,
@@ -1209,8 +1386,16 @@ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr,
                                *value = err;
                                err = 0;
                        }
+#if 0
+               /*
+                * enable this block if the else clause below is expanded to query
+                * user-supplied values
+                */
+               } else if (identifier[0] == '_')
+                       err = -ENOENT;
+#endif
                } else
-                        err = -EINVAL;
+                        err = -ENOENT;
                 if (str)
                         free(str);
         }
@@ -1288,7 +1473,7 @@ static int set_device_user(snd_use_case_mgr_t *uc_mgr,
 
         if (uc_mgr->active_verb == NULL)
                 return -ENOENT;
-        device = find_device(uc_mgr->active_verb, device_name);
+        device = find_device(uc_mgr, uc_mgr->active_verb, device_name, 1);
         if (device == NULL)
                 return -ENOENT;
         return set_device(uc_mgr, device, enable);
@@ -1303,7 +1488,7 @@ static int set_modifier_user(snd_use_case_mgr_t *uc_mgr,
         if (uc_mgr->active_verb == NULL)
                 return -ENOENT;
 
-        modifier = find_modifier(uc_mgr, modifier_name);
+        modifier = find_modifier(uc_mgr, uc_mgr->active_verb, modifier_name, 1);
         if (modifier == NULL)
                 return -ENOENT;
         return set_modifier(uc_mgr, modifier, enable);
@@ -1317,7 +1502,7 @@ static int switch_device(snd_use_case_mgr_t *uc_mgr,
         struct transition_sequence *trans;
         struct list_head *pos;
         int err, seq_found = 0;
-        
+
         if (uc_mgr->active_verb == NULL)
                 return -ENOENT;
         if (device_status(uc_mgr, old_device) == 0) {
@@ -1328,10 +1513,12 @@ static int switch_device(snd_use_case_mgr_t *uc_mgr,
                 uc_error("error: device %s already enabled", new_device);
                 return -EINVAL;
         }
-        xold = find_device(uc_mgr->active_verb, old_device);
+        xold = find_device(uc_mgr, uc_mgr->active_verb, old_device, 1);
         if (xold == NULL)
                 return -ENOENT;
-        xnew = find_device(uc_mgr->active_verb, new_device);
+        list_del(&xold->active_list);
+        xnew = find_device(uc_mgr, uc_mgr->active_verb, new_device, 1);
+        list_add_tail(&xold->active_list, &uc_mgr->active_devices);
         if (xnew == NULL)
                 return -ENOENT;
         err = 0;
@@ -1369,7 +1556,7 @@ static int switch_modifier(snd_use_case_mgr_t *uc_mgr,
         struct transition_sequence *trans;
         struct list_head *pos;
         int err, seq_found = 0;
-        
+
         if (uc_mgr->active_verb == NULL)
                 return -ENOENT;
         if (modifier_status(uc_mgr, old_modifier) == 0) {
@@ -1380,10 +1567,10 @@ static int switch_modifier(snd_use_case_mgr_t *uc_mgr,
                 uc_error("error: modifier %s already enabled", new_modifier);
                 return -EINVAL;
         }
-        xold = find_modifier(uc_mgr, old_modifier);
+        xold = find_modifier(uc_mgr, uc_mgr->active_verb, old_modifier, 1);
         if (xold == NULL)
                 return -ENOENT;
-        xnew = find_modifier(uc_mgr, new_modifier);
+        xnew = find_modifier(uc_mgr, uc_mgr->active_verb, new_modifier, 1);
         if (xnew == NULL)
                 return -ENOENT;
         err = 0;
@@ -1410,7 +1597,7 @@ static int switch_modifier(snd_use_case_mgr_t *uc_mgr,
                 if (err < 0)
                         return err;
         }
-        return err;        
+        return err;
 }
 
 /**