OSDN Git Service

Introduce bswap.h for portable definitions of byte swap macros.
[android-x86/external-alsa-lib.git] / src / pcm / pcm_alaw.c
index de8f944..fa58441 100644 (file)
@@ -1,44 +1,61 @@
+/**
+ * \file pcm/pcm_alaw.c
+ * \ingroup PCM_Plugins
+ * \brief PCM A-Law Conversion Plugin Interface
+ * \author Abramo Bagnara <abramo@alsa-project.org>
+ * \date 2000-2001
+ */
 /*
  *  PCM - A-Law conversion
  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
  *
  *
  *   This library is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU Library General Public License as
- *   published by the Free Software Foundation; either version 2 of
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 of
  *   the License, or (at your option) any later version.
  *
  *   This program is distributed in the hope that it will be useful,
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU Library General Public License for more details.
+ *   GNU Lesser General Public License for more details.
  *
- *   You should have received a copy of the GNU Library General Public
+ *   You should have received a copy of the GNU Lesser General Public
  *   License along with this library; if not, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  */
   
-#include <byteswap.h>
+#include "bswap.h"
 #include "pcm_local.h"
 #include "pcm_plugin.h"
 
-typedef void (*alaw_f)(snd_pcm_channel_area_t *src_areas,
-                       size_t src_offset,
-                       snd_pcm_channel_area_t *dst_areas,
-                       size_t dst_offset,
-                       size_t frames, size_t channels, int getputidx);
+#include "plugin_ops.h"
+
+#ifndef PIC
+/* entry for static linking */
+const char *_snd_module_pcm_alaw = "";
+#endif
+
+#ifndef DOC_HIDDEN
+
+typedef void (*alaw_f)(const snd_pcm_channel_area_t *dst_areas,
+                      snd_pcm_uframes_t dst_offset,
+                      const snd_pcm_channel_area_t *src_areas,
+                      snd_pcm_uframes_t src_offset,
+                      unsigned int channels, snd_pcm_uframes_t frames,
+                      unsigned int getputidx);
 
 typedef struct {
        /* This field need to be the first */
        snd_pcm_plugin_t plug;
-       int getput_idx;
+       unsigned int getput_idx;
        alaw_f func;
-       int sformat;
-       int cformat;
-       int cxfer_mode, cmmap_shape;
+       snd_pcm_format_t sformat;
 } snd_pcm_alaw_t;
 
+#endif
+
 static inline int val_seg(int val)
 {
        int r = 1;
@@ -122,33 +139,27 @@ static int alaw_to_s16(unsigned char a_val)
        return ((a_val & 0x80) ? t : -t);
 }
 
