]> git.alsa-project.org Git - alsa-lib.git/commitdiff
More work on pcm_hooks
authorAbramo Bagnara <abramo@alsa-project.org>
Wed, 6 Jun 2001 11:49:52 +0000 (11:49 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Wed, 6 Jun 2001 11:49:52 +0000 (11:49 +0000)
doc/asoundrc.doc
include/pcm.h
src/conf.c
src/pcm/pcm_hooks.c
src/pcm/pcm_local.h
src/pcm/pcm_multi.c
src/pcm/pcm_route.c
src/pcm/pcm_share.c

index e2f8c293b705d903d1abe2b4e236afbe1a91ed7b..3cc0afaf3f6d704b447df8e1ac6bba6680f1d9ba 100644 (file)
@@ -42,10 +42,15 @@ hook_args.NAME {
   ...                  # Arbitrary arguments
 }
 
-# PCM hook definition
-pcm_hook.NAME {
+# PCM hook type
+pcm_hook_type.NAME {
   [lib STR]            # Library file (default libasound.so)
   [install STR]                # Install function (default _snd_pcm_hook_NAME_install)
+}
+
+# PCM hook definition
+pcm_hook.NAME {
+  type STR             # PCM Hook type (see pcm_hook_type)
   [args STR]           # Arguments for install function (see hook_args)
   # or
   [args { }]           # Arguments for install function
index 2f537f223e41d68ba21f641a8436eccd4ce366eb..a54dc473b93e8e430bbd6f6d8ee715df9931f7e2 100644 (file)
@@ -721,10 +721,10 @@ void snd_pcm_info_set_subdevice(snd_pcm_info_t *obj, unsigned int val);
 void snd_pcm_info_set_stream(snd_pcm_info_t *obj, snd_pcm_stream_t val);
 
 typedef enum _snd_pcm_hook_type {
-       SND_PCM_HOOK_HW_PARAMS,
-       SND_PCM_HOOK_HW_FREE,
-       SND_PCM_HOOK_CLOSE,
-       SND_PCM_HOOK_LAST = SND_PCM_HOOK_CLOSE,
+       SND_PCM_HOOK_TYPE_HW_PARAMS,
+       SND_PCM_HOOK_TYPE_HW_FREE,
+       SND_PCM_HOOK_TYPE_CLOSE,
+       SND_PCM_HOOK_TYPE_LAST = SND_PCM_HOOK_TYPE_CLOSE,
 } snd_pcm_hook_type_t;
 
 typedef struct _snd_pcm_hook snd_pcm_hook_t;
index 17f3304ec2722adff44ec2a5ecb9c912351c1e63..b09a7f71f3d77418a898b2801b0052d14b9cb35e 100644 (file)
@@ -1610,7 +1610,7 @@ static int load_defaults(snd_config_t *subs, snd_config_t *defs)
        return 0;
 }
 
-static int safe_strtol(const char *str, long *val)
+int safe_strtol(const char *str, long *val)
 {
        char *end;
        long v;
index 0fd19651b914b03cf6cffda0f6f345e2a374c4ba..20526e8f13f85883c5d04f41576fdca93d86a9a1 100644 (file)
@@ -33,7 +33,7 @@ struct _snd_pcm_hook {
 typedef struct {
        snd_pcm_t *slave;
        int close_slave;
-       struct list_head hooks[SND_PCM_HOOK_LAST + 1];
+       struct list_head hooks[SND_PCM_HOOK_TYPE_LAST + 1];
 } snd_pcm_hooks_t;
 
 static int snd_pcm_hooks_close(snd_pcm_t *pcm)
@@ -47,13 +47,13 @@ static int snd_pcm_hooks_close(snd_pcm_t *pcm)
                if (err < 0)
                        return err;
        }
-       list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_CLOSE]) {
+       list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_CLOSE]) {
                snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list);
                err = hook->func(hook);
                if (err < 0)
                        return err;
        }
-       for (k = 0; k <= SND_PCM_HOOK_LAST; ++k) {
+       for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) {
                struct list_head *hooks = &h->hooks[k];
                while (!list_empty(hooks)) {
                        snd_pcm_hook_t *hook;
@@ -201,7 +201,7 @@ static int snd_pcm_hooks_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
        int err = snd_pcm_hw_params(h->slave, params);
        if (err < 0)
                return err;
-       list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_HW_PARAMS]) {
+       list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_HW_PARAMS]) {
                snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list);
                err = hook->func(hook);
                if (err < 0)
@@ -217,7 +217,7 @@ static int snd_pcm_hooks_hw_free(snd_pcm_t *pcm)
        int err = snd_pcm_hw_free(h->slave);
        if (err < 0)
                return err;
