tizen 2.3.1 release
[external/alsa-lib.git] / src / ucm / parser.c
index e8d2461..b93d832 100644 (file)
@@ -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
@@ -59,10 +59,10 @@ int parse_string(snd_config_t *n, char **res)
 /*
  * Parse safe ID
  */
-int parse_is_name_safe(char *name)
+int parse_is_name_safe(const char *name)
 {
        if (strchr(name, '.')) {
-               uc_error("char '.' now allowed in '%s'", name);
+               uc_error("char '.' not allowed in '%s'", name);
                return 0;
        }
        return 1;
@@ -75,7 +75,9 @@ int parse_get_safe_id(snd_config_t *n, const char **id)
        err = snd_config_get_id(n, id);
        if (err < 0)
                return err;
-       return parse_is_name_safe((char *)(*id));
+       if (!parse_is_name_safe((char *)(*id)))
+               return -EINVAL;
+       return 0;
 }
 
 /*
@@ -151,7 +153,7 @@ static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
                n = snd_config_iterator_entry(i);
 
                if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
-                       uc_error("compound type expected for %s", id);
+                       uc_error("compound type expected for %s, is %d", id, snd_config_get_type(cfg));
                        return -EINVAL;
                }
                
@@ -163,20 +165,40 @@ static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
        return 0;
 }
 
+static int strip_legacy_dev_index(char *name)
+{
+       char *dot = strchr(name, '.');
+       if (!dot)
+               return 0;
+       if (dot[1] != '0' || dot[2] != '\0') {
+               uc_error("device name %s contains a '.',"
+                        " and is not legacy foo.0 format", name);
+               return -EINVAL;
+       }
+       *dot = '\0';
+       return 0;
+}
 
 /*
- * Parse transition
+ * Parse device list
  */
-static int parse_supported_device(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
-                                 struct list_head *dlist,
-                                 snd_config_t *cfg)
+static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
+                            struct dev_list *dev_list,
+                            enum dev_list_type type,
+                            snd_config_t *cfg)
 {
-       struct dev_list *sdev;
+       struct dev_list_node *sdev;
        const char *id;
        snd_config_iterator_t i, next;
        snd_config_t *n;
        int err;
 
+       if (dev_list->type != DEVLIST_NONE) {
+               uc_error("error: multiple supported or"
+                       " conflicting device lists");
+               return -EEXIST;
+       }
+
        if (snd_config_get_id(cfg, &id) < 0)
                return -EINVAL;
 
@@ -191,7 +213,7 @@ static int parse_supported_device(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
                if (snd_config_get_id(n, &id) < 0)
                        return -EINVAL;
 
-               sdev = calloc(1, sizeof(struct dev_list));
+               sdev = calloc(1, sizeof(struct dev_list_node));
                if (sdev == NULL)
                        return -ENOMEM;
                err = parse_string(n, &sdev->name);
@@ -199,8 +221,17 @@ static int parse_supported_device(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
                        free(sdev);
                        return err;
                }
-               list_add(&sdev->list, dlist);
+               err = strip_legacy_dev_index(sdev->name);
+               if (err < 0) {
+                       free(sdev->name);
+                       free(sdev);
+                       return err;
+               }
+               list_add(&sdev->list, &dev_list->list);
        }
+
+       dev_list->type = type;
+
        return 0;
 }
 
@@ -285,6 +316,17 @@ static int parse_sequence(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
                        continue;
                }
 
