OSDN Git Service

Moved some info fields to proper place (setup). Renamed mmap_size to mmap_bytes....
authorAbramo Bagnara <abramo@alsa-project.org>
Sat, 1 Jul 2000 10:38:29 +0000 (10:38 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Sat, 1 Jul 2000 10:38:29 +0000 (10:38 +0000)
include/pcm.h
src/pcm/pcm.c
src/pcm/pcm_common.c
src/pcm/pcm_hw.c
src/pcm/pcm_local.h
src/pcm/pcm_mmap.c
src/pcm/pcm_multi.c
src/pcm/pcm_plug.c

index 0a4a0b3..9369f39 100644 (file)
@@ -108,6 +108,7 @@ int snd_pcm_close(snd_pcm_t *handle);
 int snd_pcm_file_descriptor(snd_pcm_t *handle);
 int snd_pcm_nonblock(snd_pcm_t *handle, int nonblock);
 int snd_pcm_info(snd_pcm_t *handle, snd_pcm_info_t *info);
+int snd_pcm_params_info(snd_pcm_t *handle, snd_pcm_params_info_t *info);
 int snd_pcm_params(snd_pcm_t *handle, snd_pcm_params_t *params);
 int snd_pcm_setup(snd_pcm_t *handle, snd_pcm_setup_t *setup);
 int snd_pcm_channel_setup(snd_pcm_t *handle, snd_pcm_channel_setup_t *setup);
index 176450a..bdbb3ab 100644 (file)
@@ -84,6 +84,14 @@ int snd_pcm_info(snd_pcm_t *handle, snd_pcm_info_t *info)
        return handle->ops->info(handle->private, info);
 }
 