-static void alaw_decode(snd_pcm_channel_area_t *src_areas,
-                       size_t src_offset,
-                       snd_pcm_channel_area_t *dst_areas,
-                       size_t dst_offset,
-                       size_t frames, size_t channels, int putidx)
+#ifndef DOC_HIDDEN
+
+void snd_pcm_alaw_decode(const snd_pcm_channel_area_t *dst_areas,
+                        snd_pcm_uframes_t dst_offset,
+                        const snd_pcm_channel_area_t *src_areas,
+                        snd_pcm_uframes_t src_offset,
+                        unsigned int channels, snd_pcm_uframes_t frames,
+                        unsigned int putidx)
 {
 #define PUT16_LABELS
 #include "plugin_ops.h"
 #undef PUT16_LABELS
        void *put = put16_labels[putidx];
-       size_t channel;
+       unsigned int channel;
        for (channel = 0; channel < channels; ++channel) {
-               char *src;
+               const unsigned char *src;
                char *dst;
                int src_step, dst_step;
-               size_t frames1;
-               snd_pcm_channel_area_t *src_area = &src_areas[channel];
-               snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
-#if 0
-               if (!src_area->enabled) {
-                       if (dst_area->wanted)
-                               snd_pcm_area_silence(&dst_areas[channel], dst_offset, frames, dst_sfmt);
-                       dst_area->enabled = 0;
-                       continue;
-               }
-               dst_area->enabled = 1;
-#endif
+               snd_pcm_uframes_t frames1;
+               const snd_pcm_channel_area_t *src_area = &src_areas[channel];
+               const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
                src = snd_pcm_channel_area_addr(src_area, src_offset);
                dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
                src_step = snd_pcm_channel_area_step(src_area);
@@ -167,34 +178,26 @@ static void alaw_decode(snd_pcm_channel_area_t *src_areas,
        }
 }
 
-static void alaw_encode(snd_pcm_channel_area_t *src_areas,
-                        size_t src_offset,
-                        snd_pcm_channel_area_t *dst_areas,
-                        size_t dst_offset,
-                        size_t frames, size_t channels, int getidx)
+void snd_pcm_alaw_encode(const snd_pcm_channel_area_t *dst_areas,
+                        snd_pcm_uframes_t dst_offset,
+                        const snd_pcm_channel_area_t *src_areas,
+                        snd_pcm_uframes_t src_offset,
+                        unsigned int channels, snd_pcm_uframes_t frames,
+                        unsigned int getidx)
 {
 #define GET16_LABELS
 #include "plugin_ops.h"
 #undef GET16_LABELS
        void *get = get16_labels[getidx];
-       size_t channel;
+       unsigned int channel;
        int16_t sample = 0;
        for (channel = 0; channel < channels; ++channel) {
-               char *src;
+               const char *src;
                char *dst;
                int src_step, dst_step;
-               size_t frames1;
-               snd_pcm_channel_area_t *src_area = &src_areas[channel];
-               snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
-#if 0
-               if (!src_area->enabled) {
-                       if (dst_area->wanted)
-                               snd_pcm_area_silence(&dst_area->area, 0, frames, dst_sfmt);
-                       dst_area->enabled = 0;
-                       continue;
-               }
-               dst_area->enabled = 1;
-#endif
+               snd_pcm_uframes_t frames1;
+               const snd_pcm_channel_area_t *src_area = &src_areas[channel];
+               const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
                src = snd_pcm_channel_area_addr(src_area, src_offset);
                dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
                src_step = snd_pcm_channel_area_step(src_area);
@@ -213,288 +216,334 @@ static void alaw_encode(snd_pcm_channel_area_t *src_areas,
        }
 }
 
-static int snd_pcm_alaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
+#endif /* DOC_HIDDEN */
+
+static int snd_pcm_alaw_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 {
-       snd_pcm_alaw_t *alaw = pcm->private;
-       unsigned int req_mask = info->req_mask;
-       unsigned int sfmt = info->req.format.sfmt;
+       snd_pcm_alaw_t *alaw = pcm->private_data;
        int err;
-       if (req_mask & SND_PCM_PARAMS_SFMT) {
-               if (alaw->sformat == SND_PCM_SFMT_A_LAW ?
-                   !snd_pcm_format_linear(sfmt) :
-                   sfmt != SND_PCM_SFMT_A_LAW) {
-                       info->req.fail_mask = SND_PCM_PARAMS_SFMT;
-                       info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
-                       return -EINVAL;
-               }
+       snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
+       err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
+                                        &access_mask);
+       if (err < 0)
+               return err;
+       if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
+               snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
+               err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
+                                                &format_mask);
+       } else {
+               err = _snd_pcm_hw_params_set_format(params, 
+                                                  SND_PCM_FORMAT_A_LAW);
        }
-       info->req_mask |= SND_PCM_PARAMS_SFMT;
-       info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | 
-                           SND_PCM_PARAMS_XFER_MODE);
-       info->req.format.sfmt = alaw->sformat;
-       err = snd_pcm_params_info(alaw->plug.slave, info);
-       info->req_mask = req_mask;
-       info->req.format.sfmt = sfmt;
        if (err < 0)
                return err;
