OSDN Git Service

New syntax for the substituted variables - $(var).
authorJaroslav Kysela <perex@perex.cz>
Mon, 11 Jun 2001 08:07:48 +0000 (08:07 +0000)
committerJaroslav Kysela <perex@perex.cz>
Mon, 11 Jun 2001 08:07:48 +0000 (08:07 +0000)
Improved the variable substitution (all references in a string are replaced).
Added special redirect loading code (to separate card dependant code to
single files).

configure.in
include/conf.h
src/Makefile.am
src/alsa.conf
src/cards/Makefile.am [new file with mode: 0644]
src/cards/SI_7018.conf [new file with mode: 0644]
src/conf.c
src/pcm/pcm.c

index 36a85c8..7e9cc4c 100644 (file)
@@ -53,6 +53,6 @@ AC_OUTPUT(Makefile doc/Makefile include/Makefile src/Makefile \
           src/control/Makefile src/mixer/Makefile src/pcm/Makefile \
          src/rawmidi/Makefile src/timer/Makefile \
           src/hwdep/Makefile src/seq/Makefile src/instr/Makefile \
-          src/compat/Makefile aserver/Makefile \
+          src/compat/Makefile src/cards/Makefile aserver/Makefile \
           test/Makefile utils/Makefile \
           utils/alsa-lib.spec)
index 7b566bd..69aa2df 100644 (file)
@@ -80,6 +80,10 @@ const char *snd_config_get_id(snd_config_t *config);
 extern snd_config_t *snd_config;
 int snd_config_update(void);
 
+int snd_config_redirect_load(snd_config_t *root, snd_config_t *config,
+                            char **name, snd_config_t **dst_config,
+                            int *dst_dynamic);
+
 #ifdef __cplusplus
 }
 #endif
index 37076ab..d71cff0 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS=control mixer pcm rawmidi timer hwdep seq instr compat
+SUBDIRS=control mixer pcm rawmidi timer hwdep seq instr compat cards
 COMPATNUM=@LIBTOOL_VERSION_INFO@
 
 lib_LTLIBRARIES = libasound.la
index 323bd66..2dd02fd 100644 (file)
@@ -26,9 +26,9 @@ pcm.hw {
                default -1
        }               
        type hw