+int snd_pcm_params_info(snd_pcm_t *handle, snd_pcm_params_info_t *info)
+{
+       assert(handle && info);
+       /* Here we pass private and not op_arg.
+          FIXME: find a better solution */
+       return handle->ops->params_info(handle->private, info);
+}
+
 int snd_pcm_setup(snd_pcm_t *handle, snd_pcm_setup_t *setup)
 {
        int err;
index cdf8d2a..580d5d1 100644 (file)
@@ -324,84 +324,137 @@ static int preferred_formats[] = {
        SND_PCM_SFMT_U8
 };
 
-int snd_pcm_plug_slave_params(snd_pcm_params_t *params,
-                             snd_pcm_info_t *slave_info,
-                             snd_pcm_params_t *slave_params)
+int snd_pcm_plug_slave_format(int format, snd_pcm_params_info_t *slave_info)
 {
-       *slave_params = *params;
-       if ((slave_info->formats & (1 << params->format.format)) == 0) {
-               int format = params->format.format;
-               if ((snd_pcm_plug_formats(slave_info->formats) & (1 << format)) == 0)
-                       return -EINVAL;
-               if (snd_pcm_format_linear(format)) {
-                       int width = snd_pcm_format_width(format);
-                       int unsignd = snd_pcm_format_unsigned(format);
-                       int big = snd_pcm_format_big_endian(format);
-                       int format1;
-                       int wid, width1=width;
-                       int dwidth1 = 8;
-                       for (wid = 0; wid < 4; ++wid) {
-                               int end, big1 = big;
-                               for (end = 0; end < 2; ++end) {
-                                       int sgn, unsignd1 = unsignd;
-                                       for (sgn = 0; sgn < 2; ++sgn) {
-                                               format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
-                                               if (format1 >= 0 &&
-                                                   slave_info->formats & (1 << format1))
-                                                       goto _found;
-                                               unsignd1 = !unsignd1;
-                                       }
-                                       big1 = !big1;
-                               }
-                               if (width1 == 32) {
-                                       dwidth1 = -dwidth1;
-                                       width1 = width;
+       if ((snd_pcm_plug_formats(slave_info->formats) & (1 << format)) == 0)
+               return -EINVAL;
+       if (snd_pcm_format_linear(format)) {
+               int width = snd_pcm_format_width(format);
+               int unsignd = snd_pcm_format_unsigned(format);
+               int big = snd_pcm_format_big_endian(format);
+               int format1;
+               int wid, width1=width;
+               int dwidth1 = 8;
+               for (wid = 0; wid < 4; ++wid) {
+                       int end, big1 = big;
+                       for (end = 0; end < 2; ++end) {
+                               int sgn, unsignd1 = unsignd;
+                               for (sgn = 0; sgn < 2; ++sgn) {
+                                       format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
+                                       if (format1 >= 0 &&
+                                           slave_info->formats & (1 << format1))
+                                               goto _found;
+                                       unsignd1 = !unsignd1;
                                }
-                               width1 += dwidth1;
+                               big1 = !big1;
                        }
-                       return -EINVAL;
-               _found:
-                       slave_params->format.format = format1;
-               } else {
-                       unsigned int i;
-                       switch (format) {
-                       case SND_PCM_SFMT_MU_LAW:
+                       if (width1 == 32) {
+                               dwidth1 = -dwidth1;
+                               width1 = width;
+                       }
+                       width1 += dwidth1;
+               }
+               return -EINVAL;
+       _found:
+               return format1;
+       } else {
+               unsigned int i;
+               switch (format) {
+               case SND_PCM_SFMT_MU_LAW:
 #ifndef __KERNEL__
-                       case SND_PCM_SFMT_A_LAW:
-                       case SND_PCM_SFMT_IMA_ADPCM:
+               case SND_PCM_SFMT_A_LAW:
+               case SND_PCM_SFMT_IMA_ADPCM:
 #endif
-                               for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) {
-                                       int format1 = preferred_formats[i];
-                                       if (slave_info->formats & (1 << format1)) {
-                                               slave_params->format.format = format1;
-                                               break;
-                                       }
-                               }
-                               if (i == sizeof(preferred_formats)/sizeof(preferred_formats[0]))
-                                       return -EINVAL;
+                       for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) {
+                               int format1 = preferred_formats[i];
+                               if (slave_info->formats & (1 << format1))
+                                       return format1;
+                       }
+               default:
+                       return -EINVAL;
+               }
+       }
+}
+
+struct {
+       unsigned int rate;
+       unsigned int flag;
+} snd_pcm_rates[] = {
+       { 8000, SND_PCM_RATE_8000 },
+       { 11025, SND_PCM_RATE_11025 },
+       { 16000, SND_PCM_RATE_16000 },
+       { 22050, SND_PCM_RATE_22050 },
+       { 32000, SND_PCM_RATE_32000 },
+       { 44100, SND_PCM_RATE_44100 },
+       { 48000, SND_PCM_RATE_48000 },
+       { 88200, SND_PCM_RATE_88200 },
+       { 96000, SND_PCM_RATE_96000 },
+       { 176400, SND_PCM_RATE_176400 },
+       { 192000, SND_PCM_RATE_192000 }
+};
+
+int snd_pcm_plug_slave_rate(unsigned int rate, snd_pcm_params_info_t *slave_info)
+{
+        if (rate <= slave_info->min_rate)
+               return slave_info->min_rate;
+       else if (rate >= slave_info->max_rate)
+               return slave_info->max_rate;
+       else if (!(slave_info->rates & (SND_PCM_RATE_CONTINUOUS |
+                                              SND_PCM_RATE_KNOT))) {
+               unsigned int k;
+               unsigned int rate1 = 0, rate2 = 0;
+               int delta1, delta2;
+               for (k = 0; k < sizeof(snd_pcm_rates) / 
+                            sizeof(snd_pcm_rates[0]); ++k) {
+                       if (!(snd_pcm_rates[k].flag & slave_info->rates))
+                               continue;
+                       if (snd_pcm_rates[k].rate < rate) {
+                               rate1 = snd_pcm_rates[k].rate;
+                       } else if (snd_pcm_rates[k].rate > rate) {
+                               rate2 = snd_pcm_rates[k].rate;
                                break;
-                       default:
-                               return -EINVAL;
                        }
                }
+               if (rate1 == 0)
+                       return rate2;
+               if (rate2 == 0)
+                       return rate1;
+               delta1 = rate - rate1;
+               delta2 = rate2 - rate;
+               if (delta1 < delta2)
+                       return rate1;
+               else
+                       return rate2;
+       }
+       return rate;
+}
+                  
+int snd_pcm_plug_slave_params(snd_pcm_params_t *params,
+                             snd_pcm_info_t *slave_info,
+                             snd_pcm_params_info_t *slave_params_info,
+                             snd_pcm_params_t *slave_params)
+{
+       int slave_rate;
+       *slave_params = *params;
+       if ((slave_params_info->formats & (1 << params->format.format)) == 0) {
+               int slave_format = snd_pcm_plug_slave_format(params->format.format, slave_params_info);
+               if (slave_format < 0)
+                       return slave_format;
+               slave_params->format.format = slave_format;
        }
 
        /* channels */
-       if (params->format.channels < slave_info->min_channels ||
-           params->format.channels > slave_info->max_channels) {
-               unsigned int dst_channels = params->format.channels < slave_info->min_channels ?
-                                slave_info->min_channels : slave_info->max_channels;
-               slave_params->format.channels = dst_channels;
-       }
+       if (params->format.channels < slave_params_info->min_channels)
+               slave_params->format.channels = slave_params_info->min_channels;
+       else if (params->format.channels > slave_params_info->max_channels)
+               slave_params->format.channels = slave_params_info->max_channels;
 
        /* rate */
-        if (params->format.rate < slave_info->min_rate ||
-            params->format.rate > slave_info->max_rate) {
-               unsigned int dst_rate = params->format.rate < slave_info->min_rate ?
-                              slave_info->min_rate : slave_info->max_rate;
-               slave_params->format.rate = dst_rate;
-       }
-
+       slave_rate = snd_pcm_plug_slave_rate(params->format.rate, slave_params_info);
+       if (slave_rate < 0)
+               return slave_rate;
+       slave_params->format.rate = slave_rate;
+                  
        /* interleave */
        if (!(slave_info->flags & SND_PCM_INFO_INTERLEAVE))
                slave_params->format.interleave = 0;
index 6ea3483..57ef23f 100644 (file)
@@ -75,6 +75,15 @@ static int snd_pcm_hw_info(void *private, snd_pcm_info_t * info)
        return 0;
 }
 
+static int snd_pcm_hw_params_info(void *private, snd_pcm_params_info_t * info)
+{
+       snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
+       int fd = hw->fd;
+       if (ioctl(fd, SND_PCM_IOCTL_PARAMS_INFO, info) < 0)
+               return -errno;
+       return 0;
+}
+
 static int snd_pcm_hw_params(void *private, snd_pcm_params_t * params)
 {
        snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
@@ -330,6 +339,7 @@ struct snd_pcm_ops snd_pcm_hw_ops = {
        close: snd_pcm_hw_close,
        nonblock: snd_pcm_hw_nonblock,
        info: snd_pcm_hw_info,
+       params_info: snd_pcm_hw_params_info,
        params: snd_pcm_hw_params,
        setup: snd_pcm_hw_setup,
        channel_setup: snd_pcm_hw_channel_setup,
index 5055d6d..11734af 100644 (file)
@@ -26,6 +26,7 @@ struct snd_pcm_ops {
        int (*close)(void *private);
        int (*nonblock)(void *private, int nonblock);
        int (*info)(void *private, snd_pcm_info_t *info);
+       int (*params_info)(void *private, snd_pcm_params_info_t *info);
        int (*params)(void *private, snd_pcm_params_t *params);
        int (*setup)(void *private, snd_pcm_setup_t *setup);
        int (*channel_setup)(void *private, snd_pcm_channel_setup_t *setup);
@@ -82,8 +83,11 @@ typedef struct snd_pcm_plug {
 } snd_pcm_plug_t;
 
 unsigned int snd_pcm_plug_formats(unsigned int formats);
+int snd_pcm_plug_slave_format(int format, snd_pcm_params_info_t *slave_info);
+int snd_pcm_plug_slave_rate(unsigned int rate, snd_pcm_params_info_t *slave_info);
 int snd_pcm_plug_slave_params(snd_pcm_params_t *params,
                              snd_pcm_info_t *slave_info,
+                             snd_pcm_params_info_t *slave_params_info,
                              snd_pcm_params_t *slave_params);
 int snd_pcm_plug_format(snd_pcm_plug_t *plug,
                        snd_pcm_params_t *params,
index bdac8a6..afffd1c 100644 (file)
@@ -516,9 +516,9 @@ int snd_pcm_mmap_data(snd_pcm_t *handle, void **data)
                return 0;
        }
 
-       if (handle->setup.mmap_size == 0)
+       if (handle->setup.mmap_bytes == 0)
                return -ENXIO;
-       if ((err = handle->ops->mmap_data(handle->op_arg, (void**)&handle->mmap_data, handle->setup.mmap_size)) < 0)
+       if ((err = handle->ops->mmap_data(handle->op_arg, (void**)&handle->mmap_data, handle->setup.mmap_bytes)) < 0)
                return err;
        if (data) 
                *data = handle->mmap_data;
@@ -575,7 +575,7 @@ int snd_pcm_munmap_data(snd_pcm_t *handle)
        int err;
        assert(handle);
        assert(handle->mmap_data);
-       if ((err = handle->ops->munmap_data(handle->op_arg, handle->mmap_data, handle->setup.mmap_size)) < 0)
+       if ((err = handle->ops->munmap_data(handle->op_arg, handle->mmap_data, handle->setup.mmap_bytes)) < 0)
                return err;
        free(handle->channels);
        handle->channels = 0;
index 6b95a09..318dd0b 100644 (file)
@@ -90,29 +90,40 @@ static int snd_pcm_multi_nonblock(void *private, int nonblock)
 static int snd_pcm_multi_info(void *private, snd_pcm_info_t *info)
 {
        snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private;
-       unsigned int i, channels;
+       unsigned int i;
        int err;
        snd_pcm_t *handle_0 = multi->slaves[0].handle;
-       unsigned int channels0 = multi->slaves[0].channels_total;
        err = snd_pcm_info(handle_0, info);
        if (err < 0)
                return err;
-       info->buffer_size /= channels0;
-       info->min_fragment_size /= channels0;
-       info->max_fragment_size /= channels0;
-       info->fragment_align /= channels0;
        for (i = 1; i < multi->slaves_count; ++i) {
                snd_pcm_t *handle_i = multi->slaves[i].handle;
                snd_pcm_info_t info_i;
                err = snd_pcm_info(handle_i, &info_i);
                if (err < 0)
                        return err;
-               channels0 = multi->slaves[i].channels_total;
-               info_i.buffer_size /= channels0;
-               info_i.min_fragment_size /= channels0;
-               info_i.max_fragment_size /= channels0;
-               info_i.fragment_align /= channels0;
                info->flags &= info_i.flags;
+       }
+       if (multi->one_to_many)
+               info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
+       return 0;
+}
+
+static int snd_pcm_multi_params_info(void *private, snd_pcm_params_info_t *info)
+{
+       snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private;
+       unsigned int i;
+       int err;
+       snd_pcm_t *handle_0 = multi->slaves[0].handle;
+       err = snd_pcm_params_info(handle_0, info);
+       if (err < 0)
+               return err;
+       for (i = 1; i < multi->slaves_count; ++i) {
+               snd_pcm_t *handle_i = multi->slaves[i].handle;
+               snd_pcm_params_info_t info_i;
+               err = snd_pcm_params_info(handle_i, &info_i);
+               if (err < 0)
+                       return err;
                info->formats &= info_i.formats;
                info->rates &= info_i.rates;
                if (info_i.min_rate > info->min_rate)
@@ -130,15 +141,6 @@ static int snd_pcm_multi_info(void *private, snd_pcm_info_t *info)
                if (info_i.max_fragments < info->max_fragments)
                        info->max_fragments = info_i.max_fragments;
        }
-       channels = multi->channels_count;
-       info->buffer_size *= channels;
-       info->min_fragment_size *= channels;
-       info->max_fragment_size *= channels;
-       info->fragment_align *= channels;
-       info->min_channels = multi->channels_count;
-       info->max_channels = multi->channels_count;
-       if (multi->one_to_many)
-               info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
        return 0;
 }
 
@@ -239,7 +241,7 @@ static int snd_pcm_multi_setup(void *private, snd_pcm_setup_t *setup)
                }
        }
        /* Loaded with a value != 0 if mmap is feasible */
-       setup->mmap_size = !multi->one_to_many;
+       setup->mmap_bytes = !multi->one_to_many;
        return 0;
 }
 
@@ -592,6 +594,7 @@ struct snd_pcm_ops snd_pcm_multi_ops = {
        close: snd_pcm_multi_close,
        nonblock: snd_pcm_multi_nonblock,
        info: snd_pcm_multi_info,
+       params_info: snd_pcm_multi_params_info,
        params: snd_pcm_multi_params,
        setup: snd_pcm_multi_setup,
        channel_setup: snd_pcm_multi_channel_setup,
index 8605402..8a2bef0 100644 (file)
@@ -123,23 +123,95 @@ static int snd_pcm_plug_info(void *private, snd_pcm_info_t *info)
        
        if ((err = snd_pcm_info(plug->slave, info)) < 0)
                return err;
-       info->formats = snd_pcm_plug_formats(info->formats);
-       info->min_rate = 4000;
-       info->max_rate = 192000;
-       info->min_channels = 1;
-       info->max_channels = 32;
-       info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000;
+       info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
        info->flags |= SND_PCM_INFO_INTERLEAVE | SND_PCM_INFO_NONINTERLEAVE;
+       return 0;
+}
 
-       if (plug->slave->valid_setup) {
-               info->buffer_size = snd_pcm_plug_client_size(plug, info->buffer_size);
-               info->min_fragment_size = snd_pcm_plug_client_size(plug, info->min_fragment_size);
-               info->max_fragment_size = snd_pcm_plug_client_size(plug, info->max_fragment_size);
-               info->fragment_align = snd_pcm_plug_client_size(plug, info->fragment_align);
-               info->fifo_size = snd_pcm_plug_client_size(plug, info->fifo_size);
-               info->transfer_block_size = snd_pcm_plug_client_size(plug, info->transfer_block_size);
+static int snd_pcm_plug_params_info(void *private, snd_pcm_params_info_t *info)
+{
+       int err;
+       snd_pcm_plug_t *plug = (snd_pcm_plug_t*) private;
+       snd_pcm_params_info_t slave_info;
+       int rate;
+       int slave_format, slave_rate;
+       unsigned int slave_channels;
+
+       memset(&info->formats, 0, (char*)(info + 1) - (char*) &info->formats);
+       info->req.fail_reason = 0;
+       info->req.fail_mask = 0;
+
+       if (info->req_mask & SND_PCM_PARAMS_RATE) {
+               info->min_rate = info->req.format.rate;
+               info->max_rate = info->req.format.rate;
+       } else {
+               info->min_rate = 4000;
+               info->max_rate = 192000;
        }
-       info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
+       /* ??? */
+       info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000;
+       if (info->req_mask & SND_PCM_PARAMS_CHANNELS) {
+               info->min_channels = info->req.format.channels;
+               info->max_channels = info->req.format.channels;
+       } else {
+               info->min_channels = 1;
+               info->max_channels = 32;
+       }
+
+       memset(&slave_info, 0, sizeof(slave_info));
+       if ((err = snd_pcm_params_info(plug->slave, &slave_info)) < 0)
+               return err;
+
+       if (info->req_mask & SND_PCM_PARAMS_FORMAT) 
+               info->formats = 1 << info->req.format.format;
+       else
+               info->formats = snd_pcm_plug_formats(slave_info.formats);
+
+       info->min_fragments = slave_info.min_fragments;
+       info->max_fragments = slave_info.max_fragments;
+       
+       if (!(info->req_mask & SND_PCM_PARAMS_FORMAT))
+               return 0;
+       slave_format = snd_pcm_plug_slave_format(info->req.format.format, &slave_info);
+       if (slave_format < 0) {
+               info->req.fail_mask = SND_PCM_PARAMS_FORMAT;
+               info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+               return -EINVAL;
+       }
+
+       if (!(info->req_mask & SND_PCM_PARAMS_RATE))
+               return 0;
+       slave_rate = snd_pcm_plug_slave_rate(info->req.format.rate, &slave_info);
+       if (slave_rate < 0) {
+               info->req.fail_mask = SND_PCM_PARAMS_RATE;
+               info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+               return -EINVAL;
+       }
+
+       if (!(info->req_mask & SND_PCM_PARAMS_CHANNELS))
+               return 0;
+       slave_channels = info->req.format.rate;
+       if (slave_channels < info->min_channels)
+               slave_channels = info->min_channels;
+       else if (slave_channels > info->max_channels)
+               slave_channels = info->max_channels;
+
+       slave_info.req_mask = (SND_PCM_PARAMS_FORMAT |
+                              SND_PCM_PARAMS_CHANNELS |
+                              SND_PCM_PARAMS_RATE);
+       slave_info.req.format.format = info->req.format.format;
+       slave_info.req.format.channels = info->req.format.channels;
+       slave_info.req.format.rate = info->req.format.rate;
+       if ((err = snd_pcm_params_info(plug->slave, &slave_info)) < 0) {
+               info->req.fail_mask = slave_info.req.fail_mask;
+               info->req.fail_reason = slave_info.req.fail_reason;
+               return err;
+       }
+       rate = info->req.format.rate;
+       info->buffer_size = slave_info.buffer_size * rate / slave_rate;
+       info->min_fragment_size = slave_info.min_fragment_size * rate / slave_rate;
+       info->max_fragment_size = slave_info.max_fragment_size * rate / slave_rate;
+       info->fragment_align = slave_info.fragment_align * rate / slave_rate;
        return 0;
 }
 
@@ -176,7 +248,7 @@ static int snd_pcm_plug_setup(void *private, snd_pcm_setup_t *setup)
        setup->frames_align = snd_pcm_plug_client_size(plug, setup->frames_align);
        setup->frames_xrun_max = snd_pcm_plug_client_size(plug, setup->frames_xrun_max);
        setup->frames_fill_max = snd_pcm_plug_client_size(plug, setup->frames_fill_max);
-       setup->mmap_size = 0;
+       setup->mmap_bytes = 0;
        if (plug->handle->stream == SND_PCM_STREAM_PLAYBACK)
                setup->format = plug->first->src_format;
        else
@@ -510,6 +582,7 @@ struct snd_pcm_ops snd_pcm_plug_ops = {
        close: snd_pcm_plug_close,
        nonblock: snd_pcm_plug_nonblock,
        info: snd_pcm_plug_info,
+       params_info: snd_pcm_plug_params_info,
        params: snd_pcm_plug_params,
        setup: snd_pcm_plug_setup,
        channel_setup: snd_pcm_plug_channel_setup,
@@ -541,6 +614,7 @@ static int snd_pcm_plug_params(void *private, snd_pcm_params_t *params)
 {
        snd_pcm_params_t slave_params, params1;
        snd_pcm_info_t slave_info;
+       snd_pcm_params_info_t slave_params_info;
        snd_pcm_plugin_t *plugin;
        snd_pcm_plug_t *plug;
        int err;
@@ -552,13 +626,17 @@ static int snd_pcm_plug_params(void *private, snd_pcm_params_t *params)
          */
 
        memset(&slave_info, 0, sizeof(slave_info));
-       slave_info.stream = plug->slave->stream;
        if ((err = snd_pcm_info(plug->slave, &slave_info)) < 0) {
                snd_pcm_plug_clear(plug);
                return err;
        }
+       memset(&slave_params_info, 0, sizeof(slave_params_info));
+       if ((err = snd_pcm_params_info(plug->slave, &slave_params_info)) < 0) {
+               snd_pcm_plug_clear(plug);
+               return err;
+       }
 
-       if ((err = snd_pcm_plug_slave_params(params, &slave_info, &slave_params)) < 0)
+       if ((err = snd_pcm_plug_slave_params(params, &slave_info, &slave_params_info, &slave_params)) < 0)
                return err;
 
 
@@ -577,6 +655,7 @@ static int snd_pcm_plug_params(void *private, snd_pcm_params_t *params)
                plug->handle->ops->params = snd_pcm_plug_params;
                plug->handle->ops->setup = snd_pcm_plug_setup;
                plug->handle->ops->info = snd_pcm_plug_info;
+               plug->handle->ops->params_info = snd_pcm_plug_params_info;
                plug->handle->op_arg = plug->slave->op_arg;
                return 0;
        } else {
@@ -625,8 +704,11 @@ static int snd_pcm_plug_params(void *private, snd_pcm_params_t *params)
 
        pdprintf("params requested params: format = %i, rate = %i, channels = %i\n", slave_params.format.format, slave_params.format.rate, slave_params.format.channels);
        err = snd_pcm_params(plug->slave, &slave_params);
-       if (err < 0)
+       if (err < 0) {
+               params->fail_mask = slave_params.fail_mask;
+               params->fail_reason = slave_params.fail_reason;
                return err;
+       }
 
        err = snd_pcm_plug_action(plug, INIT, 0);
        if (err < 0)
@@ -656,6 +738,7 @@ int snd_pcm_plug_create(snd_pcm_t **handlep, snd_pcm_t *slave, int close_slave)
        handle->ops->params = snd_pcm_plug_params;
        handle->ops->setup = snd_pcm_plug_setup;
        handle->ops->info = snd_pcm_plug_info;
+       handle->ops->params_info = snd_pcm_plug_params_info;
        handle->op_arg = slave->op_arg;
        handle->mode = slave->mode;
        handle->private = plug;