From: Abramo Bagnara Date: Sat, 1 Jul 2000 10:38:29 +0000 (+0000) Subject: Moved some info fields to proper place (setup). Renamed mmap_size to mmap_bytes.... X-Git-Tag: v1.0.3~1213 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=cc79e0c6ea9b78b65110441c496fe57e1b160c28;p=alsa-lib.git Moved some info fields to proper place (setup). Renamed mmap_size to mmap_bytes. Added detailed fail report to params_t. Added params_info to API. --- diff --git a/include/pcm.h b/include/pcm.h index 0a4a0b36..9369f39e 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -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); diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 176450a5..bdbb3ab3 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -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; diff --git a/src/pcm/pcm_common.c b/src/pcm/pcm_common.c index cdf8d2ac..580d5d1c 100644 --- a/src/pcm/pcm_common.c +++ b/src/pcm/pcm_common.c @@ -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; diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 6ea34830..57ef23fc 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -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, diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 5055d6d1..11734afa 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -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, diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c index bdac8a6b..afffd1cc 100644 --- a/src/pcm/pcm_mmap.c +++ b/src/pcm/pcm_mmap.c @@ -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; diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index 6b95a093..318dd0b5 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -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, diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index 8605402b..8a2bef01 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -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;