-       if (req_mask & SND_PCM_PARAMS_SFMT)
-               info->formats = 1 << sfmt;
-       else
-               info->formats = alaw->sformat == SND_PCM_SFMT_A_LAW ?
-                       SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_A_LAW;
-       info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
-       info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
-       return err;
+       err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
+       if (err < 0)
+               return err;
+       params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
+       return 0;
 }
 
-static int snd_pcm_alaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
+static int snd_pcm_alaw_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
+{
+       snd_pcm_alaw_t *alaw = pcm->private_data;
+       snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
+       _snd_pcm_hw_params_any(sparams);
+       _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
+                                  &saccess_mask);
+       _snd_pcm_hw_params_set_format(sparams, alaw->sformat);
+       _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
+       return 0;
+}
+
+static int snd_pcm_alaw_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
+                                           snd_pcm_hw_params_t *sparams)
 {
-       snd_pcm_alaw_t *alaw = pcm->private;
-       snd_pcm_t *slave = alaw->plug.slave;
        int err;
-       if (alaw->sformat == SND_PCM_SFMT_A_LAW ?
-           !snd_pcm_format_linear(params->format.sfmt) :
-           params->format.sfmt != SND_PCM_SFMT_A_LAW) {
-               params->fail_mask = SND_PCM_PARAMS_SFMT;
-               params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
-               return -EINVAL;
-       }
-       alaw->cformat = params->format.sfmt;
-       alaw->cxfer_mode = params->xfer_mode;
-       alaw->cmmap_shape = params->mmap_shape;
-       params->format.sfmt = alaw->sformat;
-       params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
-       params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
-       err = snd_pcm_params_mmap(slave, params);
-       params->format.sfmt = alaw->cformat;
-       params->xfer_mode = alaw->cxfer_mode;
-       params->mmap_shape = alaw->cmmap_shape;
-       return err;
+       unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
+                             SND_PCM_HW_PARBIT_RATE |
+                             SND_PCM_HW_PARBIT_PERIOD_SIZE |
+                             SND_PCM_HW_PARBIT_BUFFER_SIZE |
+                             SND_PCM_HW_PARBIT_PERIODS |
+                             SND_PCM_HW_PARBIT_PERIOD_TIME |
+                             SND_PCM_HW_PARBIT_BUFFER_TIME |
+                             SND_PCM_HW_PARBIT_TICK_TIME);
+       err = _snd_pcm_hw_params_refine(sparams, links, params);
+       if (err < 0)
+               return err;
+       return 0;
+}
+       
+static int snd_pcm_alaw_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
+                                           snd_pcm_hw_params_t *sparams)
+{
+       int err;
+       unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
+                             SND_PCM_HW_PARBIT_RATE |
+                             SND_PCM_HW_PARBIT_PERIOD_SIZE |
+                             SND_PCM_HW_PARBIT_BUFFER_SIZE |
+                             SND_PCM_HW_PARBIT_PERIODS |
+                             SND_PCM_HW_PARBIT_PERIOD_TIME |
+                             SND_PCM_HW_PARBIT_BUFFER_TIME |
+                             SND_PCM_HW_PARBIT_TICK_TIME);
+       err = _snd_pcm_hw_params_refine(params, links, sparams);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int snd_pcm_alaw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+       return snd_pcm_hw_refine_slave(pcm, params,
+                                      snd_pcm_alaw_hw_refine_cprepare,
+                                      snd_pcm_alaw_hw_refine_cchange,
+                                      snd_pcm_alaw_hw_refine_sprepare,
+                                      snd_pcm_alaw_hw_refine_schange,
+                                      snd_pcm_generic_hw_refine);
 }
 