-       list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_HW_FREE]) {
+       list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_HW_FREE]) {
                snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list);
                err = hook->func(hook);
                if (err < 0)
@@ -299,7 +299,7 @@ int snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int
                return -ENOMEM;
        h->slave = slave;
        h->close_slave = close_slave;
-       for (k = 0; k <= SND_PCM_HOOK_LAST; ++k) {
+       for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) {
                INIT_LIST_HEAD(&h->hooks[k]);
        }
        pcm = calloc(1, sizeof(snd_pcm_t));
@@ -331,7 +331,7 @@ int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *conf)
        char buf[256];
        const char *str;
        const char *lib = NULL, *install = NULL;
-       snd_config_t *args = NULL;
+       snd_config_t *type = NULL, *args = NULL;
        snd_config_iterator_t i, next;
        int (*install_func)(snd_pcm_t *pcm, snd_config_t *args);
        void *h;
@@ -351,20 +351,8 @@ int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *conf)
                const char *id = snd_config_get_id(n);
                if (strcmp(id, "comment") == 0)
                        continue;
-               if (strcmp(id, "lib") == 0) {
-                       err = snd_config_get_string(n, &lib);
-                       if (err < 0) {
-                               SNDERR("Invalid type for %s", id);
-                               return -EINVAL;
-                       }
-                       continue;
-               }
-               if (strcmp(id, "install") == 0) {
-                       err = snd_config_get_string(n, &install);
-                       if (err < 0) {
-                               SNDERR("Invalid type for %s", id);
-                               return -EINVAL;
-                       }
+               if (strcmp(id, "type") == 0) {
+                       type = n;
                        continue;
                }
                if (strcmp(id, "args") == 0) {
@@ -374,20 +362,59 @@ int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *conf)
                SNDERR("Unknown field %s", id);
                return -EINVAL;
        }