+               if (strcmp(cmd, "msleep") == 0) {
+                       curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
+                       err = snd_config_get_integer(n, &curr->data.sleep);
+                       if (err < 0) {
+                               uc_error("error: msleep requires integer!");
+                               return err;
+                       }
+                       curr->data.sleep *= 1000L;
+                       continue;
+               }
+
                if (strcmp(cmd, "exec") == 0) {
                        curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
                        err = parse_string(n, &curr->data.exec);
@@ -389,12 +431,18 @@ static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
  * Parse Modifier Use cases
  *
  *     # Each modifier is described in new section. N modifiers are allowed
- *     SectionModifier."Capture Voice".0 {
+ *     SectionModifier."Capture Voice" {
  *
  *             Comment "Record voice call"
+ *
  *             SupportedDevice [
- *                     "x"             # all x device instances
- *                     "y.0"           # device y instance 0 only
+ *                     "x"
+ *                     "y"
+ *             ]
+ *
+ *             ConflictingDevice [
+ *                     "x"
+ *                     "y"
  *             ]
  *
  *             EnableSequence [
@@ -418,6 +466,9 @@ static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
  *             }
  *
  *      }
+ *
+ * SupportedDevice and ConflictingDevice cannot be specified together.
+ * Both are optional.
  */
 static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
                snd_config_t *cfg,
@@ -426,14 +477,21 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
 {
        struct use_case_verb *verb = data1;
        struct use_case_modifier *modifier;
-       char *name = data2;
-       const char *id;
+       const char *name;
        snd_config_iterator_t i, next;
        snd_config_t *n;
        int err;
 
-       if (!parse_is_name_safe(name))
-               return -EINVAL;
+       if (data2) {
+               name = data2;
+               if (!parse_is_name_safe(name))
+                       return -EINVAL;
+       }
+       else {
+               if (parse_get_safe_id(cfg, &name) < 0)
+                       return -EINVAL;
+       }
+
        /* allocate modifier */
        modifier = calloc(1, sizeof(*modifier));
        if (modifier == NULL)
@@ -441,18 +499,10 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
        INIT_LIST_HEAD(&modifier->enable_list);
        INIT_LIST_HEAD(&modifier->disable_list);
        INIT_LIST_HEAD(&modifier->transition_list);
-       INIT_LIST_HEAD(&modifier->dev_list);
+       INIT_LIST_HEAD(&modifier->dev_list.list);
        INIT_LIST_HEAD(&modifier->value_list);
        list_add_tail(&modifier->list, &verb->modifier_list);
-       err = parse_get_safe_id(cfg, &id);
-       if (err < 0)
-               return err;
-       modifier->name = malloc(strlen(name) + strlen(id) + 2);
-       if (modifier->name == NULL)
-               return -ENOMEM;
-       strcpy(modifier->name, name);
-       strcat(modifier->name, ".");
-       strcat(modifier->name, id);
+       modifier->name = strdup(name);
 
        snd_config_for_each(i, next, cfg) {
                const char *id;
@@ -470,7 +520,8 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
                }
 
                if (strcmp(id, "SupportedDevice") == 0) {
-                       err = parse_supported_device(uc_mgr, &modifier->dev_list, n);
+                       err = parse_device_list(uc_mgr, &modifier->dev_list,
+                                               DEVLIST_SUPPORTED, n);
                        if (err < 0) {
                                uc_error("error: failed to parse supported"
                                        " device list");
@@ -478,6 +529,16 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
                        }
                }
 
+               if (strcmp(id, "ConflictingDevice") == 0) {
+                       err = parse_device_list(uc_mgr, &modifier->dev_list,
+                                               DEVLIST_CONFLICTING, n);
+                       if (err < 0) {
+                               uc_error("error: failed to parse conflicting"
+                                       " device list");
+                               return err;
+                       }
+               }
+
                if (strcmp(id, "EnableSequence") == 0) {
                        err = parse_sequence(uc_mgr, &modifier->enable_list, n);
                        if (err < 0) {
@@ -518,11 +579,6 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
                }
        }
 
-       if (list_empty(&modifier->dev_list)) {
-               uc_error("error: %s: modifier missing supported device sequence", modifier->name);
-               return -EINVAL;
-       }
-
        return 0;
 }
 
@@ -530,9 +586,19 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
  * Parse Device Use Cases
  *
  *# Each device is described in new section. N devices are allowed
- *SectionDevice."Headphones".0 {
+ *SectionDevice."Headphones" {
  *     Comment "Headphones connected to 3.5mm jack"
  *
+ *     upportedDevice [
+ *             "x"
+ *             "y"
+ *     ]
+ *
+ *     ConflictingDevice [
+ *             "x"
+ *             "y"
+ *     ]
+ *
  *     EnableSequence [
  *             ....
  *     ]
@@ -551,37 +617,38 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
  *     }
  * }
  */
-static int parse_device_index(snd_use_case_mgr_t *uc_mgr,
-                             snd_config_t *cfg,
-                             void *data1,
-                             void *data2)
+static int parse_device(snd_use_case_mgr_t *uc_mgr,
+                       snd_config_t *cfg,
+                       void *data1,
+                       void *data2)
 {
        struct use_case_verb *verb = data1;
-       char *name = data2;
+       const char *name;
        struct use_case_device *device;
-       const char *id;
        snd_config_iterator_t i, next;
        snd_config_t *n;
        int err;
-       
-       if (!parse_is_name_safe(name))
-               return -EINVAL;
+
+       if (data2) {
+               name = data2;
+               if (!parse_is_name_safe(name))
+                       return -EINVAL;
+       }
+       else {
+               if (parse_get_safe_id(cfg, &name) < 0)
+                       return -EINVAL;
+       }
+
        device = calloc(1, sizeof(*device));
        if (device == NULL)
                return -ENOMEM;
        INIT_LIST_HEAD(&device->enable_list);
        INIT_LIST_HEAD(&device->disable_list);
        INIT_LIST_HEAD(&device->transition_list);
+       INIT_LIST_HEAD(&device->dev_list.list);
        INIT_LIST_HEAD(&device->value_list);
        list_add_tail(&device->list, &verb->device_list);
-       if (parse_get_safe_id(cfg, &id) < 0)
-               return -EINVAL;
-       device->name = malloc(strlen(name) + strlen(id) + 2);
-       if (device->name == NULL)
-               return -ENOMEM;
-       strcpy(device->name, name);
-       strcat(device->name, ".");
-       strcat(device->name, id);
+       device->name = strdup(name);
 
        snd_config_for_each(i, next, cfg) {
                const char *id;
@@ -598,6 +665,26 @@ static int parse_device_index(snd_use_case_mgr_t *uc_mgr,
                        continue;
                }
 
+               if (strcmp(id, "SupportedDevice") == 0) {
+                       err = parse_device_list(uc_mgr, &device->dev_list,
+                                               DEVLIST_SUPPORTED, n);
+                       if (err < 0) {
+                               uc_error("error: failed to parse supported"
+                                       " device list");
+                               return err;
+                       }
+               }
+
+               if (strcmp(id, "ConflictingDevice") == 0) {
+                       err = parse_device_list(uc_mgr, &device->dev_list,
+                                               DEVLIST_CONFLICTING, n);
+                       if (err < 0) {
+                               uc_error("error: failed to parse conflicting"
+                                       " device list");
+                               return err;
+                       }
+               }
+
                if (strcmp(id, "EnableSequence") == 0) {
                        uc_dbg("EnableSequence");
                        err = parse_sequence(uc_mgr, &device->enable_list, n);
@@ -643,34 +730,66 @@ static int parse_device_index(snd_use_case_mgr_t *uc_mgr,
        return 0;
 }
 
-static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
-                            snd_config_t *cfg,
-                            void *data1,
-                            void *data2 ATTRIBUTE_UNUSED)
+static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
+         snd_config_t *cfg,
+         int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
+         void *data1)
 {
-       const char *id;
+       const char *id, *idchild;
+       int child_ctr = 0, legacy_format = 1;
+       snd_config_iterator_t i, next;
+       snd_config_t *child;
        int err;
 
        err = snd_config_get_id(cfg, &id);
        if (err < 0)
                return err;
-       return parse_compound(uc_mgr, cfg, parse_device_index,
-                             data1, (void *)id);
+
+       snd_config_for_each(i, next, cfg) {
+               child_ctr++;
+               if (child_ctr > 1) {
+                       break;
+               }
+
+               child = snd_config_iterator_entry(i);
+
+               if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+                       legacy_format = 0;
+                       break;
+               }
+
+               if (snd_config_get_id(child, &idchild) < 0)
+                       return -EINVAL;
+
+               if (strcmp(idchild, "0")) {
+                       legacy_format = 0;
+                       break;
+               }
+       }
+       if (child_ctr != 1) {
+               legacy_format = 0;
+       }
+
+       if (legacy_format)
+               return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id);
+       else
+               return fcn(uc_mgr, cfg, data1, NULL);
 }
 
-static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
+static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
                             snd_config_t *cfg,
                             void *data1,
                             void *data2 ATTRIBUTE_UNUSED)
 {
-       const char *id;
-       int err;
+       return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
+}
 
-       err = snd_config_get_id(cfg, &id);
-       if (err < 0)
-               return err;
-       return parse_compound(uc_mgr, cfg, parse_modifier,
-                             data1, (void *)id);
+static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
+                            snd_config_t *cfg,
+                            void *data1,
+                            void *data2 ATTRIBUTE_UNUSED)
+{
+       return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
 }
 
 /*