-static int snd_pcm_alaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
+static int snd_pcm_alaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
 {
-       snd_pcm_alaw_t *alaw = pcm->private;
-       int err = snd_pcm_setup(alaw->plug.slave, setup);
+       snd_pcm_alaw_t *alaw = pcm->private_data;
+       snd_pcm_format_t format;
+       int err = snd_pcm_hw_params_slave(pcm, params,
+                                         snd_pcm_alaw_hw_refine_cchange,
+                                         snd_pcm_alaw_hw_refine_sprepare,
+                                         snd_pcm_alaw_hw_refine_schange,
+                                         snd_pcm_generic_hw_params);
        if (err < 0)
                return err;
-       assert(alaw->sformat == setup->format.sfmt);
-       if (alaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
-               setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
-       else
-               setup->xfer_mode = alaw->cxfer_mode;
-       if (alaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
-               setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
-       else
-               setup->mmap_shape = alaw->cmmap_shape;
-       setup->format.sfmt = alaw->cformat;
-       setup->mmap_bytes = 0;
+
+       err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
+       if (err < 0)
+               return err;
+               
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
-               if (alaw->sformat == SND_PCM_SFMT_A_LAW) {
-                       alaw->getput_idx = get_index(alaw->cformat, SND_PCM_SFMT_S16);
-                       alaw->func = alaw_encode;
+               if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
+                       alaw->getput_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S16);
+                       alaw->func = snd_pcm_alaw_encode;
                } else {
-                       alaw->getput_idx = put_index(SND_PCM_SFMT_S16, alaw->sformat);
-                       alaw->func = alaw_decode;
+                       alaw->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, alaw->sformat);
+                       alaw->func = snd_pcm_alaw_decode;
                }
        } else {
-               if (alaw->sformat == SND_PCM_SFMT_A_LAW) {
-                       alaw->getput_idx = put_index(SND_PCM_SFMT_S16, alaw->cformat);
-                       alaw->func = alaw_decode;
+               if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
+                       alaw->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, format);
+                       alaw->func = snd_pcm_alaw_decode;
                } else {
-                       alaw->getput_idx = get_index(alaw->sformat, SND_PCM_SFMT_S16);
-                       alaw->func = alaw_encode;
+                       alaw->getput_idx = snd_pcm_linear_get_index(alaw->sformat, SND_PCM_FORMAT_S16);
+                       alaw->func = snd_pcm_alaw_encode;
                }
        }
        return 0;
 }
 
-static ssize_t snd_pcm_alaw_write_areas(snd_pcm_t *pcm,
-                                       snd_pcm_channel_area_t *areas,
-                                       size_t offset,
-                                       size_t size,
-                                       size_t *slave_sizep)
+static snd_pcm_uframes_t
+snd_pcm_alaw_write_areas(snd_pcm_t *pcm,
+                        const snd_pcm_channel_area_t *areas,
+                        snd_pcm_uframes_t offset,
+                        snd_pcm_uframes_t size,
+                        const snd_pcm_channel_area_t *slave_areas,
+                        snd_pcm_uframes_t slave_offset,
+                        snd_pcm_uframes_t *slave_sizep)
 {
-       snd_pcm_alaw_t *alaw = pcm->private;
-       snd_pcm_t *slave = alaw->plug.slave;
-       size_t xfer = 0;
-       ssize_t err = 0;
-       if (slave_sizep && *slave_sizep < size)
+       snd_pcm_alaw_t *alaw = pcm->private_data;
+       if (size > *slave_sizep)
                size = *slave_sizep;
-       assert(size > 0);
-       while (xfer < size) {
-               size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
-               alaw->func(areas, offset, 
-                           snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
-                           frames, pcm->setup.format.channels,
-                           alaw->getput_idx);
-               err = snd_pcm_mmap_forward(slave, frames);
-               if (err < 0)
-                       break;
-               assert((size_t)err == frames);
-               offset += err;
-               xfer += err;
-               snd_pcm_mmap_hw_forward(pcm, err);
-       }
-       if (xfer > 0) {
-               if (slave_sizep)
-                       *slave_sizep = xfer;
-               return xfer;
-       }
-       return err;
+       alaw->func(slave_areas, slave_offset,
+                  areas, offset, 
+                  pcm->channels, size,
+                  alaw->getput_idx);
+       *slave_sizep = size;
+       return size;
 }
 
