OSDN Git Service

pcm: dsnoop: Added "hw_ptr_alignment" option in configuration for slave pointer alignment
[android-x86/external-alsa-lib.git] / src / pcm / pcm_dsnoop.c
index 6158cae..58b1e53 100644 (file)
@@ -1,13 +1,13 @@
 /**
- * \file pcm/pcm_snoop.c
+ * \file pcm/pcm_dsnoop.c
  * \ingroup PCM_Plugins
  * \brief PCM Capture Stream Snooping (dsnoop) Plugin Interface
- * \author Jaroslav Kysela <perex@suse.cz>
+ * \author Jaroslav Kysela <perex@perex.cz>
  * \date 2003
  */
 /*
  *  PCM - Capture Stream Snooping
- *  Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
  *
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -22,7 +22,7 @@
  *
  *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
   
@@ -33,6 +33,8 @@
 #include <signal.h>
 #include <string.h>
 #include <fcntl.h>
+#include <ctype.h>
+#include <grp.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/shm.h>
@@ -52,6 +54,23 @@ const char *_snd_module_pcm_dsnoop = "";
  *
  */
 
+static int snoop_timestamp(snd_pcm_t *pcm)
+{
+       snd_pcm_direct_t *dsnoop = pcm->private_data;
+       snd_pcm_uframes_t ptr1 = -2LL /* invalid value */, ptr2;
+
+       /* loop is required to sync hw.ptr with timestamp */
+       while (1) {
+               ptr2 = *dsnoop->spcm->hw.ptr;
+               if (ptr1 == ptr2)
+                       break;
+               ptr1 = ptr2;
+               dsnoop->update_tstamp = snd_pcm_hw_fast_tstamp(dsnoop->spcm);
+       }
+       dsnoop->slave_hw_ptr = ptr1;
+       return 0;
+}
+
 static void snoop_areas(snd_pcm_direct_t *dsnoop,
                        const snd_pcm_channel_area_t *src_areas,
                        const snd_pcm_channel_area_t *dst_areas,
@@ -91,14 +110,15 @@ static void snd_pcm_dsnoop_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_
        dst_areas = snd_pcm_mmap_areas(pcm);
        src_areas = snd_pcm_mmap_areas(dsnoop->spcm);
        hw_ptr %= pcm->buffer_size;
-       slave_hw_ptr %= dsnoop->shmptr->s.buffer_size;
+       slave_hw_ptr %= dsnoop->slave_buffer_size;
        while (size > 0) {
                transfer = hw_ptr + size > pcm->buffer_size ? pcm->buffer_size - hw_ptr : size;
-               transfer = slave_hw_ptr + transfer > dsnoop->shmptr->s.buffer_size ? dsnoop->shmptr->s.buffer_size - slave_hw_ptr : transfer;
+               transfer = slave_hw_ptr + transfer > dsnoop->slave_buffer_size ?
+                       dsnoop->slave_buffer_size - slave_hw_ptr : transfer;
                size -= transfer;
                snoop_areas(dsnoop, src_areas, dst_areas, slave_hw_ptr, hw_ptr, transfer);
                slave_hw_ptr += transfer;
-               slave_hw_ptr %= dsnoop->shmptr->s.buffer_size;
+               slave_hw_ptr %= dsnoop->slave_buffer_size;
                hw_ptr += transfer;
                hw_ptr %= pcm->buffer_size;
        }
@@ -107,19 +127,36 @@ static void snd_pcm_dsnoop_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_
 /*
  *  synchronize hardware pointer (hw_ptr) with ours
  */
-static snd_pcm_sframes_t snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
+static int snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
 {
        snd_pcm_direct_t *dsnoop = pcm->private_data;
        snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
        snd_pcm_sframes_t diff;
-       
+       int err;
+
+       switch (snd_pcm_state(dsnoop->spcm)) {
+       case SND_PCM_STATE_DISCONNECTED:
+               dsnoop->state = SNDRV_PCM_STATE_DISCONNECTED;
+               return -ENODEV;
+       case SND_PCM_STATE_XRUN:
+               if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
+                       return err;
+               break;
+       default:
+               break;
+       }
+       if (snd_pcm_direct_client_chk_xrun(dsnoop, pcm))
+               return -EPIPE;
+       if (dsnoop->slowptr)
+               snd_pcm_hwsync(dsnoop->spcm);
        old_slave_hw_ptr = dsnoop->slave_hw_ptr;
-       slave_hw_ptr = dsnoop->slave_hw_ptr = *dsnoop->spcm->hw.ptr;
+       snoop_timestamp(pcm);
+       slave_hw_ptr = dsnoop->slave_hw_ptr;
        diff = slave_hw_ptr - old_slave_hw_ptr;
        if (diff == 0)          /* fast path */
                return 0;
        if (diff < 0) {
-               slave_hw_ptr += dsnoop->shmptr->s.boundary;
+               slave_hw_ptr += dsnoop->slave_boundary;
                diff = slave_hw_ptr - old_slave_hw_ptr;
        }
        snd_pcm_dsnoop_sync_area(pcm, old_slave_hw_ptr, diff);
@@ -129,198 +166,24 @@ static snd_pcm_sframes_t snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
        if (pcm->stop_threshold >= pcm->boundary)       /* don't care */
                return 0;
        if ((avail = snd_pcm_mmap_capture_hw_avail(pcm)) >= pcm->stop_threshold) {
-               struct timeval tv;
-               gettimeofday(&tv, 0);
-               dsnoop->trigger_tstamp.tv_sec = tv.tv_sec;
-               dsnoop->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
+               gettimestamp(&dsnoop->trigger_tstamp, pcm->tstamp_type);
                dsnoop->state = SND_PCM_STATE_XRUN;
                dsnoop->avail_max = avail;
                return -EPIPE;
        }
        if (avail > dsnoop->avail_max)
                dsnoop->avail_max = avail;
-       return diff;
+       return 0;
 }
 
 /*
  *  plugin implementation
  */
 
-static int snd_pcm_dsnoop_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
-{
-       /* value is cached for us in pcm->mode (SND_PCM_NONBLOCK flag) */
-       return 0;
-}
-
-static int snd_pcm_dsnoop_async(snd_pcm_t *pcm, int sig, pid_t pid)
-{
-       snd_pcm_direct_t *dsnoop = pcm->private_data;
-       return snd_timer_async(dsnoop->timer, sig, pid);
-}
-
-static int snd_pcm_dsnoop_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
-{
-       snd_pcm_direct_t *dsnoop = pcm->private_data;
-       unsigned short events;
-       static snd_timer_read_t rbuf[5];        /* can be overwriten by multiple plugins, we don't need the value */
-
-       assert(pfds && nfds == 1 && revents);
-       events = pfds[0].revents;
-       if (events & POLLIN) {
-               events |= POLLOUT;
-               events &= ~POLLIN;
-               /* empty the timer read queue */
-               while (snd_timer_read(dsnoop->timer, &rbuf, sizeof(rbuf)) == sizeof(rbuf)) ;
-       }
-       *revents = events;
-       return 0;
-}
-
-static int snd_pcm_dsnoop_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
-{
-       // snd_pcm_direct_t *dsnoop = pcm->private_data;
-
-       memset(info, 0, sizeof(*info));
-       info->stream = pcm->stream;
-       info->card = -1;
-       /* FIXME: fill this with something more useful: we know the hardware name */
-       strncpy(info->id, pcm->name, sizeof(info->id));
-       strncpy(info->name, pcm->name, sizeof(info->name));
-       strncpy(info->subname, pcm->name, sizeof(info->subname));
-       info->subdevices_count = 1;
-       return 0;
-}
-
-static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params,
-                                       snd_pcm_hw_param_t var)
-{
-       return &params->masks[var - SND_PCM_HW_PARAM_FIRST_MASK];
-}
-
-static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
-                                               snd_pcm_hw_param_t var)
-{
-       return &params->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL];
-}
-
-static int hw_param_interval_refine_one(snd_pcm_hw_params_t *params,
-                                       snd_pcm_hw_param_t var,
-                                       snd_pcm_hw_params_t *src)
-{
-       snd_interval_t *i;
-
-       if (!(params->rmask & (1<<var)))        /* nothing to do? */
-               return 0;
-       i = hw_param_interval(params, var);
-       if (snd_interval_empty(i)) {
-               SNDERR("dsnoop interval %i empty?", (int)var);
-               return -EINVAL;
-       }
-       if (snd_interval_refine(i, hw_param_interval(src, var)))
-               params->cmask |= 1<<var;
-       return 0;
-}
-
-#undef REFINE_DEBUG
-
-static int snd_pcm_dsnoop_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
-{
-       snd_pcm_direct_t *dsnoop = pcm->private_data;
-       snd_pcm_hw_params_t *hw_params = &dsnoop->shmptr->hw_params;
-       static snd_mask_t access = { .bits = { 
-                                       (1<<SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) |
-                                       (1<<SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) |
-                                       (1<<SNDRV_PCM_ACCESS_RW_INTERLEAVED) |
-                                       (1<<SNDRV_PCM_ACCESS_RW_NONINTERLEAVED),
-                                       0, 0, 0 } };
-       int err;
-
-#ifdef REFINE_DEBUG
-       snd_output_t *log;
-       snd_output_stdio_attach(&log, stderr, 0);
-       snd_output_puts(log, "DMIX REFINE (begin):\n");
-       snd_pcm_hw_params_dump(params, log);
-#endif
-       if (params->rmask & (1<<SND_PCM_HW_PARAM_ACCESS)) {
-               if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS))) {
-                       SNDERR("dsnoop access mask empty?");
-                       return -EINVAL;
-               }
-               if (snd_mask_refine(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS), &access))
-                       params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
-       }
-       if (params->rmask & (1<<SND_PCM_HW_PARAM_FORMAT)) {
-               if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT))) {
-                       SNDERR("dsnoop format mask empty?");
-                       return -EINVAL;
-               }
-               if (snd_mask_refine_set(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT),
-                                       snd_mask_value(hw_param_mask(hw_params, SND_PCM_HW_PARAM_FORMAT))))
-                       params->cmask |= 1<<SND_PCM_HW_PARAM_FORMAT;
-       }
-       //snd_mask_none(hw_param_mask(params, SND_PCM_HW_PARAM_SUBFORMAT));
-       if (params->rmask & (1<<SND_PCM_HW_PARAM_CHANNELS)) {
-               if (snd_interval_empty(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS))) {
-                       SNDERR("dsnoop channels mask empty?");
-                       return -EINVAL;
-               }
-               err = snd_interval_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS), dsnoop->channels);
-               if (err < 0)
-                       return err;
-       }
-       err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_RATE, hw_params);
-       if (err < 0)
-               return err;
-       err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE, hw_params);
-       if (err < 0)
-               return err;
-       err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_TIME, hw_params);
-       if (err < 0)
-               return err;
-       err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE, hw_params);
-       if (err < 0)
-               return err;
-       err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME, hw_params);
-       if (err < 0)
-               return err;
-       err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIODS, hw_params);
-       if (err < 0)
-               return err;
-#ifdef REFINE_DEBUG
-       snd_output_puts(log, "DMIX REFINE (end):\n");
-       snd_pcm_hw_params_dump(params, log);
-       snd_output_close(log);
-#endif
-       return 0;
-}
-
-static int snd_pcm_dsnoop_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED)
-{
-       /* values are cached in the pcm structure */
-       
-       return 0;
-}
-
-static int snd_pcm_dsnoop_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
-{
-       /* values are cached in the pcm structure */
-       return 0;
-}
-
-static int snd_pcm_dsnoop_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED)
-{
-       /* values are cached in the pcm structure */
-       return 0;
-}
-
-static int snd_pcm_dsnoop_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
-{
-        return snd_pcm_channel_info_shm(pcm, info, -1);
-}
-
 static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
 {
        snd_pcm_direct_t *dsnoop = pcm->private_data;
+       snd_pcm_state_t state;
 
        switch(dsnoop->state) {
        case SNDRV_PCM_STATE_DRAINING:
@@ -331,18 +194,36 @@ static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
                break;
        }
        memset(status, 0, sizeof(*status));
-       status->state = dsnoop->state;
+       snd_pcm_status(dsnoop->spcm, status);
+       state = snd_pcm_state(dsnoop->spcm);
+       status->state = state == SND_PCM_STATE_RUNNING ? dsnoop->state : state;
        status->trigger_tstamp = dsnoop->trigger_tstamp;
-       status->tstamp = snd_pcm_hw_fast_tstamp(dsnoop->spcm);
        status->avail = snd_pcm_mmap_capture_avail(pcm);
        status->avail_max = status->avail > dsnoop->avail_max ? status->avail : dsnoop->avail_max;
        dsnoop->avail_max = 0;
+       status->delay = snd_pcm_mmap_capture_delay(pcm);
        return 0;
 }
 
 static snd_pcm_state_t snd_pcm_dsnoop_state(snd_pcm_t *pcm)
 {
        snd_pcm_direct_t *dsnoop = pcm->private_data;
+       int err;
+       snd_pcm_state_t state;
+       state = snd_pcm_state(dsnoop->spcm);
+       switch (state) {
+       case SND_PCM_STATE_SUSPENDED:
+       case SND_PCM_STATE_DISCONNECTED:
+               dsnoop->state = state;
+               return state;
+       case SND_PCM_STATE_XRUN:
+               if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
+                       return err;
+               break;
+       default:
+               break;
+       }
+       snd_pcm_direct_client_chk_xrun(dsnoop, pcm);
        return dsnoop->state;
 }
 