-       card $CARD
-       device $DEV
-       subdevice $SUBDEV
+       card $(CARD)
+       device $(DEV)
+       subdevice $(SUBDEV)
 }
 
 pcm.plughw {
@@ -48,9 +48,9 @@ pcm.plughw {
        type plug
        slave.pcm {
                type hw
-               card $CARD
-               device $DEV
-               subdevice $SUBDEV
+               card $(CARD)
+               device $(DEV)
+               subdevice $(SUBDEV)
        }
 }
 
@@ -60,7 +60,7 @@ pcm.plug {
                type string
        }
        type plug
-       slave.pcm $SLAVE
+       slave.pcm $(SLAVE)
 }
 
 pcm.shm {
@@ -73,8 +73,8 @@ pcm.shm {
                type string
        }
        type shm
-       server $SOCKET
-       pcm $PCM
+       server $(SOCKET)
+       pcm $(PCM)
 }
 
 pcm.tee {
@@ -92,9 +92,9 @@ pcm.tee {
                default raw
        }
        type file
-       slave.pcm $SLAVE
-       file $FILE
-       format $FORMAT
+       slave.pcm $(SLAVE)
+       file $(FILE)
+       format $(FORMAT)
 }
 
 pcm.file {
@@ -109,8 +109,8 @@ pcm.file {
        }
        type file
        slave.pcm null
-       file $FILE
-       format $FORMAT
+       file $(FILE)
+       format $(FORMAT)
 }
 
 pcm.surround40 {
@@ -125,9 +125,26 @@ pcm.surround40 {
                default <@ALSA_SURROUND40_DEVICE:0@>
        }
        type surround
-       card $CARD
-       device $DEVICE
-       stype 4.0
+       card $(CARD)
+       device $(DEVICE)
+       stype "4.0"
+}
+       
+pcm.surround40_new {
+       $.0 CARD
+       $.1 DEV
+       $.CARD {
+               type integer
+               default <@ALSA_SURROUND40_CARD,ALSA_PCM_CARD,ALSA_CARD:0@>
+       }
+       $.DEV {
+               type integer
+               default <@ALSA_SURROUND40_DEVICE:0@>
+       }
+       redirect {
+               filename "&(datadir)/cards/&(card_id:$(CARD)).conf"
+               name "pcm.surround40_$(DEV)_&(pcm_id:$(CARD),$(DEV)):$(CARD),$(DEV)"
+       }
 }
        
 pcm.surround51 {
@@ -142,9 +159,9 @@ pcm.surround51 {
                default <@ALSA_SURROUND51_DEVICE:0@>
        }
        type surround
-       card $CARD
-       device $DEVICE
-       stype 5.1
+       card $(CARD)
+       device $(DEVICE)
+       stype "5.1"
 }
        
 pcm.null {
@@ -162,7 +179,7 @@ ctl.hw {
                type integer
        }
        type hw
-       card $CARD
+       card $(CARD)
 }
 
 ctl.shm {
@@ -175,8 +192,8 @@ ctl.shm {
                type string
        }
        type shm
-       server $SOCKET
-       ctl $PCM
+       server $(SOCKET)
+       ctl $(PCM)
 }
 
 rawmidi.default {
@@ -200,9 +217,9 @@ rawmidi.hw {
                default -1
        }               
        type hw
-       card $CARD
-       device $DEV
-       subdevice $SUBDEV
+       card $(CARD)
+       device $(DEV)
+       subdevice $(SUBDEV)
 }
 
 seq.default {
@@ -242,7 +259,7 @@ pcm.iec958 {
                default 0x00
        }
        type hooks
-       slave.pcm $PCM
+       slave.pcm $(PCM)
        hooks.0 {
                type ctl_elems
                args.0 {
@@ -250,10 +267,10 @@ pcm.iec958 {
                        subdevice 0
                        preserve true
                        lock true
-                       value.0 $AES0
-                       value.1 $AES1
-                       value.2 $AES2
-                       value.3 $AES3
+                       value.0 $(AES0)
+                       value.1 $(AES1)
+                       value.2 $(AES2)
+                       value.3 $(AES3)
                }
        }
 }
diff --git a/src/cards/Makefile.am b/src/cards/Makefile.am
new file mode 100644 (file)
index 0000000..4696066
--- /dev/null
@@ -0,0 +1,5 @@
+alsadir = $(datadir)/alsa/cards
+cfg_files = SI_7018.conf
+
+EXTRA_DIST = $(cfg_files)
+alsa_DATA = $(cfg_files)
diff --git a/src/cards/SI_7018.conf b/src/cards/SI_7018.conf
new file mode 100644 (file)
index 0000000..1dce64d
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# Configuration for the SI7018 chip
+#
+
+pcm.surround40_0_trident_dx_nx {
+       $.0 CARD
+       $.1 DEV
+       $.CARD {
+               type integer
+       }
+       $.DEV {
+               type integer
+       }
+       type hw
+       card $(CARD)
+       device $(DEV)
+       subdevice -1
+}
index 8b27143..e50bc06 100644 (file)
@@ -63,6 +63,38 @@ typedef struct {
        } error;
 } input_t;
 