-static ssize_t snd_pcm_alaw_read_areas(snd_pcm_t *pcm,
-                                      snd_pcm_channel_area_t *areas,
-                                      size_t offset,
-                                      size_t size,
-                                      size_t *slave_sizep)
+static snd_pcm_uframes_t
+snd_pcm_alaw_read_areas(snd_pcm_t *pcm,
+                       const snd_pcm_channel_area_t *areas,
+                       snd_pcm_uframes_t offset,
+                       snd_pcm_uframes_t size,
+                       const snd_pcm_channel_area_t *slave_areas,
+                       snd_pcm_uframes_t slave_offset,
+                       snd_pcm_uframes_t *slave_sizep)
 {
-       snd_pcm_alaw_t *alaw = pcm->private;
-       snd_pcm_t *slave = alaw->plug.slave;
-       size_t xfer = 0;
-       ssize_t err = 0;
-       if (slave_sizep && *slave_sizep < size)
+       snd_pcm_alaw_t *alaw = pcm->private_data;
+       if (size > *slave_sizep)
                size = *slave_sizep;
-       assert(size > 0);
-       while (xfer < size) {
-               size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
-               alaw->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
-                          areas, offset, 
-                          frames, pcm->setup.format.channels,
-                          alaw->getput_idx);
-               err = snd_pcm_mmap_forward(slave, frames);
-               if (err < 0)
-                       break;
-               assert((size_t)err == frames);
-               offset += err;
-               xfer += err;
-               snd_pcm_mmap_hw_forward(pcm, err);
-       }
-       if (xfer > 0) {
-               if (slave_sizep)
-                       *slave_sizep = xfer;
-               return xfer;
-       }
-       return err;
+       alaw->func(areas, offset, 
+                  slave_areas, slave_offset,
+                  pcm->channels, size,
+                  alaw->getput_idx);
+       *slave_sizep = size;
+       return size;
 }
 