@@ -357,12 +238,15 @@ static int snd_pcm_dsnoop_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
                err = snd_pcm_dsnoop_sync_ptr(pcm);
                if (err < 0)
                        return err;
+               /* Fall through */
        case SNDRV_PCM_STATE_PREPARED:
        case SNDRV_PCM_STATE_SUSPENDED:
                *delayp = snd_pcm_mmap_capture_hw_avail(pcm);
                return 0;
        case SNDRV_PCM_STATE_XRUN:
                return -EPIPE;
+       case SNDRV_PCM_STATE_DISCONNECTED:
+               return -ENODEV;
        default:
                return -EBADFD;
        }
@@ -381,48 +265,39 @@ static int snd_pcm_dsnoop_hwsync(snd_pcm_t *pcm)
                return 0;
        case SNDRV_PCM_STATE_XRUN:
                return -EPIPE;
+       case SNDRV_PCM_STATE_DISCONNECTED:
+               return -ENODEV;
        default:
                return -EBADFD;
        }
 }
 
-static int snd_pcm_dsnoop_prepare(snd_pcm_t *pcm)
-{
-       snd_pcm_direct_t *dsnoop = pcm->private_data;
-
-       snd_pcm_direct_check_interleave(dsnoop, pcm);
-       // assert(pcm->boundary == dsnoop->shmptr->s.boundary); /* for sure */
-       dsnoop->state = SND_PCM_STATE_PREPARED;
-       dsnoop->appl_ptr = 0;
-       dsnoop->hw_ptr = 0;
-       return 0;
-}
-
 static int snd_pcm_dsnoop_reset(snd_pcm_t *pcm)
 {
        snd_pcm_direct_t *dsnoop = pcm->private_data;
        dsnoop->hw_ptr %= pcm->period_size;
        dsnoop->appl_ptr = dsnoop->hw_ptr;
-       dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr = *dsnoop->spcm->hw.ptr;
+       dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr;
+       snd_pcm_direct_reset_slave_ptr(pcm, dsnoop);
        return 0;
 }
 
 static int snd_pcm_dsnoop_start(snd_pcm_t *pcm)
 {
        snd_pcm_direct_t *dsnoop = pcm->private_data;
-       struct timeval tv;
        int err;
-       
+
        if (dsnoop->state != SND_PCM_STATE_PREPARED)
                return -EBADFD;
+       snd_pcm_hwsync(dsnoop->spcm);
+       snoop_timestamp(pcm);
+       dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr;
+       snd_pcm_direct_reset_slave_ptr(pcm, dsnoop);
        err = snd_timer_start(dsnoop->timer);
        if (err < 0)
                return err;
        dsnoop->state = SND_PCM_STATE_RUNNING;
-       dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr = *dsnoop->spcm->hw.ptr;
-       gettimeofday(&tv, 0);
-       dsnoop->trigger_tstamp.tv_sec = tv.tv_sec;
-       dsnoop->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
+       dsnoop->trigger_tstamp = dsnoop->update_tstamp;
        return 0;
 }
 