+int safe_strtol(const char *str, long *val)
+{
+       char *end;
+       long v;
+       if (!*str)
+               return -EINVAL;
+       errno = 0;
+       v = strtol(str, &end, 0);
+       if (errno)
+               return -errno;
+       if (*end)
+               return -EINVAL;
+       *val = v;
+       return 0;
+}
+
+static int safe_strtod(const char *str, double *val)
+{
+       char *end;
+       double v;
+       if (!*str)
+               return -EINVAL;
+       errno = 0;
+       v = strtod(str, &end);
+       if (errno)
+               return -errno;
+       if (*end)
+               return -EINVAL;
+       *val = v;
+       return 0;
+}
+
 static int get_char(input_t *input)
 {
        int c;
@@ -721,7 +753,7 @@ static int _snd_config_save_leaf(snd_config_t *n, snd_output_t *out,
                snd_output_printf(out, "%ld", n->u.integer);
                break;
        case SND_CONFIG_TYPE_REAL:
-               snd_output_printf(out, "%16g", n->u.real);
+               snd_output_printf(out, "%-16g", n->u.real);
                break;
        case SND_CONFIG_TYPE_STRING:
                string_print(n->u.string, 0, out);
@@ -882,7 +914,7 @@ int snd_config_load(snd_config_t *config, snd_input_t *in)
                                assert(0);
                                break;
                        }
-                       SNDERR("%s:%d:%d:%s", fd->name ? fd->name : "",
+                       SNDERR("%s:%d:%d:%s", fd->name ? fd->name : "_toplevel_",
                            fd->line, fd->column, str);
                }
                goto _end;
@@ -1084,6 +1116,48 @@ int snd_config_set_string(snd_config_t *config, const char *value)
 }
 
 /**
+ * \brief Change the value of a config node
+ * \param config Config node handle
+ * \param ascii Value in ASCII form
+ * \return 0 on success otherwise a negative error code
+ */
+int snd_config_set_ascii(snd_config_t *config, const char *ascii)
+{
+       assert(config && ascii);
+       switch (snd_enum_to_int(config->type)) {
+       case SND_CONFIG_TYPE_INTEGER:
+               {
+                       long i;
+                       int err = safe_strtol(ascii, &i);
+                       if (err < 0)
+                               return err;
+                       config->u.integer = i;
+               }
+               break;
+       case SND_CONFIG_TYPE_REAL:
+               {
+                       double d;
+                       int err = safe_strtod(ascii, &d);
+                       if (err < 0)
+                               return err;
+                       config->u.real = d;
+                       break;
+               }
+       case SND_CONFIG_TYPE_STRING:
+               {
+                       char *ptr = realloc(config->u.string, strlen(ascii) + 1);
+                       if (ptr == NULL)
+                               return -ENOMEM;
+                       strcpy(config->u.string, ascii);
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/**
  * \brief Get the value of an integer config node
  * \param config Config node handle
  * \param ptr Returned value pointer
@@ -1129,6 +1203,51 @@ int snd_config_get_string(snd_config_t *config, const char **ptr)
 }
 
 /**
+ * \brief Get the value in ASCII form
+ * \param config Config node handle
+ * \param ascii Returned dynamically allocated ASCII string
+ * \return 0 on success otherwise a negative error code
+ */
+int snd_config_get_ascii(snd_config_t *config, char **ascii)
+{
+       assert(config && ascii);
+       switch (snd_enum_to_int(config->type)) {
+       case SND_CONFIG_TYPE_INTEGER:
+               {
+                       char res[12];
+                       int err;
+                       err = snprintf(res, sizeof(res), "%li", config->u.integer);
+                       if (err < 0 || err == sizeof(res)) {
+                               assert(0);
+                               return -ENOMEM;
+                       }
+                       *ascii = strdup(res);
+               }
+               break;
+       case SND_CONFIG_TYPE_REAL:
+               {
+                       char res[32];
+                       int err;
+                       err = snprintf(res, sizeof(res), "%-16g", config->u.real);
+                       if (err < 0 || err == sizeof(res)) {
+                               assert(0);
+                               return -ENOMEM;
+                       }
+                       *ascii = strdup(res);
+               }
+               break;
+       case SND_CONFIG_TYPE_STRING:
+               *ascii = strdup(config->u.string);
+               break;
+       default:
+               return -EINVAL;
+       }
+       if (*ascii == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
+/**
  * \brief Dump a config tree contents
  * \param config Config node handle
  * \param out Output handle
@@ -1526,12 +1645,97 @@ static int _snd_config_copy(snd_config_t *src,
        return 1;
 }
 
+/**
+ * \brief Return the copy of configuration
+ * \param dst Destination configuration handle
+ * \return src Source configuration handle
+ */
 int snd_config_copy(snd_config_t **dst,
                    snd_config_t *src)
 {
        return snd_config_walk(src, dst, _snd_config_copy, NULL);
 }
 
+/**
+ * \brief Expand the dynamic contents
+ * \param src Source string
+ * \param idchr Identification character
+ * \param callback Callback function
+ * \param private_data Private data for the given callback function
+ * \param dst Destination string
+ */
+int snd_config_string_replace(const char *src, char idchr,
+                             int (*callback)(const char *what, char **dst, void *private_data),
+                             void *private_data,
+                             char **dst)
+{
+       int len = 0, len1, err;
+       const char *ptr, *end;
+       char *tmp, *what, *fptr, *rdst = NULL;
+
+       assert(src && idchr && dst);
+       while (*src != '\0') {
+               ptr = strchr(src, idchr);
+               end = NULL;
+               if (ptr == src && *(ptr + 1) == '(' && (end = strchr(ptr + 2, ')')) != NULL) {
+                       src = end + 1;
+                       if (callback == NULL)
+                               continue;
+                       len1 = end - (ptr + 2);
+                       if (len1 == 0)          /* empty */
+                               continue;
+                       what = malloc(len1 + 1);
+                       memcpy(what, ptr + 2, len1);
+                       what[len1] = '\0';
+                       fptr = NULL;
+                       err = callback(what, &fptr, private_data);
+                       free(what);
+                       if (err < 0) {
+                               if (*dst != NULL)
+                                       free(*dst);
+                               return err;
+                       }
+                       if (fptr == NULL)       /* empty */
+                               continue;
+                       len1 = strlen(ptr = fptr);
+               } else {
+                       if (ptr == NULL) {
+                               len1 = strlen(ptr = src);
+                       } else {
+                               len1 = ptr - src;
+                               ptr = src;
+                       }
+                       src += len1;
+                       fptr = NULL;
+               }
+               tmp = realloc(rdst, len + len1 + 1);
+               if (tmp == NULL) {
+                       if (*dst != NULL)
+                               free(*dst);
+                       return -ENOMEM;
+               }
+               memcpy(tmp + len, ptr, len1);
+               tmp[len+=len1] = '\0';
+               if (fptr)
+                       free(fptr);
+               rdst = tmp;
+       }
+       *dst = rdst;
+       return 0;
+}
+
+static int _snd_config_expand_replace(const char *what, char **dst, void *private_data)
+{
+       snd_config_t *vars = private_data;
+       snd_config_t *val;
+
+       assert(dst);
+       if (snd_config_search(vars, what, &val) < 0)
+               return 0;       /* empty */
+       else
+               return snd_config_get_ascii(val, dst);
+}
+
 static int _snd_config_expand(snd_config_t *src,
                              snd_config_t **dst,
                              snd_config_walk_pass_t pass,
@@ -1578,8 +1782,11 @@ static int _snd_config_expand(snd_config_t *src,
                        snd_config_t *val;
                        snd_config_t *vars = private_data;
                        err = snd_config_get_string(src, &s);
-                       if (s[0] == '$') {
-                               if (snd_config_search(vars, s + 1, &val) < 0)
+                       if (strncmp(s, "$(", 2) == 0 && s[strlen(s) - 1] == ')') {
+                               char *str = alloca((strlen(s) - 3) + 1);
+                               memcpy(str, s + 2, strlen(s) - 3);
+                               str[strlen(s) - 3] = 0;
+                               if (snd_config_search(vars, str, &val) < 0)
                                        return 0;
                                err = snd_config_copy(dst, val);
                                if (err < 0)
@@ -1593,10 +1800,26 @@ static int _snd_config_expand(snd_config_t *src,
                                err = snd_config_make(dst, id, type);
                                if (err < 0)
                                        return err;
-                               err = snd_config_set_string(*dst, s);
-                               if (err < 0) {
-                                       snd_config_delete(*dst);
-                                       return err;
+                               if (strstr(s, "$(") != NULL) {
+                                       char *str = NULL;
+                                       err = snd_config_string_replace(s, '$', _snd_config_expand_replace, vars, &str);
+                                       if (err < 0)
+                                               return err;
+                                       if (str == NULL) {
+                                               snd_config_delete(*dst);
+                                               return 0;
+                                       }
+                                       err = snd_config_set_string(*dst, str);
+                                       if (err < 0) {
+                                               snd_config_delete(*dst);
+                                               return err;
+                                       }
+                               } else {
+                                       err = snd_config_set_string(*dst, s);
+                                       if (err < 0) {
+                                               snd_config_delete(*dst);
+                                               return err;
+                                       }
                                }
                        }
                        break;
@@ -1647,39 +1870,6 @@ static int load_defaults(snd_config_t *subs, snd_config_t *defs)
        return 0;
 }
 
-int safe_strtol(const char *str, long *val)
-{
-       char *end;
-       long v;
-       if (!*str)
-               return -EINVAL;
-       errno = 0;
-       v = strtol(str, &end, 0);
-       if (errno)
-               return -errno;
-       if (*end)
-               return -EINVAL;
-       *val = v;
-       return 0;
-}
-
-static int safe_strtod(const char *str, double *val)
-{
-       char *end;
-       double v;
-       if (!*str)
-               return -EINVAL;
-       errno = 0;
-       v = strtod(str, &end);
-       if (errno)
-               return -errno;
-       if (*end)
-               return -EINVAL;
-       *val = v;
-       return 0;
-}
-
-
 static void skip_blank(const char **ptr)
 {
        while (1) {
@@ -2062,3 +2252,183 @@ static void _snd_config_end(void)
        files_info_count = 0;
 }
 #endif
+
+static int _snd_config_redirect_load_replace(const char *what, char **dst, void *private_data ATTRIBUTE_UNUSED)
+{
+       enum {
+               CARD_ID,
+               PCM_ID,
+               RAWMIDI_ID
+       } id;
+       int len;
+
+       if (!strcmp(what, "datadir")) {
+               *dst = strdup(DATADIR "/alsa");
+               return *dst == NULL ? -ENOMEM : 0;
+       }
+       if (!strncmp(what, "card_id:", len = 8))
+               id = CARD_ID;
+       else if (!strncmp(what, "pcm_id:", len = 7))
+               id = PCM_ID;
+       else if (!strncmp(what, "rawmidi_id:", len = 11))
+               id = RAWMIDI_ID;
+       else
+               return 0;
+       {
+               snd_ctl_t *ctl;
+               int err;
+               char name[12];
+               const char *str = NULL;
+               char *fstr = NULL;
+               sprintf(name, "hw:%d", atoi(what + len));
+               err = snd_ctl_open(&ctl, name, 0);
+               if (err < 0)
+                       return err;
+               switch (id) {
+               case CARD_ID:
+                       {
+                               snd_ctl_card_info_t *info;
+                               snd_ctl_card_info_alloca(&info);
+                               err = snd_ctl_card_info(ctl, info);
+                               if (err < 0)
+                                       return err;
+                               err = snd_card_type_enum_to_string(snd_ctl_card_info_get_type(info), &fstr);
+                       }
+                       break;
+               case PCM_ID:
+                       {
+                               char *ptr = strchr(what + len, ',');
+                               int dev = atoi(what + len);
+                               int subdev = ptr ? atoi(ptr + 1) : -1;
+                               snd_pcm_info_t *info;
+                               snd_pcm_info_alloca(&info);
+                               snd_pcm_info_set_device(info, dev);
+                               snd_pcm_info_set_subdevice(info, subdev);
+                               err = snd_ctl_pcm_info(ctl, info);
+                               if (err < 0)
+                                       return err;
+                               str = snd_pcm_info_get_id(info);
+                       }
+                       break;
+               case RAWMIDI_ID:
+                       {
+                               char *ptr = strchr(what + len, ',');
+                               int dev = atoi(what + len);
+                               int subdev = ptr ? atoi(ptr + 1) : -1;
+                               snd_rawmidi_info_t *info;
+                               snd_rawmidi_info_alloca(&info);
+                               snd_rawmidi_info_set_device(info, dev);
+                               snd_rawmidi_info_set_subdevice(info, subdev);
+                               err = snd_ctl_rawmidi_info(ctl, info);
+                               if (err < 0)
+                                       return err;
+                               str = snd_rawmidi_info_get_id(info);
+                       }
+                       break;
+               }
+               if (err < 0)
+                       return err;
+               snd_ctl_close(ctl);
+               *dst = fstr ? fstr : (str ? strdup(str) : NULL);
+               if (*dst == NULL)
+                       return 0;
+               return 0;
+       }
+       return 0;       /* empty */
+}
+
+/**
+ * \brief Redirect the configuration block to an another
+ * \param root the root of all configurations
+ * \param config redirect configuration
+ * \param name the identifier of new configuration block
+ * \param dst_config new configuration block
+ * \param dst_dynamic new configuration block is dynamically allocated
+ */
+int snd_config_redirect_load(snd_config_t *root,
+                            snd_config_t *config,
+                            char **name,
+                            snd_config_t **dst_config,
+                            int *dst_dynamic)
+{
+       int err, dynamic;
+       snd_config_t *result, *c;
+       char *rname;
+
+       assert(config);
+       assert(name);
+       assert(dst_config);
+       assert(dst_dynamic);
+       if (snd_config_get_type(config) == SND_CONFIG_TYPE_STRING) {
+               const char *str;
+               snd_config_get_string(config, &str);
+               *name = strdup(str);
+               if (*name == NULL)
+                       return -ENOMEM;
+               *dst_config = root;
+               *dst_dynamic = 0;
+               return 0;
+       }
+       if (snd_config_get_type(config) != SND_CONFIG_TYPE_COMPOUND)
+               return -EINVAL;
+       result = root;
+       dynamic = 0;
+       rname = NULL;
+       if (snd_config_search(config, "filename", &c) >= 0) {
+               snd_config_t *rconfig;
+               char *filename;
+               snd_input_t *input;
+               err = snd_config_copy(&rconfig, root);
+               if (err < 0)
+                       return err;
+               if (snd_config_get_type(c) == SND_CONFIG_TYPE_STRING) {
+                       snd_config_get_string(c, (const char **)&filename);
+                       if ((err = snd_config_string_replace(filename, '&', _snd_config_redirect_load_replace, NULL, &filename)) < 0)
+                               goto __filename_error;
+                       if (filename == NULL)
+                               goto __filename_error_einval;
+               } else {
+                     __filename_error_einval:
+                       err = -EINVAL;
+                     __filename_error:
+                       snd_config_delete(rconfig);
+                       return err;
+               }
+               err = snd_input_stdio_open(&input, filename, "r");
+               if (err < 0) {
+                       SNDERR("Unable to open filename %s: %s", filename, snd_strerror(err));
+                       goto __filename_error;
+               }
+               err = snd_config_load(rconfig, input);
+               if (err < 0) {
+                       snd_input_close(input);
+                       goto __filename_error;
+                       return err;
+               }
+               snd_input_close(input);
+               free(filename);
+               result = rconfig;
+               dynamic = 1;
+       }
+       if (snd_config_search(config, "name", &c) >= 0) {
+               const char *ptr;
+               if ((err = snd_config_get_string(c, &ptr)) < 0)
+                       goto __error;
+               if ((err = snd_config_string_replace(ptr, '&', _snd_config_redirect_load_replace, NULL, &rname)) < 0)
+                       goto __error;
+       }
+       if (rname == NULL) {
+               err = -EINVAL;
+               goto __error;
+       }
+       *dst_config = result;
+       *dst_dynamic = dynamic;
+       *name = rname;
+       return 0;
+      __error:
+       if (rname)
+               free(rname);
+       if (dynamic)
+               snd_config_delete(result);
+       return err;
+}
index 44050d9..cf6d60b 100644 (file)
@@ -995,30 +995,68 @@ static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
        return 0;
 }
 
-static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, const char *name, 
-                                snd_pcm_stream_t stream, int mode)
+static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
+                                const char *name, snd_pcm_stream_t stream, int mode)
 {
        int err;
        snd_config_t *pcm_conf;
+       char *key;
        const char *args = strchr(name, ':');
        char *base;
+       snd_config_t *conf;
        if (args) {
                args++;
                base = alloca(args - name);
                memcpy(base, name, args - name - 1);
-               base[args - name - 1] = 0;
-       } else
-               base = (char *) name;
-       err = snd_config_search_alias(snd_config, "pcm", base, &pcm_conf);
+               base[args - name - 1] = '\0';
+               key = strchr(base, '.');
+               if (key)
+                       *key++ = '\0';
+       } else {
+               key = strchr(name, '.');
+               if (key) {
+                       key++;
+                       base = alloca(key - name);
+                       memcpy(base, name, key - name - 1);
+                       base[key - name - 1] = '\0';
+               } else
+                       base = (char *) name;
+       }
+       if (key == NULL) {
+               key = base;
+               base = NULL;
+       }
+       err = snd_config_search_alias(root, base, key, &pcm_conf);
        if (err < 0) {
-               SNDERR("Unknown PCM %s", name);
-               return err;
+               (void)(base == NULL && (err = snd_config_search_alias(root, "pcm", key, &pcm_conf)));
+               if (err < 0) {
+                       SNDERR("Unknown PCM %s", name);
+                       return err;
+               }
        }
+       if (args == NULL && snd_config_search(pcm_conf, "$", &conf) >= 0) /* expand arguments */
+               args = "";
        if (args) {
                err = snd_config_expand(pcm_conf, args, &pcm_conf);
                if (err < 0)
                        return err;
        }
+       if (snd_config_search(pcm_conf, "redirect", &conf) >= 0) {
+               snd_config_t *tmp_conf;
+               int conf_free_tmp;
+               char *redir_name = NULL;
+               err = snd_config_redirect_load(root, conf, &redir_name, &tmp_conf, &conf_free_tmp);
+               if (args)
+                       snd_config_delete(pcm_conf);
+               if (err < 0)
+                       return err;
+               err = snd_pcm_open_noupdate(pcmp, tmp_conf, redir_name, stream, mode);
+               if (redir_name)
+                       free(redir_name);
+               if (conf_free_tmp)
+                       snd_config_delete(tmp_conf);
+               return err;
+       }
        err = snd_pcm_open_conf(pcmp, name, pcm_conf, stream, mode);
        if (args)
                snd_config_delete(pcm_conf);
@@ -1041,7 +1079,7 @@ int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
        err = snd_config_update();
        if (err < 0)
                return err;
-       return snd_pcm_open_noupdate(pcmp, name, stream, mode);
+       return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode);
 }
 
 #ifndef DOC_HIDDEN
@@ -1050,7 +1088,7 @@ int snd_pcm_open_slave(snd_pcm_t **pcmp, snd_config_t *conf,
 {
        const char *str;
        if (snd_config_get_string(conf, &str) >= 0)
-               return snd_pcm_open_noupdate(pcmp, str, stream, mode);
+               return snd_pcm_open_noupdate(pcmp, snd_config, str, stream, mode);
        return snd_pcm_open_conf(pcmp, NULL, conf, stream, mode);
 }
 #endif