-       if (args && snd_config_get_string(args, &str) >= 0) {
-               err = snd_config_search_alias(snd_config, "hook_args", str, &args);
-               if (err < 0) {
-                       SNDERR("unknown hook_args %s", str);
-                       return err;
+       if (!type) {
+               SNDERR("type is not defined");
+               return -EINVAL;
+       }
+       err = snd_config_get_string(type, &str);
+       if (err < 0) {
+               SNDERR("Invalid type for %s", snd_config_get_id(type));
+               return err;
+       }
+       err = snd_config_search_alias(snd_config, "pcm_hook_type", str, &type);
+       if (err >= 0) {
+               if (snd_config_get_type(type) != SND_CONFIG_TYPE_COMPOUND) {
+                       SNDERR("Invalid type for PCM type %s definition", str);
+                       return -EINVAL;
+               }
+               snd_config_for_each(i, next, type) {
+                       snd_config_t *n = snd_config_iterator_entry(i);
+                       const char *id = snd_config_get_id(n);
+                       if (strcmp(id, "comment") == 0)
+                               continue;
+                       if (strcmp(id, "lib") == 0) {
+                               err = snd_config_get_string(n, &lib);
+                               if (err < 0) {
+                                       SNDERR("Invalid type for %s", id);
+                                       return -EINVAL;
+                               }
+                               continue;
+                       }
+                       if (strcmp(id, "install") == 0) {
+                               err = snd_config_get_string(n, &install);
+                               if (err < 0) {
+                                       SNDERR("Invalid type for %s", id);
+                                       return -EINVAL;
+                               }
+                               continue;
+                       }
+                       SNDERR("Unknown field %s", id);
+                       return -EINVAL;
                }
        }
        if (!install) {
                install = buf;
-               snprintf(buf, sizeof(buf), "_snd_pcm_hook_%s_install",
-                        snd_config_get_id(conf));
+               snprintf(buf, sizeof(buf), "_snd_pcm_hook_%s_install", str);
        }
        if (!lib)
                lib = ALSA_LIB;
+       if (args && snd_config_get_string(args, &str) >= 0) {
+               err = snd_config_search_alias(snd_config, "hook_args", str, &args);
+               if (err < 0) {
+                       SNDERR("unknown hook_args %s", str);
+                       return err;
+               }
+       }
        h = dlopen(lib, RTLD_NOW);
        if (!h) {
                SNDERR("Cannot open shared library %s", lib);
@@ -534,3 +561,641 @@ int snd_pcm_hook_remove(snd_pcm_hook_t *hook)
        return 0;
 }
 
+typedef struct {
+       unsigned int lock: 1;
+       unsigned int preserve: 1;
+       snd_ctl_elem_id_t *id;
+       snd_ctl_elem_info_t *info;
+       snd_ctl_elem_value_t *val;
+       snd_ctl_elem_value_t *mask;
+       snd_ctl_elem_value_t *old;
+       struct list_head list;
+} snd_pcm_hook_ctl_elem_t;
+
+typedef struct {
+       snd_ctl_t *ctl;
+       struct list_head elems;
+} snd_pcm_hook_ctl_elems_t;
+
+static int free_elems(snd_pcm_hook_ctl_elems_t *h)
+{
+       int err;
+       while (!list_empty(&h->elems)) {
+               snd_pcm_hook_ctl_elem_t *elem = list_entry(h->elems.next, snd_pcm_hook_ctl_elem_t, list);
+               snd_ctl_elem_id_free(elem->id);
+               snd_ctl_elem_info_free(elem->info);
+               snd_ctl_elem_value_free(elem->val);
+               snd_ctl_elem_value_free(elem->mask);
+               snd_ctl_elem_value_free(elem->old);
+               list_del(&elem->list);
+               free(elem);
+       }
+       err = snd_ctl_close(h->ctl);
+       if (err < 0)
+               return err;
+       free(h);
+       return 0;
+}
+
+static int snd_pcm_hook_ctl_elems_hw_params(snd_pcm_hook_t *hook)
+{
+       snd_pcm_hook_ctl_elems_t *h = snd_pcm_hook_get_private(hook);
+       struct list_head *pos;
+       int err;
+       unsigned int k;
+       list_for_each(pos, &h->elems) {
+               snd_pcm_hook_ctl_elem_t *elem = list_entry(pos, snd_pcm_hook_ctl_elem_t, list);
+               unsigned int count;
+               snd_ctl_elem_type_t type;
+               if (elem->lock) {
+                       err = snd_ctl_elem_lock(h->ctl, elem->id);
+                       if (err < 0) {
+                               SNDERR("Cannot lock ctl elem");
+                               return err;
+                       }
+               }
+               err = snd_ctl_elem_read(h->ctl, elem->old);
+               if (err < 0) {
+                       SNDERR("Cannot read ctl elem");
+                       return err;
+               }
+               count = snd_ctl_elem_info_get_count(elem->info);
+               type = snd_ctl_elem_info_get_type(elem->info);
+               switch (type) {
+               case SND_CTL_ELEM_TYPE_BOOLEAN:
+                       for (k = 0; k < count; ++k) {
+                               int old, val, mask;
+                               old = snd_ctl_elem_value_get_boolean(elem->old, k);
+                               mask = snd_ctl_elem_value_get_boolean(elem->mask, k);
+                               old &= ~mask;
+                               if (old) {
+                                       val = snd_ctl_elem_value_get_boolean(elem->val, k);
+                                       val |= old;
+                                       snd_ctl_elem_value_set_boolean(elem->val, k, val);
+                               }
+                       }
+                       break;
+               case SND_CTL_ELEM_TYPE_INTEGER:
+                       for (k = 0; k < count; ++k) {
+                               long old, val, mask;
+                               old = snd_ctl_elem_value_get_integer(elem->old, k);
+                               mask = snd_ctl_elem_value_get_integer(elem->mask, k);
+                               old &= ~mask;
+                               if (old) {
+                                       val = snd_ctl_elem_value_get_integer(elem->val, k);
+                                       val |= old;
+                                       snd_ctl_elem_value_set_integer(elem->val, k, val);
+                               }
+                       }
+                       break;
+               case SND_CTL_ELEM_TYPE_ENUMERATED:
+                       for (k = 0; k < count; ++k) {
+                               unsigned int old, val, mask;
+                               old = snd_ctl_elem_value_get_enumerated(elem->old, k);
+                               mask = snd_ctl_elem_value_get_enumerated(elem->mask, k);
+                               old &= ~mask;
+                               if (old) {
+                                       val = snd_ctl_elem_value_get_enumerated(elem->val, k);
+                                       val |= old;
+                                       snd_ctl_elem_value_set_enumerated(elem->val, k, val);
+                               }
+                       }
+                       break;
+               case SND_CTL_ELEM_TYPE_IEC958:
+                       count = sizeof(snd_aes_iec958_t);
+                       /* Fall through */
+               case SND_CTL_ELEM_TYPE_BYTES:
+                       for (k = 0; k < count; ++k) {
+                               unsigned char old, val, mask;
+                               old = snd_ctl_elem_value_get_byte(elem->old, k);
+                               mask = snd_ctl_elem_value_get_byte(elem->mask, k);
+                               old &= ~mask;
+                               if (old) {
+                                       val = snd_ctl_elem_value_get_byte(elem->val, k);
+                                       val |= old;
+                                       snd_ctl_elem_value_set_byte(elem->val, k, val);
+                               }
+                       }
+                       break;
+               default:
+                       assert(0);
+                       break;
+               }
+               err = snd_ctl_elem_write(h->ctl, elem->val);
+               if (err < 0) {
+                       SNDERR("Cannot write ctl elem");
+                       return err;
+               }
+       }
+       return 0;
+}
+
+static int snd_pcm_hook_ctl_elems_hw_free(snd_pcm_hook_t *hook)
+{
+       snd_pcm_hook_ctl_elems_t *h = snd_pcm_hook_get_private(hook);
+       struct list_head *pos;
+       int err;
+       list_for_each(pos, &h->elems) {
+               snd_pcm_hook_ctl_elem_t *elem = list_entry(pos, snd_pcm_hook_ctl_elem_t, list);
+               if (elem->lock) {
+                       err = snd_ctl_elem_unlock(h->ctl, elem->id);
+                       if (err < 0) {
+                               SNDERR("Cannot unlock ctl elem");
+                               return err;
+                       }
+               }
+               if (elem->preserve) {
+                       err = snd_ctl_elem_write(h->ctl, elem->old);
+                       if (err < 0) {
+                               SNDERR("Cannot restore ctl elem");
+                               return err;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int snd_pcm_hook_ctl_elems_close(snd_pcm_hook_t *hook)
+{
+       snd_pcm_hook_ctl_elems_t *h = snd_pcm_hook_get_private(hook);
+       return free_elems(h);
+}
+
+int snd_config_get_iface(snd_config_t *conf)
+{
+       long v;
+       const char *str;
+       int err;
+       snd_ctl_elem_iface_t idx;
+       err = snd_config_get_integer(conf, &v);
+       if (err >= 0) {
+               if (v < 0 || v > SND_CTL_ELEM_IFACE_LAST) {
+               _invalid_value:
+                       SNDERR("Invalid value for %s", snd_config_get_id(conf));
+                       return -EINVAL;
+               }
+               return v;
+       }
+       err = snd_config_get_string(conf, &str);
+       if (err < 0) {
+               SNDERR("Invalid type for %s", snd_config_get_id(conf));
+               return -EINVAL;
+       }
+       for (idx = 0; idx <= SND_CTL_ELEM_IFACE_LAST; idx++) {
+               if (strcasecmp(snd_ctl_elem_iface_name(idx), str) == 0)
+                       return idx;
+       }
+       goto _invalid_value;
+}
+
+int snd_config_get_bool(snd_config_t *conf)
+{
+       long v;
+       const char *str;
+       int err;
+       unsigned int k;
+       static struct {
+               const char *str;
+               int val;
+       } b[] = {
+               { "false", 0 },
+               { "true", 1 },
+               { "no", 0 },
+               { "yes", 1 },
+               { "off", 0 },
+               { "on", 1 },
+       };
+       err = snd_config_get_integer(conf, &v);
+       if (err >= 0) {
+               if (v < 0 || v > 1) {
+               _invalid_value:
+                       SNDERR("Invalid value for %s", snd_config_get_id(conf));
+                       return -EINVAL;
+               }
+               return v;
+       }
+       err = snd_config_get_string(conf, &str);
+       if (err < 0) {
+               SNDERR("Invalid type for %s", snd_config_get_id(conf));
+               return -EINVAL;
+       }
+       for (k = 0; k < sizeof(b) / sizeof(*b); k++) {
+               if (strcasecmp(b[k].str, str) == 0)
+                       return b[k].val;
+       }
+       goto _invalid_value;
+}
+
+int snd_config_get_ctl_elem_enumerated(snd_config_t *n, snd_ctl_t *ctl,
+                                      snd_ctl_elem_info_t *info)
+{
+       const char *str;
+       long val;
+       unsigned int idx, items;
+       switch (snd_enum_to_int(snd_config_get_type(n))) {
+       case SND_CONFIG_TYPE_INTEGER:
+               snd_config_get_integer(n, &val);
+               return val;
+       case SND_CONFIG_TYPE_STRING:
+               snd_config_get_string(n, &str);
+               break;
+       default:
+               return -1;
+       }
+       items = snd_ctl_elem_info_get_items(info);
+       for (idx = 0; idx < items; idx++) {
+               int err;
+               snd_ctl_elem_info_set_item(info, idx);
+               err = snd_ctl_elem_info(ctl, info);
+               if (err < 0) {
+                       SNDERR("Cannot obtain info for CTL elem");
+                       return err;
+               }
+               if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
+                       return idx;
+       }
+       return -1;
+}
+
+int snd_config_get_ctl_elem_value(snd_config_t *conf,
+                                 snd_ctl_t *ctl,
+                                 snd_ctl_elem_value_t *val,
+                                 snd_ctl_elem_value_t *mask,
+                                 snd_ctl_elem_info_t *info)
+{
+       int err;
+       snd_config_iterator_t i, next;
+       snd_ctl_elem_id_t *id;
+       snd_ctl_elem_type_t type;
+       unsigned int count;
+       long v;
+       long idx;
+       snd_ctl_elem_id_alloca(&id);
+       snd_ctl_elem_value_get_id(val, id);
+       count = snd_ctl_elem_info_get_count(info);
+       type = snd_ctl_elem_info_get_type(info);
+       if (count == 1) {
+               switch (snd_enum_to_int(type)) {
+               case SND_CTL_ELEM_TYPE_BOOLEAN:
+                       v = snd_config_get_bool(conf);
+                       if (v >= 0) {
+                               snd_ctl_elem_value_set_boolean(val, 0, v);
+                               if (mask)
+                                       snd_ctl_elem_value_set_boolean(mask, 0, 1);
+                               return 0;
+                       }
+                       break;
+               case SND_CTL_ELEM_TYPE_INTEGER:
+                       err = snd_config_get_integer(conf, &v);
+                       if (err == 0) {
+                               snd_ctl_elem_value_set_integer(val, 0, v);
+                               if (mask)
+                                       snd_ctl_elem_value_set_integer(mask, 0, ~0L);
+                               return 0;
+                       }
+                       break;
+               case SND_CTL_ELEM_TYPE_ENUMERATED:
+                       v = snd_config_get_ctl_elem_enumerated(conf, ctl, info);
+                       if (v >= 0) {
+                               snd_ctl_elem_value_set_enumerated(val, 0, v);
+                               if (mask)
+                                       snd_ctl_elem_value_set_enumerated(mask, 0, ~0);
+                               return 0;
+                       }
+                       break;
+               case SND_CTL_ELEM_TYPE_BYTES:
+               case SND_CTL_ELEM_TYPE_IEC958:
+                       break;
+               default:
+                       SNDERR("Unknow control type: %d", snd_enum_to_int(type));
+                       return -EINVAL;
+               }
+       }
+       switch (snd_enum_to_int(type)) {
+       case SND_CTL_ELEM_TYPE_IEC958:
+               count = sizeof(snd_aes_iec958_t);
+               /* Fall through */
+       case SND_CTL_ELEM_TYPE_BYTES:
+       {
+               const char *buf;
+               err = snd_config_get_string(conf, &buf);
+               if (err >= 0) {
+                       int c1 = 0;
+                       unsigned int len = strlen(buf);
+                       unsigned int idx = 0;
+                       if (len % 2 != 0 || len > count * 2) {
+                       _bad_content:
+                               SNDERR("bad value content\n");
+                               return -EINVAL;
+                       }
+                       while (*buf) {
+                               int c = *buf++;
+                               if (c >= '0' && c <= '9')
+                                       c -= '0';
+                               else if (c >= 'a' && c <= 'f')
+                                       c = c - 'a' + 10;
+                               else if (c >= 'A' && c <= 'F')
+                                       c = c - 'A' + 10;
+                               else {
+                                       goto _bad_content;
+                               }
+                               if (idx % 2 == 1) {
+                                       snd_ctl_elem_value_set_byte(val, idx / 2, c1 << 4 | c);
+                                       if (mask)
+                                               snd_ctl_elem_value_set_byte(mask, idx / 2, 0xff);
+                               } else
+                                       c1 = c;
+                               idx++;
+                       }
+                       return 0;
+               }
+       }
+       default:
+               break;
+       }
+       if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
+               SNDERR("bad value type");
+               return -EINVAL;
+       }
+
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               err = safe_strtol(snd_config_get_id(n), &idx);
+               if (err < 0 || idx < 0 || (unsigned int) idx >= count) {
+                       SNDERR("bad value index");
+                       return -EINVAL;
+               }
+               switch (snd_enum_to_int(type)) {
+               case SND_CTL_ELEM_TYPE_BOOLEAN:
+                       v = snd_config_get_bool(n);
+                       if (v < 0)
+                               goto _bad_content;
+                       snd_ctl_elem_value_set_boolean(val, idx, v);
+                       if (mask)
+                               snd_ctl_elem_value_set_boolean(mask, idx, 1);
+                       break;
+               case SND_CTL_ELEM_TYPE_INTEGER:
+                       err = snd_config_get_integer(n, &v);
+                       if (err < 0)
+                               goto _bad_content;
+                       snd_ctl_elem_value_set_integer(val, idx, v);
+                       if (mask)
+                               snd_ctl_elem_value_set_integer(mask, idx, ~0L);
+                       break;
+               case SND_CTL_ELEM_TYPE_ENUMERATED:
+                       v = snd_config_get_ctl_elem_enumerated(n, ctl, info);
+                       if (v < 0)
+                               goto _bad_content;
+                       snd_ctl_elem_value_set_enumerated(val, idx, v);
+                       if (mask)
+                               snd_ctl_elem_value_set_enumerated(mask, idx, ~0);
+                       break;
+               case SND_CTL_ELEM_TYPE_BYTES:
+               case SND_CTL_ELEM_TYPE_IEC958:
+                       err = snd_config_get_integer(n, &v);
+                       if (err < 0 || v < 0 || v > 255)
+                               goto _bad_content;
+                       snd_ctl_elem_value_set_byte(val, idx, v);
+                       if (mask)
+                               snd_ctl_elem_value_set_byte(mask, idx, 0xff);
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+static int add_elem(snd_pcm_hook_ctl_elems_t *h, snd_config_t *conf, snd_pcm_info_t *info)
+{
+       snd_config_iterator_t i, next;
+       int iface = SND_CTL_ELEM_IFACE_PCM;
+       const char *name = NULL;
+       long index = 0;
+       long device = -1;
+       long subdevice = -1;
+       int lock = 0;
+       int preserve = 0;
+       snd_config_t *value = NULL, *mask = NULL;
+       snd_pcm_hook_ctl_elem_t *elem;
+       int err;
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id = snd_config_get_id(n);
+               if (strcmp(id, "comment") == 0)
+                       continue;
+               if (strcmp(id, "iface") == 0) {
+                       iface = snd_config_get_iface(n);
+                       if (iface < 0)
+                               return iface;
+                       continue;
+               }
+               if (strcmp(id, "name") == 0) {
+                       err = snd_config_get_string(n, &name);
+                       if (err < 0) {
+                       _invalid_type:
+                               SNDERR("Invalid type for %s", id);
+                               return err;
+                       }
+                       continue;
+               }
+               if (strcmp(id, "index") == 0) {
+                       err = snd_config_get_integer(n, &index);
+                       if (err < 0)
+                               goto _invalid_type;
+                       continue;
+               }
+               if (strcmp(id, "device") == 0) {
+                       err = snd_config_get_integer(n, &device);
+                       if (err < 0)
+                               goto _invalid_type;
+                       continue;
+               }
+               if (strcmp(id, "subdevice") == 0) {
+                       err = snd_config_get_integer(n, &subdevice);
+                       if (err < 0)
+                               goto _invalid_type;
+                       continue;
+               }
+               if (strcmp(id, "lock") == 0) {
+                       lock = snd_config_get_bool(n);
+                       if (lock < 0) {
+                               err = lock;
+                       _invalid_value:
+                               SNDERR("Invalid value for %s", id);
+                               return err;
+                       }
+                       continue;
+               }
+               if (strcmp(id, "preserve") == 0) {
+                       preserve = snd_config_get_bool(n);
+                       if (preserve < 0) {
+                               err = preserve;
+                               goto _invalid_value;
+                       }
+                       continue;
+               }
+               if (strcmp(id, "value") == 0) {
+                       value = n;
+                       continue;
+               }
+               if (strcmp(id, "mask") == 0) {
+                       mask = n;
+                       continue;
+               }
+               SNDERR("Unknown field %s", id);
+               return -EINVAL;
+       }
+       if (!name) {
+               SNDERR("Missing control name");
+               return -EINVAL;
+       }
+       if (!value) {
+               SNDERR("Missing control value");
+               return -EINVAL;
+       }
+       if (device < 0) {
+               if (iface == SND_CTL_ELEM_IFACE_PCM)
+                       device = snd_pcm_info_get_device(info);
+               else
+                       device = 0;
+       }
+       if (subdevice < 0) {
+               if (iface == SND_CTL_ELEM_IFACE_PCM)
+                       subdevice = snd_pcm_info_get_subdevice(info);
+               else
+                       subdevice = 0;
+       }
+       elem = calloc(1, sizeof(*elem));
+       if (!elem)
+               return -ENOMEM;
+       err = snd_ctl_elem_id_malloc(&elem->id);
+       if (err < 0)
+               goto _err;
+       err = snd_ctl_elem_info_malloc(&elem->info);
+       if (err < 0)
+               goto _err;
+       err = snd_ctl_elem_value_malloc(&elem->val);
+       if (err < 0)
+               goto _err;
+       err = snd_ctl_elem_value_malloc(&elem->mask);
+       if (err < 0)
+               goto _err;
+       err = snd_ctl_elem_value_malloc(&elem->old);
+       if (err < 0)
+               goto _err;
+       elem->lock = lock;
+       elem->preserve = preserve;
+       snd_ctl_elem_id_set_interface(elem->id, iface);
+       snd_ctl_elem_id_set_name(elem->id, name);
+       snd_ctl_elem_id_set_index(elem->id, index);
+       snd_ctl_elem_id_set_device(elem->id, device);
+       snd_ctl_elem_id_set_subdevice(elem->id, subdevice);
+       snd_ctl_elem_info_set_id(elem->info, elem->id);
+       err = snd_ctl_elem_info(h->ctl, elem->info);
+       if (err < 0) {
+               SNDERR("Cannot obtain info for CTL elem");
+               goto _err;
+       }
+       snd_ctl_elem_value_set_id(elem->val, elem->id);
+       snd_ctl_elem_value_set_id(elem->old, elem->id);
+       if (mask) {
+               err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, NULL, elem->info);
+               if (err < 0)
+                       goto _err;
+               err = snd_config_get_ctl_elem_value(mask, h->ctl, elem->mask, NULL, elem->info);
+               if (err < 0)
+                       goto _err;
+       } else {
+               err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
+               if (err < 0)
+                       goto _err;
+       }
+               
+       err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
+       if (err < 0)
+               goto _err;
+       list_add_tail(&elem->list, &h->elems);
+       return 0;
+
+ _err:
+       if (elem->id)
+               snd_ctl_elem_id_free(elem->id);
+       if (elem->info)
+               snd_ctl_elem_info_free(elem->info);
+       if (elem->val)
+               snd_ctl_elem_value_free(elem->val);
+       if (elem->mask)
+               snd_ctl_elem_value_free(elem->mask);
+       if (elem->old)
+               snd_ctl_elem_value_free(elem->old);
+       free(elem);
+       return err;
+}
+
+int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
+{
+       int err;
+       int card;
+       snd_pcm_info_t *info;
+       char ctl_name[16];
+       snd_ctl_t *ctl;
+       snd_pcm_hook_ctl_elems_t *h;
+       snd_config_iterator_t i, next;
+       snd_pcm_hook_t *h_hw_params = NULL, *h_hw_free = NULL, *h_close = NULL;
+       assert(conf);
+       assert(snd_config_get_type(conf) == SND_CONFIG_TYPE_COMPOUND);
+       snd_pcm_info_alloca(&info);
+       err = snd_pcm_info(pcm, info);
+       if (err < 0)
+               return err;
+       card = snd_pcm_info_get_card(info);
+       if (card < 0) {
+               SNDERR("No card for this PCM");
+               return -EINVAL;
+       }
+       sprintf(ctl_name, "hw:%d", card);
+       err = snd_ctl_open(&ctl, ctl_name, 0);
+       if (err < 0) {
+               SNDERR("Cannot open CTL %s", ctl_name);
+               return err;
+       }
+       h = calloc(1, sizeof(*h));
+       if (!h) {
+               snd_ctl_close(ctl);
+               return -ENOMEM;
+       }
+       h->ctl = ctl;
+       INIT_LIST_HEAD(&h->elems);
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               err = add_elem(h, n, info);
+               if (err < 0) {
+                       free_elems(h);
+                       return err;
+               }
+       }
+       err = snd_pcm_hook_add(&h_hw_params, pcm, SND_PCM_HOOK_TYPE_HW_PARAMS,
+                              snd_pcm_hook_ctl_elems_hw_params, h);
+       if (err < 0)
+               goto _err;
+       err = snd_pcm_hook_add(&h_hw_free, pcm, SND_PCM_HOOK_TYPE_HW_FREE,
+                              snd_pcm_hook_ctl_elems_hw_free, h);
+       if (err < 0)
+               goto _err;
+       err = snd_pcm_hook_add(&h_close, pcm, SND_PCM_HOOK_TYPE_CLOSE,
+                              snd_pcm_hook_ctl_elems_close, h);
+       if (err < 0)
+               goto _err;
+       return 0;
+ _err:
+       if (h_hw_params)
+               snd_pcm_hook_remove(h_hw_params);
+       if (h_hw_free)
+               snd_pcm_hook_remove(h_hw_free);
+       if (h_close)
+               snd_pcm_hook_remove(h_close);
+       free_elems(h);
+       return err;
+}
+
index a890708e53ac326538558e377c7b4b8ac6854694..5e672f428562f722aa35581718a868c7bc175e46 100644 (file)
@@ -576,3 +576,4 @@ int snd_pcm_conf_generic_id(const char *id);
         (1U << SND_PCM_FORMAT_U32_LE) | \
         (1U << SND_PCM_FORMAT_U32_BE))
 
+int safe_strtol(const char *str, long *val);
index e8e7359e808c61f73c51fd7cd554afebfd5b1f59..036dd326d2b4909b98673e4859f6fb2755d78a99 100644 (file)
@@ -79,15 +79,14 @@ static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid)
 static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
 {
        snd_pcm_multi_t *multi = pcm->private_data;
-       if (multi->slaves_count == 1)
-               return snd_pcm_info(multi->slaves[0].pcm, info);
-       memset(info, 0, sizeof(*info));
-       info->stream = snd_enum_to_int(pcm->stream);
-       info->card = -1;
-       strncpy(info->id, pcm->name, sizeof(info->id));
-       strncpy(info->name, pcm->name, sizeof(info->name));
-       strncpy(info->subname, pcm->name, sizeof(info->subname));
-       info->subdevices_count = 1;
+       int err, n;
+       assert(info->subdevice < multi->slaves_count);
+       n = info->subdevice;
+       info->subdevice = 0;
+       err = snd_pcm_info(multi->slaves[n].pcm, info);
+       if (err < 0)
+               return err;
+       info->subdevices_count = multi->slaves_count;
        return 0;
 }
 
@@ -698,17 +697,15 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, snd_config_t *conf,
                ++slaves_count;
        }
        snd_config_for_each(i, inext, bindings) {
-               int cchannel = -1;
-               char *p;
+               long cchannel;
                snd_config_t *m = snd_config_iterator_entry(i);
                const char *id = snd_config_get_id(m);
-               errno = 0;
-               cchannel = strtol(id, &p, 10);
-               if (errno || *p || cchannel < 0) {
+               err = safe_strtol(id, &cchannel);
+               if (err < 0 || cchannel < 0) {
                        SNDERR("Invalid channel number: %s", id);
                        return -EINVAL;
                }
-               if ((unsigned)cchannel >= channels_count)
+               if ((unsigned long)cchannel >= channels_count)
                        channels_count = cchannel + 1;
        }
        if (channels_count == 0) {
@@ -745,8 +742,8 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, snd_config_t *conf,
                long val;
                const char *str;
                const char *id = snd_config_get_id(m);
-               cchannel = strtol(id, 0, 10);
-               if (cchannel < 0) {
+               err = safe_strtol(id, &cchannel);
+               if (err < 0 || cchannel < 0) {
                        SNDERR("Invalid channel number: %s", id);
                        err = -EINVAL;
                        goto _free;
index b4c6efc8d66924cd23b9cd750bed417ce4439c66..c4aaf5f783a39bf0d18876cfe2baa9116ba5339d 100644 (file)
@@ -784,16 +784,15 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
        int sused = -1;
        snd_config_iterator_t i, inext;
        unsigned int k;
+       int err;
        for (k = 0; k < tt_csize * tt_ssize; ++k)
                ttable[k] = 0.0;
        snd_config_for_each(i, inext, tt) {
                snd_config_t *in = snd_config_iterator_entry(i);
                snd_config_iterator_t j, jnext;
-               char *p;
                long cchannel;
-               errno = 0;
-               cchannel = strtol(snd_config_get_id(in), &p, 10);
-               if (errno || *p || 
+               err = safe_strtol(snd_config_get_id(in), &cchannel);
+               if (err < 0 || 
                    cchannel < 0 || (unsigned int) cchannel > tt_csize) {
                        SNDERR("Invalid client channel: %s", snd_config_get_id(in));
                        return -EINVAL;
@@ -804,11 +803,9 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
                        snd_config_t *jnode = snd_config_iterator_entry(j);
                        double value;
                        long schannel;
-                       int err;
                        const char *id = snd_config_get_id(jnode);
-                       errno = 0;
-                       schannel = strtol(id, &p, 10);
-                       if (errno || *p || 
+                       err = safe_strtol(id, &schannel);
+                       if (err < 0 || 
                            schannel < 0 || (unsigned int) schannel > tt_ssize || 
                            (schannels > 0 && schannel >= schannels)) {
                                SNDERR("Invalid slave channel: %s", id);
index ad133f38268536fe2c4c4b171d3828947d34bf28..d6a1a1b737d71d765e775048b68da0d9f0e699e5 100644 (file)
@@ -1435,12 +1435,10 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name, snd_config_t *conf,
        }
        snd_config_for_each(i, next, bindings) {
                int cchannel = -1;
-               char *p;
                snd_config_t *n = snd_config_iterator_entry(i);
                const char *id = snd_config_get_id(n);
-               errno = 0;
-               cchannel = strtol(id, &p, 10);
-               if (errno || *p || cchannel < 0) {
+               err = safe_strtol(id, &cchannel);
+               if (err < 0 || cchannel < 0) {
                        SNDERR("Invalid client channel in binding: %s", id);
                        return -EINVAL;
                }
@@ -1458,7 +1456,7 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name, snd_config_t *conf,
                const char *id = snd_config_get_id(n);
                long cchannel;
                long schannel = -1;
-               cchannel = strtol(id, 0, 10);
+               cchannel = atoi(id);
                err = snd_config_get_integer(n, &schannel);
                if (err < 0)
                        goto _free;