@@ -431,12 +306,13 @@ static int snd_pcm_dsnoop_drop(snd_pcm_t *pcm)
        snd_pcm_direct_t *dsnoop = pcm->private_data;
        if (dsnoop->state == SND_PCM_STATE_OPEN)
                return -EBADFD;
-       snd_timer_stop(dsnoop->timer);
        dsnoop->state = SND_PCM_STATE_SETUP;
+       snd_timer_stop(dsnoop->timer);
        return 0;
 }
 
-static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
+/* locked version */
+static int __snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
 {
        snd_pcm_direct_t *dsnoop = pcm->private_data;
        snd_pcm_uframes_t stop_threshold;
@@ -453,55 +329,59 @@ static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
                        break;
                if (pcm->mode & SND_PCM_NONBLOCK)
                        return -EAGAIN;
-               snd_pcm_wait(pcm, -1);
+               __snd_pcm_wait_in_lock(pcm, -1);
        }
        pcm->stop_threshold = stop_threshold;
        return snd_pcm_dsnoop_drop(pcm);
 }
 
-static int snd_pcm_dsnoop_pause(snd_pcm_t *pcm, int enable)
+static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
 {
-       snd_pcm_direct_t *dsnoop = pcm->private_data;
-        if (enable) {
-               if (dsnoop->state != SND_PCM_STATE_RUNNING)
-                       return -EBADFD;
-               dsnoop->state = SND_PCM_STATE_PAUSED;
-               snd_timer_stop(dsnoop->timer);
-       } else {
-               if (dsnoop->state != SND_PCM_STATE_PAUSED)
-                       return -EBADFD;
-                dsnoop->state = SND_PCM_STATE_RUNNING;
-                snd_timer_start(dsnoop->timer);
-       }
-       return 0;
+       int err;
+
+       snd_pcm_lock(pcm);
+       err = __snd_pcm_dsnoop_drain(pcm);
+       snd_pcm_unlock(pcm);
+       return err;
+}
+
+static int snd_pcm_dsnoop_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
+{
+       return -EIO;
+}
+
+static snd_pcm_sframes_t snd_pcm_dsnoop_rewindable(snd_pcm_t *pcm)
+{
+       return snd_pcm_mmap_capture_hw_avail(pcm);
 }
 
 static snd_pcm_sframes_t snd_pcm_dsnoop_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
 {
+       snd_pcm_sframes_t avail;
+
+       avail = snd_pcm_dsnoop_rewindable(pcm);
+       if (frames > (snd_pcm_uframes_t)avail)
+               frames = avail;
        snd_pcm_mmap_appl_backward(pcm, frames);
        return frames;
 }
 