-static void snd_pcm_alaw_dump(snd_pcm_t *pcm, FILE *fp)
+static void snd_pcm_alaw_dump(snd_pcm_t *pcm, snd_output_t *out)
 {
-       snd_pcm_alaw_t *alaw = pcm->private;
-       fprintf(fp, "A-Law conversion PCM (%s)\n", 
+       snd_pcm_alaw_t *alaw = pcm->private_data;
+       snd_output_printf(out, "A-Law conversion PCM (%s)\n", 
                snd_pcm_format_name(alaw->sformat));
-       if (pcm->valid_setup) {
-               fprintf(fp, "Its setup is:\n");
-               snd_pcm_dump_setup(pcm, fp);
+       if (pcm->setup) {
+               snd_output_printf(out, "Its setup is:\n");
+               snd_pcm_dump_setup(pcm, out);
        }
-       fprintf(fp, "Slave: ");
-       snd_pcm_dump(alaw->plug.slave, fp);
+       snd_output_printf(out, "Slave: ");
+       snd_pcm_dump(alaw->plug.gen.slave, out);
 }
 
-snd_pcm_ops_t snd_pcm_alaw_ops = {
-       close: snd_pcm_plugin_close,
-       info: snd_pcm_plugin_info,
-       params_info: snd_pcm_alaw_params_info,
-       params: snd_pcm_alaw_params,
-       setup: snd_pcm_alaw_setup,
-       channel_info: snd_pcm_plugin_channel_info,
-       channel_params: snd_pcm_plugin_channel_params,
-       channel_setup: snd_pcm_plugin_channel_setup,
-       dump: snd_pcm_alaw_dump,
-       nonblock: snd_pcm_plugin_nonblock,
-       async: snd_pcm_plugin_async,
-       mmap: snd_pcm_plugin_mmap,
-       munmap: snd_pcm_plugin_munmap,
+static const snd_pcm_ops_t snd_pcm_alaw_ops = {
+       .close = snd_pcm_generic_close,
+       .info = snd_pcm_generic_info,
+       .hw_refine = snd_pcm_alaw_hw_refine,
+       .hw_params = snd_pcm_alaw_hw_params,
+       .hw_free = snd_pcm_generic_hw_free,
+       .sw_params = snd_pcm_generic_sw_params,
+       .channel_info = snd_pcm_generic_channel_info,
+       .dump = snd_pcm_alaw_dump,
+       .nonblock = snd_pcm_generic_nonblock,
+       .async = snd_pcm_generic_async,
+       .mmap = snd_pcm_generic_mmap,
+       .munmap = snd_pcm_generic_munmap,
+       .query_chmaps = snd_pcm_generic_query_chmaps,
+       .get_chmap = snd_pcm_generic_get_chmap,
+       .set_chmap = snd_pcm_generic_set_chmap,
 };
 
-int snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name, int sformat, snd_pcm_t *slave, int close_slave)
+/**
+ * \brief Creates a new A-Law conversion PCM
+ * \param pcmp Returns created PCM handle
+ * \param name Name of PCM
+ * \param sformat Slave (destination) format
+ * \param slave Slave PCM handle
+ * \param close_slave When set, the slave PCM handle is closed with copy PCM
+ * \retval zero on success otherwise a negative error code
+ * \warning Using of this function might be dangerous in the sense
+ *          of compatibility reasons. The prototype might be freely
+ *          changed in future.
+ */           
+int snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
 {
        snd_pcm_t *pcm;
        snd_pcm_alaw_t *alaw;
+       int err;
        assert(pcmp && slave);
        if (snd_pcm_format_linear(sformat) != 1 &&
-           sformat != SND_PCM_SFMT_A_LAW)
+           sformat != SND_PCM_FORMAT_A_LAW)
                return -EINVAL;
        alaw = calloc(1, sizeof(snd_pcm_alaw_t));
        if (!alaw) {
                return -ENOMEM;
        }
+       snd_pcm_plugin_init(&alaw->plug);
        alaw->sformat = sformat;
        alaw->plug.read = snd_pcm_alaw_read_areas;
        alaw->plug.write = snd_pcm_alaw_write_areas;
-       alaw->plug.slave = slave;
-       alaw->plug.close_slave = close_slave;
+       alaw->plug.undo_read = snd_pcm_plugin_undo_read_generic;
+       alaw->plug.undo_write = snd_pcm_plugin_undo_write_generic;
+       alaw->plug.gen.slave = slave;
+       alaw->plug.gen.close_slave = close_slave;
 
-       pcm = calloc(1, sizeof(snd_pcm_t));
-       if (!pcm) {
+       err = snd_pcm_new(&pcm, SND_PCM_TYPE_ALAW, name, slave->stream, slave->mode);
+       if (err < 0) {
                free(alaw);
-               return -ENOMEM;
+               return err;
        }
-       if (name)
-               pcm->name = strdup(name);
-       pcm->type = SND_PCM_TYPE_ALAW;
-       pcm->stream = slave->stream;
-       pcm->mode = slave->mode;
        pcm->ops = &snd_pcm_alaw_ops;
-       pcm->op_arg = pcm;
        pcm->fast_ops = &snd_pcm_plugin_fast_ops;
-       pcm->fast_op_arg = pcm;
-       pcm->private = alaw;
+       pcm->private_data = alaw;
        pcm->poll_fd = slave->poll_fd;
-       pcm->hw_ptr = &alaw->plug.hw_ptr;
-       pcm->appl_ptr = &alaw->plug.appl_ptr;
+       pcm->poll_events = slave->poll_events;
+       pcm->tstamp_type = slave->tstamp_type;
+       snd_pcm_set_hw_ptr(pcm, &alaw->plug.hw_ptr, -1, 0);
+       snd_pcm_set_appl_ptr(pcm, &alaw->plug.appl_ptr, -1, 0);
        *pcmp = pcm;
 
        return 0;
 }
 
-int _snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name,
-                        snd_config_t *conf, 
-                        int stream, int mode)
+/*! \page pcm_plugins
+
+\section pcm_plugins_alaw Plugin: A-Law
+
+This plugin converts A-Law samples to linear or linear to A-Law samples
+from master A-Law conversion PCM to given slave PCM. The channel count,
+format and rate must match for both of them.
+
+\code
+pcm.name {
+        type alaw               # A-Law conversion PCM
+        slave STR               # Slave name
+        # or
+        slave {                 # Slave definition
+                pcm STR         # Slave PCM name
+                # or
+                pcm { }         # Slave PCM definition
+                format STR      # Slave format
+        }
+}
+\endcode
+
+\subsection pcm_plugins_alaw_funcref Function reference
+
+<UL>
+  <LI>snd_pcm_alaw_open()
+  <LI>_snd_pcm_alaw_open()
+</UL>
+
+*/
+
+/**
+ * \brief Creates a new A-Law conversion PCM
+ * \param pcmp Returns created PCM handle
+ * \param name Name of PCM
+ * \param root Root configuration node
+ * \param conf Configuration node with copy PCM description
+ * \param stream Stream type
+ * \param mode Stream mode
+ * \retval zero on success otherwise a negative error code
+ * \warning Using of this function might be dangerous in the sense
+ *          of compatibility reasons. The prototype might be freely
+ *          changed in future.
+ */
+int _snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name,
+                      snd_config_t *root, snd_config_t *conf, 
+                      snd_pcm_stream_t stream, int mode)
 {
-       snd_config_iterator_t i;
-       char *sname = NULL;
+       snd_config_iterator_t i, next;
        int err;
        snd_pcm_t *spcm;
-       int sformat = -1;
-       snd_config_foreach(i, conf) {
-               snd_config_t *n = snd_config_entry(i);
-               if (strcmp(n->id, "comment") == 0)
-                       continue;
-               if (strcmp(n->id, "type") == 0)
-                       continue;
-               if (strcmp(n->id, "stream") == 0)
+       snd_config_t *slave = NULL, *sconf;
+       snd_pcm_format_t sformat;
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id;
+               if (snd_config_get_id(n, &id) < 0)
                        continue;
-               if (strcmp(n->id, "sname") == 0) {
-                       err = snd_config_string_get(n, &sname);
-                       if (err < 0)
-                               return -EINVAL;
+               if (snd_pcm_conf_generic_id(id))
                        continue;
-               }
-               if (strcmp(n->id, "sformat") == 0) {
-                       char *f;
-                       err = snd_config_string_get(n, &f);
-                       if (err < 0)
-                               return -EINVAL;
-                       sformat = snd_pcm_format_value(f);
-                       if (sformat < 0)
-                               return -EINVAL;
-                       if (snd_pcm_format_linear(sformat) != 1 &&
-                           sformat != SND_PCM_SFMT_A_LAW)
-                               return -EINVAL;
+               if (strcmp(id, "slave") == 0) {
+                       slave = n;
                        continue;
                }
+               SNDERR("Unknown field %s", id);
                return -EINVAL;
        }
-       if (!sname || !sformat)
+       if (!slave) {
+               SNDERR("slave is not defined");
                return -EINVAL;
-       /* This is needed cause snd_config_update may destroy config */
-       sname = strdup(sname);
-       if (!sname)
-               return  -ENOMEM;
-       err = snd_pcm_open(&spcm, sname, stream, mode);
-       free(sname);
+       }
+       err = snd_pcm_slave_conf(root, slave, &sconf, 1,
+                                SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
+       if (err < 0)
+               return err;
+       if (snd_pcm_format_linear(sformat) != 1 &&
+           sformat != SND_PCM_FORMAT_A_LAW) {
+               snd_config_delete(sconf);
+               SNDERR("invalid slave format");
+               return -EINVAL;
+       }
+       err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
+       snd_config_delete(sconf);
        if (err < 0)
                return err;
        err = snd_pcm_alaw_open(pcmp, name, sformat, spcm, 1);
@@ -502,5 +551,6 @@ int _snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name,
                snd_pcm_close(spcm);
        return err;
 }
-                               
-
+#ifndef DOC_HIDDEN
+SND_DLSYM_BUILD_VERSION(_snd_pcm_alaw_open, SND_PCM_DLSYM_VERSION);
+#endif