+static snd_pcm_sframes_t snd_pcm_dsnoop_forwardable(snd_pcm_t *pcm)
+{
+       return snd_pcm_mmap_capture_avail(pcm);
+}
+
 static snd_pcm_sframes_t snd_pcm_dsnoop_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
 {
        snd_pcm_sframes_t avail;
 
-       avail = snd_pcm_mmap_capture_hw_avail(pcm);
-       if (avail < 0)
-               return 0;
+       avail = snd_pcm_dsnoop_forwardable(pcm);
        if (frames > (snd_pcm_uframes_t)avail)
                frames = avail;
        snd_pcm_mmap_appl_forward(pcm, frames);
        return frames;
 }
 
-static int snd_pcm_dsnoop_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
-{
-       // snd_pcm_direct_t *dsnoop = pcm->private_data;
-       // FIXME
-       return 0;
-}
-
 static snd_pcm_sframes_t snd_pcm_dsnoop_writei(snd_pcm_t *pcm ATTRIBUTE_UNUSED, const void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
 {
        return -ENODEV;
@@ -512,16 +392,6 @@ static snd_pcm_sframes_t snd_pcm_dsnoop_writen(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
        return -ENODEV;
 }
 
-static int snd_pcm_dsnoop_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
-{
-       return 0;
-}
-
-static int snd_pcm_dsnoop_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
-{
-       return 0;
-}
-
 static int snd_pcm_dsnoop_close(snd_pcm_t *pcm)
 {
        snd_pcm_direct_t *dsnoop = pcm->private_data;
@@ -534,14 +404,12 @@ static int snd_pcm_dsnoop_close(snd_pcm_t *pcm)
                snd_pcm_direct_server_discard(dsnoop);
        if (dsnoop->client)
                snd_pcm_direct_client_discard(dsnoop);
-       if (snd_pcm_direct_shm_discard(dsnoop) > 0) {
-               if (snd_pcm_direct_semaphore_discard(dsnoop) < 0)
-                       snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
-       } else {
-               snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
-       }
-       if (dsnoop->bindings)
-               free(dsnoop->bindings);
+       if (snd_pcm_direct_shm_discard(dsnoop)) {
+               if (snd_pcm_direct_semaphore_discard(dsnoop))
+                       snd_pcm_direct_semaphore_final(dsnoop, DIRECT_IPC_SEM_CLIENT);
+       } else
+               snd_pcm_direct_semaphore_final(dsnoop, DIRECT_IPC_SEM_CLIENT);
+       free(dsnoop->bindings);
        pcm->private_data = NULL;
        free(dsnoop);
        return 0;
@@ -554,16 +422,31 @@ static snd_pcm_sframes_t snd_pcm_dsnoop_mmap_commit(snd_pcm_t *pcm,
        snd_pcm_direct_t *dsnoop = pcm->private_data;
        int err;
 
+       switch (snd_pcm_state(dsnoop->spcm)) {
+       case SND_PCM_STATE_XRUN:
+               if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
+                       return err;
+               break;
+       case SND_PCM_STATE_SUSPENDED:
+               return -ESTRPIPE;
+       default:
+               break;
+       }
+       if (snd_pcm_direct_client_chk_xrun(dsnoop, pcm))
+               return -EPIPE;
        if (dsnoop->state == SND_PCM_STATE_RUNNING) {
                err = snd_pcm_dsnoop_sync_ptr(pcm);
                if (err < 0)
                        return err;
        }
        snd_pcm_mmap_appl_forward(pcm, size);
+       /* clear timer queue to avoid a bogus return from poll */
+       if (snd_pcm_mmap_capture_avail(pcm) < pcm->avail_min)
+               snd_pcm_direct_clear_timer_queue(dsnoop);
        return size;
 }
 
-static snd_pcm_sframes_t snd_pcm_dsnoop_avail_update(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+static snd_pcm_sframes_t snd_pcm_dsnoop_avail_update(snd_pcm_t *pcm)
 {
        snd_pcm_direct_t *dsnoop = pcm->private_data;
        int err;
@@ -573,65 +456,101 @@ static snd_pcm_sframes_t snd_pcm_dsnoop_avail_update(snd_pcm_t *pcm ATTRIBUTE_UN
                if (err < 0)
                        return err;
        }
+       if (dsnoop->state == SND_PCM_STATE_XRUN)
+               return -EPIPE;
+
        return snd_pcm_mmap_capture_avail(pcm);
 }
 
+static int snd_pcm_dsnoop_htimestamp(snd_pcm_t *pcm,
+                                    snd_pcm_uframes_t *avail,
+                                    snd_htimestamp_t *tstamp)
+{
+       snd_pcm_direct_t *dsnoop = pcm->private_data;
+       snd_pcm_uframes_t avail1;
+       int ok = 0;
+       
+       while (1) {
+               if (dsnoop->state == SND_PCM_STATE_RUNNING ||
+                   dsnoop->state == SND_PCM_STATE_DRAINING)
+                       snd_pcm_dsnoop_sync_ptr(pcm);
+               avail1 = snd_pcm_mmap_capture_avail(pcm);
+               if (ok && *avail == avail1)
+                       break;
+               *avail = avail1;
+               *tstamp = snd_pcm_hw_fast_tstamp(dsnoop->spcm);
+               ok = 1;
+       }
+       return 0;
+}
+
 static void snd_pcm_dsnoop_dump(snd_pcm_t *pcm, snd_output_t *out)
 {
        snd_pcm_direct_t *dsnoop = pcm->private_data;
 
-       snd_output_printf(out, "Direct Stream Mixing PCM\n");
+       snd_output_printf(out, "Direct Snoop PCM\n");
        if (pcm->setup) {
-               snd_output_printf(out, "\nIts setup is:\n");
+               snd_output_printf(out, "Its setup is:\n");
                snd_pcm_dump_setup(pcm, out);
        }
        if (dsnoop->spcm)
                snd_pcm_dump(dsnoop->spcm, out);
 }
 
-static snd_pcm_ops_t snd_pcm_dsnoop_ops = {
-       close: snd_pcm_dsnoop_close,
-       info: snd_pcm_dsnoop_info,
-       hw_refine: snd_pcm_dsnoop_hw_refine,
-       hw_params: snd_pcm_dsnoop_hw_params,
-       hw_free: snd_pcm_dsnoop_hw_free,
-       sw_params: snd_pcm_dsnoop_sw_params,
-       channel_info: snd_pcm_dsnoop_channel_info,
-       dump: snd_pcm_dsnoop_dump,
-       nonblock: snd_pcm_dsnoop_nonblock,
-       async: snd_pcm_dsnoop_async,
-       poll_revents: snd_pcm_dsnoop_poll_revents,
-       mmap: snd_pcm_dsnoop_mmap,
-       munmap: snd_pcm_dsnoop_munmap,
+static const snd_pcm_ops_t snd_pcm_dsnoop_ops = {
+       .close = snd_pcm_dsnoop_close,
+       .info = snd_pcm_direct_info,
+       .hw_refine = snd_pcm_direct_hw_refine,
+       .hw_params = snd_pcm_direct_hw_params,
+       .hw_free = snd_pcm_direct_hw_free,
+       .sw_params = snd_pcm_direct_sw_params,
+       .channel_info = snd_pcm_direct_channel_info,
+       .dump = snd_pcm_dsnoop_dump,
+       .nonblock = snd_pcm_direct_nonblock,
+       .async = snd_pcm_direct_async,
+       .mmap = snd_pcm_direct_mmap,
+       .munmap = snd_pcm_direct_munmap,
+       .query_chmaps = snd_pcm_direct_query_chmaps,
+       .get_chmap = snd_pcm_direct_get_chmap,
+       .set_chmap = snd_pcm_direct_set_chmap,
 };
 
-static snd_pcm_fast_ops_t snd_pcm_dsnoop_fast_ops = {
-       status: snd_pcm_dsnoop_status,
-       state: snd_pcm_dsnoop_state,
-       hwsync: snd_pcm_dsnoop_hwsync,
-       delay: snd_pcm_dsnoop_delay,
-       prepare: snd_pcm_dsnoop_prepare,
-       reset: snd_pcm_dsnoop_reset,
-       start: snd_pcm_dsnoop_start,
-       drop: snd_pcm_dsnoop_drop,
-       drain: snd_pcm_dsnoop_drain,
-       pause: snd_pcm_dsnoop_pause,
-       rewind: snd_pcm_dsnoop_rewind,
-       forward: snd_pcm_dsnoop_forward,
-       resume: snd_pcm_dsnoop_resume,
-       writei: snd_pcm_dsnoop_writei,
-       writen: snd_pcm_dsnoop_writen,
-       readi: snd_pcm_mmap_readi,
-       readn: snd_pcm_mmap_readn,
-       avail_update: snd_pcm_dsnoop_avail_update,
-       mmap_commit: snd_pcm_dsnoop_mmap_commit,
+static const snd_pcm_fast_ops_t snd_pcm_dsnoop_fast_ops = {
+       .status = snd_pcm_dsnoop_status,
+       .state = snd_pcm_dsnoop_state,
+       .hwsync = snd_pcm_dsnoop_hwsync,
+       .delay = snd_pcm_dsnoop_delay,
+       .prepare = snd_pcm_direct_prepare,
+       .reset = snd_pcm_dsnoop_reset,
+       .start = snd_pcm_dsnoop_start,
+       .drop = snd_pcm_dsnoop_drop,
+       .drain = snd_pcm_dsnoop_drain,
+       .pause = snd_pcm_dsnoop_pause,
+       .rewindable = snd_pcm_dsnoop_rewindable,
+       .rewind = snd_pcm_dsnoop_rewind,
+       .forwardable = snd_pcm_dsnoop_forwardable,
+       .forward = snd_pcm_dsnoop_forward,
+       .resume = snd_pcm_direct_resume,
+       .link = NULL,
+       .link_slaves = NULL,
+       .unlink = NULL,
+       .writei = snd_pcm_dsnoop_writei,
+       .writen = snd_pcm_dsnoop_writen,
+       .readi = snd_pcm_mmap_readi,
+       .readn = snd_pcm_mmap_readn,
+       .avail_update = snd_pcm_dsnoop_avail_update,
+       .mmap_commit = snd_pcm_dsnoop_mmap_commit,
+       .htimestamp = snd_pcm_dsnoop_htimestamp,
+       .poll_descriptors = snd_pcm_direct_poll_descriptors,
+       .poll_descriptors_count = NULL,
+       .poll_revents = snd_pcm_direct_poll_revents,
 };
 
 /**
  * \brief Creates a new dsnoop PCM
  * \param pcmp Returns created PCM handle
  * \param name Name of PCM
- * \param ipc_key IPC key for semaphore and shared memory
+ * \param opts Direct PCM configurations
  * \param params Parameters for slave
  * \param root Configuration root
  * \param sconf Slave configuration
@@ -643,14 +562,14 @@ static snd_pcm_fast_ops_t snd_pcm_dsnoop_fast_ops = {
  *          changed in future.
  */
 int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
-                     key_t ipc_key, struct slave_params *params,
-                     snd_config_t *bindings,
-                     snd_config_t *root, snd_config_t *sconf,
-                     snd_pcm_stream_t stream, int mode)
+                       struct snd_pcm_direct_open_conf *opts,
+                       struct slave_params *params,
+                       snd_config_t *root, snd_config_t *sconf,
+                       snd_pcm_stream_t stream, int mode)
 {
        snd_pcm_t *pcm = NULL, *spcm = NULL;
        snd_pcm_direct_t *dsnoop = NULL;
-       int ret, first_instance;
+       int ret, first_instance, fail_sem_loop = 10;
 
        assert(pcmp);
 
@@ -662,31 +581,38 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
        dsnoop = calloc(1, sizeof(snd_pcm_direct_t));
        if (!dsnoop) {
                ret = -ENOMEM;
-               goto _err;
+               goto _err_nosem;
        }
        
-       ret = snd_pcm_direct_parse_bindings(dsnoop, bindings);
+       ret = snd_pcm_direct_parse_bindings(dsnoop, params, opts->bindings);
        if (ret < 0)
-               goto _err;
+               goto _err_nosem;
        
-       dsnoop->ipc_key = ipc_key;
+       dsnoop->ipc_key = opts->ipc_key;
+       dsnoop->ipc_perm = opts->ipc_perm;
+       dsnoop->ipc_gid = opts->ipc_gid;
        dsnoop->semid = -1;
        dsnoop->shmid = -1;
 
        ret = snd_pcm_new(&pcm, dsnoop->type = SND_PCM_TYPE_DSNOOP, name, stream, mode);
        if (ret < 0)
-               goto _err;
+               goto _err_nosem;
 
-       ret = snd_pcm_direct_semaphore_create_or_connect(dsnoop);
-       if (ret < 0) {
-               SNDERR("unable to create IPC semaphore");
-               goto _err;
-       }
+       while (1) {
+               ret = snd_pcm_direct_semaphore_create_or_connect(dsnoop);
+               if (ret < 0) {
+                       SNDERR("unable to create IPC semaphore");
+                       goto _err_nosem;
+               }
        
-       ret = snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
-       if (ret < 0) {
-               snd_pcm_direct_semaphore_discard(dsnoop);
-               goto _err;
+               ret = snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
+               if (ret < 0) {
+                       snd_pcm_direct_semaphore_discard(dsnoop);
+                       if (--fail_sem_loop <= 0)
+                               goto _err_nosem;
+                       continue;
+               }
+               break;
        }
                
        first_instance = ret = snd_pcm_direct_shm_create_or_connect(dsnoop);
@@ -699,9 +625,18 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
        pcm->fast_ops = &snd_pcm_dsnoop_fast_ops;
        pcm->private_data = dsnoop;
        dsnoop->state = SND_PCM_STATE_OPEN;
+       dsnoop->slowptr = opts->slowptr;
+       dsnoop->max_periods = opts->max_periods;
+       dsnoop->var_periodsize = opts->var_periodsize;
+       dsnoop->sync_ptr = snd_pcm_dsnoop_sync_ptr;
+       dsnoop->hw_ptr_alignment = opts->hw_ptr_alignment;
 
+ retry:
        if (first_instance) {
-               ret = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
+               /* recursion is already checked in
+                  snd_pcm_direct_get_slave_ipc_offset() */
+               ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
+                                        mode | SND_PCM_NONBLOCK, NULL);
                if (ret < 0) {
                        SNDERR("unable to open slave");
                        goto _err;
@@ -720,38 +655,60 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
 
                dsnoop->spcm = spcm;
                
-               ret = snd_pcm_direct_server_create(dsnoop);
-               if (ret < 0) {
-                       SNDERR("unable to create server");
-                       goto _err;
+               if (dsnoop->shmptr->use_server) {
+                       ret = snd_pcm_direct_server_create(dsnoop);
+                       if (ret < 0) {
+                               SNDERR("unable to create server");
+                               goto _err;
+                       }
                }
 
                dsnoop->shmptr->type = spcm->type;
        } else {
-               ret = snd_pcm_direct_client_connect(dsnoop);
-               if (ret < 0) {
-                       SNDERR("unable to connect client");
-                       return ret;
-               }
+               if (dsnoop->shmptr->use_server) {
+                       /* up semaphore to avoid deadlock */
+                       snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
+                       ret = snd_pcm_direct_client_connect(dsnoop);
+                       if (ret < 0) {
+                               SNDERR("unable to connect client");
+                               goto _err_nosem;
+                       }
                        
-               ret = snd_pcm_hw_open_fd(&spcm, "dsnoop_client", dsnoop->hw_fd, 0);
-               if (ret < 0) {
-                       SNDERR("unable to open hardware");
-                       goto _err;
-               }
+                       snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
+
+                       ret = snd_pcm_direct_open_secondary_client(&spcm, dsnoop, "dsnoop_client");
+                       if (ret < 0)
+                               goto _err;
+               } else {
+
+                       ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
+                                                mode | SND_PCM_NONBLOCK |
+                                                SND_PCM_APPEND,
+                                                NULL);
+                       if (ret < 0) {
+                               /* all other streams have been closed;
+                                * retry as the first instance
+                                */
+                               if (ret == -EBADFD) {
+                                       first_instance = 1;
+                                       goto retry;
+                               }
+                               SNDERR("unable to open slave");
+                               goto _err;
+                       }
+                       if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
+                               SNDERR("dsnoop plugin can be only connected to hw plugin");
+                               ret = -EINVAL;
+                               goto _err;
+                       }
                
-               spcm->donot_close = 1;
-               spcm->setup = 1;
-               spcm->buffer_size = dsnoop->shmptr->s.buffer_size;
-               spcm->sample_bits = dsnoop->shmptr->s.sample_bits;
-               spcm->channels = dsnoop->shmptr->s.channels;
-               spcm->format = dsnoop->shmptr->s.format;
-               spcm->boundary = dsnoop->shmptr->s.boundary;
-               ret = snd_pcm_mmap(spcm);
-               if (ret < 0) {
-                       SNDERR("unable to mmap channels");
-                       goto _err;
+                       ret = snd_pcm_direct_initialize_secondary_slave(dsnoop, spcm, params);
+                       if (ret < 0) {
+                               SNDERR("unable to initialize slave");
+                               goto _err;
+                       }
                }
+
                dsnoop->spcm = spcm;
        }
 
@@ -763,7 +720,7 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
 
        pcm->poll_fd = dsnoop->poll_fd;
        pcm->poll_events = POLLIN;      /* it's different than other plugins */
-               
+       pcm->tstamp_type = spcm->tstamp_type;
        pcm->mmap_rw = 1;
        snd_pcm_set_hw_ptr(pcm, &dsnoop->hw_ptr, -1, 0);
        snd_pcm_set_appl_ptr(pcm, &dsnoop->appl_ptr, -1, 0);
@@ -777,25 +734,23 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
        return 0;
        
  _err:
+       if (dsnoop->timer)
+               snd_timer_close(dsnoop->timer);
+       if (dsnoop->server)
+               snd_pcm_direct_server_discard(dsnoop);
+       if (dsnoop->client)
+               snd_pcm_direct_client_discard(dsnoop);
+       if (spcm)
+               snd_pcm_close(spcm);
+       if ((dsnoop->shmid >= 0) && (snd_pcm_direct_shm_discard(dsnoop))) {
+               if (snd_pcm_direct_semaphore_discard(dsnoop))
+                       snd_pcm_direct_semaphore_final(dsnoop, DIRECT_IPC_SEM_CLIENT);
+       } else
+               snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
+
+ _err_nosem:
        if (dsnoop) {
-               if (dsnoop->timer)
-                       snd_timer_close(dsnoop->timer);
-               if (dsnoop->server)
-                       snd_pcm_direct_server_discard(dsnoop);
-               if (dsnoop->client)
-                       snd_pcm_direct_client_discard(dsnoop);
-               if (spcm)
-                       snd_pcm_close(spcm);
-               if (dsnoop->shmid >= 0) {
-                       if (snd_pcm_direct_shm_discard(dsnoop) > 0) {
-                               if (dsnoop->semid >= 0) {
-                                       if (snd_pcm_direct_semaphore_discard(dsnoop) < 0)
-                                               snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
-                               }
-                       }
-               }
-               if (dsnoop->bindings)
-                       free(dsnoop->bindings);
+               free(dsnoop->bindings);
                free(dsnoop);
        }
        if (pcm)
@@ -805,15 +760,26 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
 
 /*! \page pcm_plugins
 
-\section pcm_plugins_snoop Plugin: dsnoop
+\section pcm_plugins_dsnoop Plugin: dsnoop
 
 This plugin splits one capture stream to more.
+It works the reverse way of \ref pcm_plugins_dmix "dmix plugin",
+reading the shared capture buffer from many clients concurrently.
+The meaning of parameters below are almost identical with
+dmix plugin.
 
 \code
 pcm.name {
        type dsnoop             # Direct snoop
        ipc_key INT             # unique IPC key
        ipc_key_add_uid BOOL    # add current uid to unique IPC key
+       ipc_perm INT            # IPC permissions (octal, default 0600)
+       hw_ptr_alignment STR    # Slave application and hw pointer alignment type
+               # STR can be one of the below strings :
+               # no
+               # roundup
+               # rounddown
+               # auto (default)
        slave STR
        # or
        slave {                 # Slave definition
@@ -834,10 +800,30 @@ pcm.name {
        bindings {              # note: this is client independent!!!
                N INT           # maps slave channel to client channel N
        }
+       slowptr BOOL            # slow but more precise pointer updates
 }
 \endcode
 
-\subsection pcm_plugins_hw_funcref Function reference
+<code>hw_ptr_alignment</code> specifies slave application and hw
+pointer alignment type. By default hw_ptr_alignment is auto. Below are
+the possible configurations:
+- no: minimal latency with minimal frames dropped at startup. But
+  wakeup of application (return from snd_pcm_wait() or poll()) can
+  take up to 2 * period.
+- roundup: It is guaranteed that all frames will be played at
+  startup. But the latency will increase upto period-1 frames.
+- rounddown: It is guaranteed that a wakeup will happen for each
+  period and frames can be written from application. But on startup
+  upto period-1 frames will be dropped.
+- auto: Selects the best approach depending on the used period and
+  buffer size.
+  If the application buffer size is < 2 * application period,
+  "roundup" will be selected to avoid over runs. If the slave_period
+  is < 10ms we could expect that there are low latency
+  requirements. Therefore "rounddown" will be chosen to avoid long
+  wakeup times. Else "no" will be chosen.
+
+\subsection pcm_plugins_dsnoop_funcref Function reference
 
 <UL>
   <LI>snd_pcm_dsnoop_open()
@@ -862,76 +848,26 @@ int _snd_pcm_dsnoop_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, next;
-       snd_config_t *slave = NULL, *bindings = NULL, *sconf;
+       snd_config_t *sconf;
        struct slave_params params;
-       int bsize, psize, ipc_key_add_uid = 0;
-       key_t ipc_key = 0;
+       struct snd_pcm_direct_open_conf dopen;
+       int bsize, psize;
        int err;
-       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 (snd_pcm_conf_generic_id(id))
-                       continue;
-               if (strcmp(id, "ipc_key") == 0) {
-                       long key;
-                       err = snd_config_get_integer(n, &key);
-                       if (err < 0) {
-                               SNDERR("The field ipc_key must be an integer type");
-                               return err;
-                       }
-                       ipc_key = key;
-                       continue;
-               }
-               if (strcmp(id, "ipc_key_add_uid") == 0) {
-                       char *tmp;
-                       err = snd_config_get_ascii(n, &tmp);
-                       if (err < 0) {
-                               SNDERR("The field ipc_key_add_uid must be a boolean type");
-                               return err;
-                       }
-                       err = snd_config_get_bool_ascii(tmp);
-                       free(tmp);
-                       if (err < 0) {
-                               SNDERR("The field ipc_key_add_uid must be a boolean type");
-                               return err;
-                       }
-                       ipc_key_add_uid = err;
-                       continue;
-               }
-               if (strcmp(id, "slave") == 0) {
-                       slave = n;
-                       continue;
-               }
-               if (strcmp(id, "bindings") == 0) {
-                       bindings = n;
-                       continue;
-               }
-               SNDERR("Unknown field %s", id);
-               return -EINVAL;
-       }
-       if (!slave) {
-               SNDERR("slave is not defined");
-               return -EINVAL;
-       }
-       if (ipc_key_add_uid)
-               ipc_key += getuid();
-       if (!ipc_key) {
-               SNDERR("Unique IPC key is not defined");
-               return -EINVAL;
-       }
+
+       err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
+       if (err < 0)
+               return err;
+
        /* the default settings, it might be invalid for some hardware */
        params.format = SND_PCM_FORMAT_S16;
        params.rate = 48000;
        params.channels = 2;
-       params.period_time = 125000;    /* 0.125 seconds */
+       params.period_time = -1;
        params.buffer_time = -1;
        bsize = psize = -1;
        params.periods = 3;
-       err = snd_pcm_slave_conf(root, slave, &sconf, 8,
-                                SND_PCM_HW_PARAM_FORMAT, 0, &params.format,
+       err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8,
+                                SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &params.format,
                                 SND_PCM_HW_PARAM_RATE, 0, &params.rate,
                                 SND_PCM_HW_PARAM_CHANNELS, 0, &params.channels,
                                 SND_PCM_HW_PARAM_PERIOD_TIME, 0, &params.period_time,
@@ -942,19 +878,19 @@ int _snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
        if (err < 0)
                return err;
 
-       /* sorry, limited features */
-       if (params.format != SND_PCM_FORMAT_S16 &&
-           params.format != SND_PCM_FORMAT_S32) {
-               SNDERR("invalid format, specify s16 or s32");
-               snd_config_delete(sconf);
-               return -EINVAL;
-       }
+       /* set a reasonable default */  
+       if (psize == -1 && params.period_time == -1)
+               params.period_time = 125000;    /* 0.125 seconds */
+
+       if (params.format == -2)
+               params.format = SND_PCM_FORMAT_UNKNOWN;
 
        params.period_size = psize;
        params.buffer_size = bsize;
-       err = snd_pcm_dsnoop_open(pcmp, name, ipc_key, &params, bindings, root, sconf, stream, mode);
-       if (err < 0)
-               snd_config_delete(sconf);
+
+       err = snd_pcm_dsnoop_open(pcmp, name, &dopen, &params,
+                                 root, sconf, stream, mode);
+       snd_config_delete(sconf);
        return err;
 }
 #ifndef DOC_HIDDEN