]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Added support for auto mmap. Much improved version of pcm_share (without async signals)
authorAbramo Bagnara <abramo@alsa-project.org>
Fri, 20 Oct 2000 09:18:13 +0000 (09:18 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Fri, 20 Oct 2000 09:18:13 +0000 (09:18 +0000)
14 files changed:
doc/asoundrc.doc
src/pcm/pcm.c
src/pcm/pcm_adpcm.c
src/pcm/pcm_alaw.c
src/pcm/pcm_hw.c
src/pcm/pcm_linear.c
src/pcm/pcm_local.h
src/pcm/pcm_mulaw.c
src/pcm/pcm_multi.c
src/pcm/pcm_plug.c
src/pcm/pcm_plugin.c
src/pcm/pcm_rate.c
src/pcm/pcm_route.c
src/pcm/pcm_share.c

index e9e6d537cc7218b0a12cbe4b544ae98b6be344f1..a63519aca4b17b8b42b8693b0b364af896f13449 100644 (file)
@@ -74,7 +74,9 @@ sname         Slave PCM name on server
 - share                Share PCM
 Fields:
 sname          Slave name
-[schannels]    Slave channels 
+[schannels]    Slave channels
+[sformat]      Slave format
+[srate]                Slave rate
 binding                Bindings table
   .N           Slave channel for client channel N
 
index 49772a830ee5dde621f1c4f01ed92106eba94403..7e6f1df945e0c41eeb19d5b9a885357d2d0dbd88 100644 (file)
@@ -26,6 +26,8 @@
 #include <stdarg.h>
 #include <sys/ioctl.h>
 #include <sys/poll.h>
+#include <sys/shm.h>
+#include <sys/mman.h>
 #include <dlfcn.h>
 #include "pcm_local.h"
 #include "list.h"
@@ -138,27 +140,39 @@ int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
        return pcm->ops->channel_setup(pcm->op_arg, setup);
 }
 
-int snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
+int _snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
 {
        int err;
        snd_pcm_setup_t setup;
-       int mmap = 0;
-       assert(pcm && params);
+       if ((err = pcm->ops->params(pcm->op_arg, params)) < 0)
+               return err;
+       pcm->valid_setup = 0;
+       return snd_pcm_setup(pcm, &setup);
+}
+
+int snd_pcm_params_mmap(snd_pcm_t *pcm, snd_pcm_params_t *params)
+{
+       int err;
        if (pcm->mmap_info) {
-               mmap = 1;
                err = snd_pcm_munmap(pcm);
                if (err < 0)
                        return err;
        }
-       if ((err = pcm->ops->params(pcm->op_arg, params)) < 0)
-               return err;
-       pcm->valid_setup = 0;
-       err = snd_pcm_setup(pcm, &setup);
-       if (pcm->mmap_auto || mmap)
-               snd_pcm_mmap(pcm);
+       err = _snd_pcm_params(pcm, params);
+       if (pcm->valid_setup)
+               snd_pcm_mmap(pcm);
        return err;
 }
 
+int snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
+{
+       assert(pcm && params);
+       if (pcm->mmap_auto)
+               return snd_pcm_params_mmap(pcm, params);
+       assert(!pcm->mmap_info);
+       return _snd_pcm_params(pcm, params);
+}
+
 int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
 {
        assert(pcm && status);
@@ -227,7 +241,7 @@ int snd_pcm_set_avail_min(snd_pcm_t *pcm, size_t frames)
        int err;
        assert(pcm);
        assert(pcm->valid_setup);
-       assert(frames > 0 && frames < pcm->setup.buffer_size);
+       assert(frames > 0);
        err = pcm->fast_ops->set_avail_min(pcm->fast_op_arg, frames);
        if (err < 0)
                return err;
@@ -1069,6 +1083,60 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
        return err;
 }
 
+int snd_pcm_alloc_user_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i)
+{
+       i->type = SND_PCM_MMAP_USER;
+       i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size);
+       i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666);
+       if (i->u.user.shmid < 0) {
+               SYSERR("shmget failed");
+               return -errno;
+       }
+       i->addr = shmat(i->u.user.shmid, 0, 0);
+       if (i->addr == (void*) -1) {
+               SYSERR("shmat failed");
+               return -errno;
+       }
+       return 0;
+}
+
+int snd_pcm_alloc_kernel_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i, int fd)
+{
+       i->type = SND_PCM_MMAP_KERNEL;
+       i->size = pcm->setup.mmap_bytes;
+       i->addr = mmap(NULL, pcm->setup.mmap_bytes,
+                      PROT_WRITE | PROT_READ,
+                      MAP_FILE|MAP_SHARED, 
+                      fd, SND_PCM_MMAP_OFFSET_DATA);
+       if (i->addr == MAP_FAILED ||
+           i->addr == NULL) {
+               SYSERR("data mmap failed");
+               return -errno;
+       }
+       i->u.kernel.fd = fd;
+       return 0;
+}
+
+int snd_pcm_free_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i)
+{
+       if (i->type == SND_PCM_MMAP_USER) {
+               if (shmdt(i->addr) < 0) {
+                       SYSERR("shmdt failed");
+                       return -errno;
+               }
+               if (shmctl(i->u.user.shmid, IPC_RMID, 0) < 0) {
+                       SYSERR("shmctl IPC_RMID failed");
+                       return -errno;
+               }
+       } else {
+               if (munmap(pcm->mmap_info->addr, pcm->mmap_info->size) < 0) {
+                       SYSERR("data munmap failed");
+                       return -errno;
+               }
+       }
+       return 0;
+}
+
 void snd_pcm_error(const char *file, int line, const char *function, int err, const char *fmt, ...)
 {
        va_list arg;
index 4834fe595757403772c9fb59806852f562ad312a..4f0929a31cbccd3fc046aebfca37e9c0b0ddf177 100644 (file)
@@ -383,7 +383,7 @@ static int snd_pcm_adpcm_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
        params->format.sfmt = adpcm->sformat;
        params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
        params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
-       err = snd_pcm_params(slave, params);
+       err = snd_pcm_params_mmap(slave, params);
        params->format.sfmt = adpcm->cformat;
        params->xfer_mode = adpcm->cxfer_mode;
        params->mmap_shape = adpcm->cmmap_shape;
index 33913b2607efcc104abea361d2d3792f0cffcb89..20e170d4c3d8aeade9375326b79d5690b241907b 100644 (file)
@@ -265,7 +265,7 @@ static int snd_pcm_alaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
        params->format.sfmt = alaw->sformat;
        params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
        params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
-       err = snd_pcm_params(slave, params);
+       err = snd_pcm_params_mmap(slave, params);
        params->format.sfmt = alaw->cformat;
        params->xfer_mode = alaw->cxfer_mode;
        params->mmap_shape = alaw->cmmap_shape;
index b6570d4ee124a99cbeb10de2b0f8f66925efe85d..0c290cf3227742e6ee262bf26fb1f8fb728033d7 100644 (file)
@@ -393,37 +393,21 @@ static int snd_pcm_hw_mmap(snd_pcm_t *pcm)
 {
        snd_pcm_hw_t *hw = pcm->private;
        snd_pcm_mmap_info_t *i = calloc(1, sizeof(*i));
+       int err;
        if (!i)
                return -ENOMEM;
        if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
-               i->type = SND_PCM_MMAP_USER;
-               i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size);
-               i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666);
-               if (i->u.user.shmid < 0) {
-                       SYSERR("shmget failed");
-                       free(i);
-                       return -errno;
-               }
-               i->addr = shmat(i->u.user.shmid, 0, 0);
-               if (i->addr == (void*) -1) {
-                       SYSERR("shmat failed");
+               err = snd_pcm_alloc_user_mmap(pcm, i);
+               if (err < 0) {
                        free(i);
-                       return -errno;
+                       return err;
                }
        } else {
-               i->type = SND_PCM_MMAP_KERNEL;
-               i->size = pcm->setup.mmap_bytes;
-               i->addr = mmap(NULL, pcm->setup.mmap_bytes,
-                                           PROT_WRITE | PROT_READ,
-                                           MAP_FILE|MAP_SHARED, 
-                                           hw->fd, SND_PCM_MMAP_OFFSET_DATA);
-               if (i->addr == MAP_FAILED ||
-                   i->addr == NULL) {
-                       SYSERR("data mmap failed");
+               err = snd_pcm_alloc_kernel_mmap(pcm, i, hw->fd);
+               if (err < 0) {
                        free(i);
-                       return -errno;
+                       return err;
                }
-               i->u.kernel.fd = hw->fd;
        }
        pcm->mmap_info = i;
        pcm->mmap_info_count = 1;
@@ -452,21 +436,9 @@ static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm)
 
 static int snd_pcm_hw_munmap(snd_pcm_t *pcm)
 {
-       if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
-               if (shmdt(pcm->mmap_info->addr) < 0) {
-                       SYSERR("shmdt failed");
-                       return -errno;
-               }
-               if (shmctl(pcm->mmap_info->u.user.shmid, IPC_RMID, 0) < 0) {
-                       SYSERR("shmctl IPC_RMID failed");
-                       return -errno;
-               }
-       } else {
-               if (munmap(pcm->mmap_info->addr, pcm->mmap_info->size) < 0) {
-                       SYSERR("data munmap failed");
-                       return -errno;
-               }
-       }
+       int err = snd_pcm_free_mmap(pcm, pcm->mmap_info);
+       if (err < 0)
+               return err;
        pcm->mmap_info_count = 0;
        free(pcm->mmap_info);
        pcm->mmap_info = 0;
index 31cddbefe7daf4163f37e313f499d45e2f5236ea..b0cc2393da585c579100723871668470056588ab 100644 (file)
@@ -120,7 +120,7 @@ static int snd_pcm_linear_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
        params->format.sfmt = linear->sformat;
        params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
        params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
-       err = snd_pcm_params(slave, params);
+       err = snd_pcm_params_mmap(slave, params);
        params->format.sfmt = linear->cformat;
        params->xfer_mode = linear->cxfer_mode;
        params->mmap_shape = linear->cmmap_shape;
index 1f9fee2819819ff08030cb88f02a91d1b44d6bc2..a649251c03220916a6e74bc77b6f8f57bc9d339b 100644 (file)
@@ -112,8 +112,7 @@ struct snd_pcm {
 void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void *buf);
 void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void **bufs);
 
-int snd_pcm_mmap(snd_pcm_t *pcm);
-int snd_pcm_munmap(snd_pcm_t *pcm);
+int snd_pcm_params_mmap(snd_pcm_t *pcm, snd_pcm_params_t *params);
 int snd_pcm_mmap_ready(snd_pcm_t *pcm);
 ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *pcm, off_t offset);
 void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames);
@@ -137,6 +136,9 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
                            snd_pcm_xfer_areas_func_t func);
 ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size);
 ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size);
+int snd_pcm_alloc_user_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i);
+int snd_pcm_alloc_kernel_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i, int fd);
+int snd_pcm_free_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i);
 
 static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm)
 {
@@ -158,10 +160,13 @@ static inline size_t snd_pcm_mmap_capture_avail(snd_pcm_t *pcm)
 
 static inline size_t snd_pcm_mmap_avail(snd_pcm_t *pcm)
 {
+       ssize_t avail;
+       avail = *pcm->hw_ptr - *pcm->appl_ptr;
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
-               return snd_pcm_mmap_playback_avail(pcm);
-       else
-               return snd_pcm_mmap_capture_avail(pcm);
+               avail += pcm->setup.buffer_size;
+       if (avail < 0)
+               avail += pcm->setup.boundary;
+       return avail;
 }
 
 static inline ssize_t snd_pcm_mmap_playback_hw_avail(snd_pcm_t *pcm)
@@ -184,10 +189,13 @@ static inline ssize_t snd_pcm_mmap_capture_hw_avail(snd_pcm_t *pcm)
 
 static inline ssize_t snd_pcm_mmap_hw_avail(snd_pcm_t *pcm)
 {
+       ssize_t avail;
+       avail = *pcm->hw_ptr - *pcm->appl_ptr;
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
-               return snd_pcm_mmap_playback_hw_avail(pcm);
-       else
-               return snd_pcm_mmap_capture_hw_avail(pcm);
+               avail += pcm->setup.buffer_size;
+       if (avail < 0)
+               avail += pcm->setup.boundary;
+       return pcm->setup.buffer_size - avail;
 }
 
 #define snd_pcm_mmap_playback_delay snd_pcm_mmap_playback_hw_avail
index ae78f39a947fd271e3a555add906721d525d9500..1c4c046ec466633b783613c1994eb8e8f0665134 100644 (file)
@@ -282,7 +282,7 @@ static int snd_pcm_mulaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
        params->format.sfmt = mulaw->sformat;
        params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
        params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
-       err = snd_pcm_params(slave, params);
+       err = snd_pcm_params_mmap(slave, params);
        params->format.sfmt = mulaw->cformat;
        params->xfer_mode = mulaw->cxfer_mode;
        params->mmap_shape = mulaw->cmmap_shape;
index 94ccc44cba257c0b57f6cf6ec7ac322a2d6b201c..d22929bfe38d4ca54d2cdd02a231c4f908f4af12 100644 (file)
@@ -221,7 +221,7 @@ static int snd_pcm_multi_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
        for (i = 0; i < multi->slaves_count; ++i) {
                snd_pcm_t *slave = multi->slaves[i].pcm;
                p.format.channels = multi->slaves[i].channels_count;
-               err = snd_pcm_params(slave, &p);
+               err = snd_pcm_params_mmap(slave, &p);
                if (err < 0) {
                        params->fail_mask = p.fail_mask;
                        params->fail_reason = p.fail_reason;
index 8c2a5f6d2e67fca37e970f0c1ab339179fd82b27..d659ccb5c0b0ecde15ec64f7a0d0a5a21af9698a 100644 (file)
@@ -323,7 +323,6 @@ static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_for
        err = snd_pcm_rate_open(new, NULL, slv->sfmt, slv->rate, plug->slave, plug->slave != plug->req_slave);
        if (err < 0)
                return err;
-       (*new)->mmap_auto = 1;
        slv->rate = clt->rate;
        if (snd_pcm_format_linear(clt->sfmt))
                slv->sfmt = clt->sfmt;
@@ -388,7 +387,6 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm
        err = snd_pcm_route_open(new, NULL, slv->sfmt, slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->slave, plug->slave != plug->req_slave);
        if (err < 0)
                return err;
-       (*new)->mmap_auto = 1;
        slv->channels = clt->channels;
        if (snd_pcm_format_linear(clt->sfmt))
                slv->sfmt = clt->sfmt;
@@ -453,7 +451,6 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_f
        err = f(new, NULL, slv->sfmt, plug->slave, plug->slave != plug->req_slave);
        if (err < 0)
                return err;
-       (*new)->mmap_auto = 1;
        slv->sfmt = cfmt;
        return 1;
 }
@@ -492,7 +489,6 @@ static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
                }
                k++;
        }
-       plug->slave->mmap_auto = 0;
        assert(0);
        return 0;
 }
index e0880776937ebb8e995a05f5318bf1ea29060008..9d88126447428992d618f90bac182d25d7ca8536 100644 (file)
@@ -322,19 +322,10 @@ int snd_pcm_plugin_mmap(snd_pcm_t *pcm)
        i = calloc(1, sizeof(*i));
        if (!i)
                return -ENOMEM;
-       i->type = SND_PCM_MMAP_USER;
-       i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size);
-       i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666);
-       if (i->u.user.shmid < 0) {
-               SYSERR("shmget failed");
+       err = snd_pcm_alloc_user_mmap(pcm, i);
+       if (err < 0) {
                free(i);
-               return -errno;
-       }
-       i->addr = shmat(i->u.user.shmid, 0, 0);
-       if (i->addr == (void*) -1) {
-               SYSERR("shmat failed");
-               free(i);
-               return -errno;
+               return err;
        }
        pcm->mmap_info = i;
        pcm->mmap_info_count = 1;
@@ -348,14 +339,9 @@ int snd_pcm_plugin_munmap(snd_pcm_t *pcm)
        int err = snd_pcm_munmap(slave);
        if (err < 0)
                return err;
-       if (shmdt(pcm->mmap_info->addr) < 0) {
-               SYSERR("shmdt failed");
-               return -errno;
-       }
-       if (shmctl(pcm->mmap_info->u.user.shmid, IPC_RMID, 0) < 0) {
-               SYSERR("shmctl IPC_RMID failed");
-               return -errno;
-       }
+       err = snd_pcm_free_mmap(pcm, pcm->mmap_info);
+       if (err < 0)
+               return err;
        free(pcm->mmap_info);
        pcm->mmap_info_count = 0;
        pcm->mmap_info = 0;
index c4c8b2412b89a395b7ab04116d3d2336b7f3e040..ec8176cd00f90916a1aa05091b64922909ee518a 100644 (file)
@@ -342,7 +342,7 @@ static int snd_pcm_rate_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
        /* FIXME: boundary? */
        slave_params.xfer_mode = SND_PCM_XFER_UNSPECIFIED;
        slave_params.mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
-       err = snd_pcm_params(slave, &slave_params);
+       err = snd_pcm_params_mmap(slave, &slave_params);
        params->fail_mask = slave_params.fail_mask;
        params->fail_reason = slave_params.fail_reason;
        return err;
index 7b6883c4d5e73f27a382d51f60b5bb6cdf2f7396..25a160ad0bb8bfce577e41fefbcc6cc611413630 100644 (file)
@@ -494,7 +494,7 @@ static int snd_pcm_route_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
                params->format.channels = route->req_schannels;
        params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
        params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
-       err = snd_pcm_params(slave, params);
+       err = snd_pcm_params_mmap(slave, params);
        params->format.sfmt = route->cformat;
        params->format.channels = route->cchannels;
        params->xfer_mode = route->cxfer_mode;
index ace777d985e57a154a0a4403e5d7e63e8b99be8d..541bafc42fffef9a9642cd7f7e9698c7e336ae44 100644 (file)
 
 static LIST_HEAD(slaves);
 static pthread_mutex_t slaves_mutex = PTHREAD_MUTEX_INITIALIZER;
+char *slaves_mutex_holder;
+
+#define _S(x) #x
+#define S(x) _S(x)
+
+#if 1
+#define Pthread_mutex_lock(mutex) pthread_mutex_lock(mutex)
+#define Pthread_mutex_unlock(mutex) pthread_mutex_unlock(mutex)
+#else
+#define Pthread_mutex_lock(mutex) \
+do { \
+  int err = pthread_mutex_trylock(mutex); \
+  if (err == EBUSY) { \
+    fprintf(stderr, "lock " #mutex " is busy (%s): waiting in " __FUNCTION__ "\n", *(mutex##_holder)); \
+    pthread_mutex_lock(mutex); \
+    fprintf(stderr, "... got\n"); \
+  } \
+  *(mutex##_holder) = __FUNCTION__; \
+} while (0)
+
+#define Pthread_mutex_unlock(mutex) \
+do { \
+  *(mutex##_holder) = 0; \
+  pthread_mutex_unlock(mutex); \
+} while (0)
+#endif
 
 typedef struct {
        struct list_head clients;
        struct list_head list;
        snd_pcm_t *pcm;
+       int sformat;
+       int srate;
        size_t channels_count;
        size_t open_count;
        size_t setup_count;
@@ -47,8 +75,14 @@ typedef struct {
        size_t prepared_count;
        size_t running_count;
        size_t safety_threshold;
+       size_t silence_frames;
+       size_t hw_ptr;
+       int poll[2];
+       int polling;
        pthread_t thread;
        pthread_mutex_t mutex;
+       char *mutex_holder;
+       pthread_cond_t poll_cond;
 } snd_pcm_share_slave_t;
 
 typedef struct {
@@ -59,8 +93,10 @@ typedef struct {
        int *slave_channels;
        int xfer_mode;
        int xrun_mode;
+       size_t avail_min;
        int async_sig;
        pid_t async_pid;
+       int drain_silenced;
        struct timeval trigger_time;
        int state;
        size_t hw_ptr;
@@ -71,43 +107,169 @@ typedef struct {
        void *stopped_data;
 } snd_pcm_share_t;
 
-
 static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state);
 
+static size_t snd_pcm_share_slave_avail(snd_pcm_share_slave_t *slave)
+{
+       ssize_t avail;
+       snd_pcm_t *pcm = slave->pcm;
+       avail = slave->hw_ptr - *pcm->appl_ptr;
+       if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
+               avail += pcm->setup.buffer_size;
+       if (avail < 0)
+               avail += pcm->setup.boundary;
+       return avail;
+}
 
-static void _snd_pcm_update_poll(snd_pcm_t *pcm)
+/* Warning: take the mutex before to call this */
+/* Return number of frames to mmap_forward the slave */
+static size_t _snd_pcm_share_slave_forward(snd_pcm_share_slave_t *slave)
+{
+       struct list_head *i;
+       size_t buffer_size, boundary;
+       size_t slave_appl_ptr;
+       ssize_t frames, safety_frames;
+       ssize_t min_frames, max_frames;
+       size_t avail, slave_avail;
+       size_t slave_hw_avail;
+       slave_avail = snd_pcm_share_slave_avail(slave);
+       boundary = slave->pcm->setup.boundary;
+       buffer_size = slave->pcm->setup.buffer_size;
+       min_frames = slave_avail;
+       max_frames = 0;
+       slave_appl_ptr = *slave->pcm->appl_ptr;
+       for (i = slave->clients.next; i != &slave->clients; i = i->next) {
+               snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list);
+               snd_pcm_t *pcm = share->pcm;
+               switch (share->state) {
+               case SND_PCM_STATE_RUNNING:
+                       break;
+               case SND_PCM_STATE_DRAINING:
+                       if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
+                               continue;
+                       break;
+               default:
+                       continue;
+               }
+               avail = snd_pcm_mmap_avail(pcm);
+               frames = slave_avail - avail;
+               if (frames > max_frames)
+                       max_frames = frames;
+               if (share->state != SND_PCM_STATE_RUNNING)
+                       continue;
+               if (frames < min_frames)
+                       min_frames = frames;
+       }
+       if (max_frames == 0)
+               return 0;
+       frames = min_frames;
+       /* Slave xrun prevention */
+       slave_hw_avail = buffer_size - slave_avail;
+       safety_frames = slave->safety_threshold - slave_hw_avail;
+       if (safety_frames > 0 &&
+           frames < safety_frames) {
+               /* Avoid to pass over the last */
+               if (max_frames < safety_frames)
+                       frames = max_frames;
+               else
+                       frames = safety_frames;
+       }
+       if (frames < 0)
+               return 0;
+       return frames;
+}
+
+
+/* 
+   - stop PCM on xrun
+   - update poll status
+   - draining silencing
+   - return distance in frames to next event
+*/
+static size_t _snd_pcm_share_missing(snd_pcm_t *pcm, int slave_xrun)
 {
        snd_pcm_share_t *share = pcm->private;
        snd_pcm_share_slave_t *slave = share->slave;
-       size_t avail;
-       int ready;
+       snd_pcm_t *spcm = slave->pcm;
+       size_t buffer_size = spcm->setup.buffer_size;
+       int ready = 1, running = 0;
+       size_t avail = 0, slave_avail;
+       ssize_t hw_avail;
+       size_t missing = INT_MAX;
+       ssize_t ready_missing;
+       //      printf("state=%d hw_ptr=%d appl_ptr=%d slave appl_ptr=%d safety=%d silence=%d\n", share->state, slave->hw_ptr, share->appl_ptr, *slave->pcm->appl_ptr, slave->safety_threshold, slave->silence_frames);
        switch (share->state) {
+       case SND_PCM_STATE_RUNNING:
+               break;
        case SND_PCM_STATE_DRAINING:
-               if (pcm->stream == SND_PCM_STREAM_CAPTURE)
-                       ready = 1;
-               else {
-                       share->hw_ptr = *slave->pcm->hw_ptr;
-                       avail = snd_pcm_mmap_avail(pcm);
-                       if (avail >= pcm->setup.buffer_size) {
+               if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
+                       break;
+               /* Fall through */
+       default:
+               return INT_MAX;
+       }
+       if (slave_xrun && pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) {
+               _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
+               goto update_poll;
+       }
+       share->hw_ptr = slave->hw_ptr;
+       avail = snd_pcm_mmap_avail(pcm);
+       hw_avail = buffer_size - avail;
+       slave_avail = snd_pcm_share_slave_avail(slave);
+       if (avail < slave_avail) {
+               /* Some frames need still to be transferred */
+               ssize_t slave_hw_avail = buffer_size - slave_avail;
+               ssize_t safety_missing = slave_hw_avail - slave->safety_threshold;
+               if (safety_missing < 0) {
+                       ssize_t err;
+                       ssize_t frames = slave_avail - avail;
+                       if (-safety_missing <= frames) {
+                               frames = -safety_missing;
+                               missing = 1;
+                       }
+                       err = snd_pcm_mmap_forward(spcm, frames);
+                       assert(err == frames);
+                       slave_avail -= frames;
+               } else {
+                       if (safety_missing == 0)
+                               missing = 1;
+                       else
+                               missing = safety_missing;
+               }
+       }
+       switch (share->state) {
+       case SND_PCM_STATE_DRAINING:
+               if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+                       if (hw_avail <= 0) {
                                _snd_pcm_share_stop(pcm, SND_PCM_STATE_SETUP);
-                               ready = 1;
-                       } else
-                               ready = 0;
+                               break;
+                       }
+                       if ((size_t)hw_avail < missing)
+                               missing = hw_avail;
+                       running = 1;
+                       ready = 0;
                }
                break;
        case SND_PCM_STATE_RUNNING:
-               share->hw_ptr = *slave->pcm->hw_ptr;
-               avail = snd_pcm_mmap_avail(pcm);
-               if (avail >= pcm->setup.buffer_size &&
-                   pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) {
-                       _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
-                       ready = 1;
-               } else
-                       ready = (avail >= pcm->setup.avail_min);
+               if (pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) {
+                       if (hw_avail <= 0) {
+                               _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
+                               break;
+                       }
+                       if ((size_t)hw_avail < missing)
+                               missing = hw_avail;
+               }
+               ready_missing = share->avail_min - avail;
+               if (ready_missing > 0) {
+                       ready = 0;
+                       if ((size_t)ready_missing < missing)
+                               missing = ready_missing;
+               }
+               running = 1;
                break;
-       default:
-               ready = 1;
        }
+
+ update_poll:
        if (ready != share->ready) {
                char buf[1];
                if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
@@ -123,132 +285,135 @@ static void _snd_pcm_update_poll(snd_pcm_t *pcm)
                }
                share->ready = ready;
        }
+       if (!running)
+               return INT_MAX;
+       if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
+           share->state == SND_PCM_STATE_DRAINING &&
+           !share->drain_silenced) {
+               /* drain silencing */
+               if (avail >= slave->silence_frames) {
+                       size_t offset = share->appl_ptr % buffer_size;
+                       size_t xfer = 0;
+                       size_t size = slave->silence_frames;
+                       while (xfer < size) {
+                               size_t frames = size - xfer;
+                               size_t cont = buffer_size - offset;
+                               if (cont < frames)
+                                       frames = cont;
+                               snd_pcm_areas_silence(pcm->running_areas, offset, pcm->setup.format.channels, frames, pcm->setup.format.sfmt);
+                               offset += frames;
+                               if (offset >= buffer_size)
+                                       offset = 0;
+                               xfer += frames;
+                       }
+                       share->drain_silenced = 1;
+               } else {
+                       size_t silence_missing;
+                       silence_missing = slave->silence_frames - avail;
+                       if (silence_missing < missing)
+                               missing = silence_missing;
+               }
+       }
+       //      printf("missing=%d\n", missing);
+       return missing;
 }
 
-static void snd_pcm_share_interrupt(snd_pcm_share_slave_t *slave)
+static size_t _snd_pcm_share_slave_missing(snd_pcm_share_slave_t *slave)
 {
+       size_t missing = INT_MAX;
        struct list_head *i;
-       pthread_mutex_lock(&slave->mutex);
-       snd_pcm_avail_update(slave->pcm);
-       /* Update poll status */
+       ssize_t avail = snd_pcm_avail_update(slave->pcm);
+       int slave_xrun = (avail == -EPIPE);
+       slave->hw_ptr = *slave->pcm->hw_ptr;
        for (i = slave->clients.next; i != &slave->clients; i = i->next) {
                snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list);
                snd_pcm_t *pcm = share->pcm;
-               switch (share->state) {
-               case SND_PCM_STATE_DRAINING:
-                       if (pcm->stream == SND_PCM_STREAM_CAPTURE)
-                               break;
-                       /* Fall through */
-               case SND_PCM_STATE_RUNNING:
-                       if (pcm->mode & SND_PCM_ASYNC)
-                               kill(share->async_pid, share->async_sig);
-                       _snd_pcm_update_poll(pcm);
-                       break;
-               default:
-                       break;
-
-               }
+               size_t m = _snd_pcm_share_missing(pcm, slave_xrun);
+               if (m < missing)
+                       missing = m;
        }
-       pthread_mutex_unlock(&slave->mutex);
-}
-
-
-void sigio_handler(int sig ATTRIBUTE_UNUSED)
-{
+       return missing;
 }
 
 void *snd_pcm_share_slave_thread(void *data)
 {
        snd_pcm_share_slave_t *slave = data;
+       snd_pcm_t *spcm = slave->pcm;
+       struct pollfd pfd[2];
        int err;
-       struct sigaction act;
-       err = snd_pcm_async(slave->pcm, SIGIO, 0);
-       assert(err == 0);
-       act.sa_handler = sigio_handler;
-       sigemptyset(&act.sa_mask);
-       sigaddset(&act.sa_mask, SIGIO);
-       act.sa_flags = 0;
-       err = sigaction(SIGIO, &act, NULL);
-       assert(err == 0);
-       while (1) {
-               int sig;
-               sigwait(&act.sa_mask, &sig);
-               snd_pcm_share_interrupt(slave);
+       pfd[0].fd = slave->poll[0];
+       pfd[0].events = POLLIN;
+       pfd[1].fd = snd_pcm_poll_descriptor(spcm);
+       pfd[1].events = POLLIN | POLLOUT;
+       Pthread_mutex_lock(&slave->mutex);
+       err = pipe(slave->poll);
+       assert(err >= 0);
+       while (slave->open_count > 0) {
+               size_t missing;
+               //              printf("begin min_missing\n");
+               missing = _snd_pcm_share_slave_missing(slave);
+               //              printf("min_missing=%d\n", missing);
+               if (missing < INT_MAX) {
+                       size_t hw_ptr;
+                       ssize_t avail_min;
+                       hw_ptr = slave->hw_ptr + missing;
+                       hw_ptr += spcm->setup.frag_size - 1;
+                       if (hw_ptr >= spcm->setup.boundary)
+                               hw_ptr -= spcm->setup.boundary;
+                       hw_ptr -= hw_ptr % spcm->setup.frag_size;
+                       avail_min = hw_ptr - *spcm->appl_ptr;
+                       if (spcm->stream == SND_PCM_STREAM_PLAYBACK)
+                               avail_min += spcm->setup.buffer_size;
+                       if (avail_min < 0)
+                               avail_min += spcm->setup.boundary;
+                       // printf("avail_min=%d\n", avail_min);
+                       if ((size_t)avail_min != spcm->setup.avail_min)
+                               snd_pcm_set_avail_min(spcm, avail_min);
+                       slave->polling = 1;
+                       Pthread_mutex_unlock(&slave->mutex);
+                       err = poll(pfd, 2, -1);
+                       Pthread_mutex_lock(&slave->mutex);
+                       if (pfd[0].revents & POLLIN) {
+                               char buf[1];
+                               read(pfd[0].fd, buf, 1);
+                       }
+               } else {
+                       slave->polling = 0;
+                       pthread_cond_wait(&slave->poll_cond, &slave->mutex);
+               }
        }
+       Pthread_mutex_unlock(&slave->mutex);
        return NULL;
 }
 
-/* Warning: take the mutex before to call this */
-static void _snd_pcm_share_slave_forward(snd_pcm_share_slave_t *slave)
+static void _snd_pcm_share_update(snd_pcm_t *pcm)
 {
-       struct list_head *i;
-       size_t buffer_size, boundary;
-       size_t slave_appl_ptr;
-       ssize_t frames, safety_frames;
-       size_t min_frames, max_frames;
-       ssize_t avail;
-       int err;
-#if 0
-       avail = snd_pcm_avail_update(slave->pcm);
-       if (avail <= 0)
-               return;
-       assert(avail > 0);
-#else
-       avail = snd_pcm_mmap_avail(slave->pcm);
-#endif
-       boundary = slave->pcm->setup.boundary;
-       buffer_size = slave->pcm->setup.buffer_size;
-       min_frames = buffer_size;
-       max_frames = 0;
-       slave_appl_ptr = *slave->pcm->appl_ptr;
-       for (i = slave->clients.next; i != &slave->clients; i = i->next) {
-               snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list);
-               snd_pcm_t *pcm = share->pcm;
-               switch (share->state) {
-               case SND_PCM_STATE_RUNNING:
-                       // share->hw_ptr = *slave->pcm->hw_ptr;
-                       break;
-               case SND_PCM_STATE_DRAINING:
-               {
-                       if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
-                               continue;
-                       // share->hw_ptr = *slave->pcm->hw_ptr;
-                       break;
-               }
-               default:
-                       continue;
-               }
-               frames = share->appl_ptr - slave_appl_ptr;
-               if (frames > (ssize_t)buffer_size)
-                       frames -= pcm->setup.boundary;
-               else if (frames < -(ssize_t)pcm->setup.buffer_size)
-                       frames += pcm->setup.boundary;
-               if (frames < 0) {
-                       continue;
-               }
-               if ((size_t)frames < min_frames)
-                       min_frames = frames;
-               if ((size_t)frames > max_frames)
-                       max_frames = frames;
-       }
-       if (max_frames == 0)
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       snd_pcm_t *spcm = slave->pcm;
+       size_t missing;
+       ssize_t avail = snd_pcm_avail_update(spcm);
+       slave->hw_ptr = *slave->pcm->hw_ptr;
+       missing = _snd_pcm_share_missing(pcm, avail == -EPIPE);
+       if (!slave->polling) {
+               pthread_cond_signal(&slave->poll_cond);
                return;
-       frames = min_frames;
-       if (frames > avail)
-               frames = avail;
-       /* Slave xrun prevention */
-       safety_frames = slave->safety_threshold - snd_pcm_mmap_hw_avail(slave->pcm);
-       if (safety_frames > 0 &&
-           frames < (ssize_t)safety_frames) {
-               /* Avoid to pass over the last */
-               if (max_frames < (size_t)safety_frames)
-                       frames = max_frames;
-               else
-                       frames = safety_frames;
        }
-       if (frames > 0) {
-               err = snd_pcm_mmap_forward(slave->pcm, frames);
-               assert(err == frames);
+       if (missing < INT_MAX) {
+               size_t hw_ptr;
+               ssize_t avail_min;
+               hw_ptr = slave->hw_ptr + missing;
+               hw_ptr += spcm->setup.frag_size - 1;
+               if (hw_ptr >= spcm->setup.boundary)
+                       hw_ptr -= spcm->setup.boundary;
+               hw_ptr -= hw_ptr % spcm->setup.frag_size;
+               avail_min = hw_ptr - *spcm->appl_ptr;
+               if (spcm->stream == SND_PCM_STREAM_PLAYBACK)
+                       avail_min += spcm->setup.buffer_size;
+               if (avail_min < 0)
+                       avail_min += spcm->setup.boundary;
+               if ((size_t)avail_min < spcm->setup.avail_min)
+                       snd_pcm_set_avail_min(spcm, avail_min);
        }
 }
 
@@ -290,12 +455,27 @@ static int snd_pcm_share_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info
                info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
                return -EINVAL;
        }
+       if (slave->sformat >= 0) {
+               if ((req_mask & SND_PCM_PARAMS_SFMT) &&
+                   info->req.format.sfmt != slave->sformat) {
+                       info->req.fail_mask |= SND_PCM_PARAMS_SFMT;
+                       info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+                       return -EINVAL;
+               }
+               info->req.format.sfmt = slave->sformat;
+               info->req_mask |= SND_PCM_PARAMS_SFMT;
+       }
+       if (slave->srate >= 0) {
+               info->req.format.rate = slave->srate;
+               info->req_mask |= SND_PCM_PARAMS_RATE;
+       }
+
        info->req_mask |= SND_PCM_PARAMS_CHANNELS;
        info->req.format.channels = slave->channels_count;
        err = snd_pcm_params_info(slave->pcm, info);
        info->req.format.channels = channels;
        info->req_mask = req_mask;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        if (slave->setup_count > 1 || 
            (slave->setup_count == 1 && !pcm->valid_setup)) {
                snd_pcm_setup_t *s = &slave->pcm->setup;
@@ -322,7 +502,7 @@ static int snd_pcm_share_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info
                info->flags |= SND_PCM_INFO_COMPLEX;
        }
  _end:
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return err;
 }
 
@@ -333,7 +513,7 @@ static int snd_pcm_share_mmap(snd_pcm_t *pcm)
        snd_pcm_mmap_info_t *i;
        size_t count;
        int err = 0;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        if (slave->mmap_count == 0) {
                err = snd_pcm_mmap(slave->pcm);
                if (err < 0)
@@ -348,28 +528,17 @@ static int snd_pcm_share_mmap(snd_pcm_t *pcm)
                err = -ENOMEM;
                goto _end;
        }
-       i->type = SND_PCM_MMAP_USER;
-       i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size);
-       i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666);
-       if (i->u.user.shmid < 0) {
-               SYSERR("shmget failed");
-               free(i);
-               err = -errno;
-               goto _end;
-       }
-       i->addr = shmat(i->u.user.shmid, 0, 0);
-       if (i->addr == (void*) -1) {
-               SYSERR("shmat failed");
+       err = snd_pcm_alloc_user_mmap(pcm, i);
+       if (err < 0) {
                free(i);
-               err = -errno;
-               goto _end;
+               return err;
        }
        share->stopped_data = i->addr;
        memcpy(i + 1, slave->pcm->mmap_info, count * sizeof(*pcm->mmap_info));
        pcm->mmap_info_count = count + 1;
        pcm->mmap_info = i;
  _end:
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return 0;
 }
 
@@ -379,28 +548,21 @@ static int snd_pcm_share_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
        snd_pcm_share_slave_t *slave = share->slave;
        snd_pcm_mmap_info_t *i = pcm->mmap_info;
        int err = 0;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        slave->mmap_count--;
        if (slave->mmap_count == 0) {
                err = snd_pcm_munmap(slave->pcm);
                if (err < 0)
                        goto _end;
        }
-       if (shmdt(i->addr) < 0) {
-               SYSERR("shmdt failed");
-               err = -errno;
-               goto _end;
-       }
-       if (shmctl(i->u.user.shmid, IPC_RMID, 0) < 0) {
-               SYSERR("shmctl IPC_RMID failed");
-               err =-errno;
+       err = snd_pcm_free_mmap(pcm, i);
+       if (err < 0)
                goto _end;
-       }
        free(i);
        pcm->mmap_info_count = 0;
        pcm->mmap_info = 0;
  _end:
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return err;
 }
                
@@ -418,12 +580,12 @@ static int snd_pcm_share_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
        }
        share->xfer_mode = params->xfer_mode;
        share->xrun_mode = params->xrun_mode;
-       pthread_mutex_lock(&slave->mutex);
+       share->avail_min = params->avail_min;
+       Pthread_mutex_lock(&slave->mutex);
        if (slave->setup_count > 1 || 
            (slave->setup_count == 1 && !pcm->valid_setup)) {
                snd_pcm_setup_t *s = &slave->pcm->setup;
                if (params->format.sfmt != s->format.sfmt) {
-                       printf("%d %d\n", params->format.sfmt, s->format.sfmt);
                        ERR("slave is already running with different format");
                        params->fail_mask |= SND_PCM_PARAMS_SFMT;
                }
@@ -434,17 +596,34 @@ static int snd_pcm_share_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
                }
        } else {
                snd_pcm_params_t sp = *params;
+               snd_pcm_setup_t *ss;
+               if (slave->sformat >= 0 &&
+                   params->format.sfmt != slave->sformat) {
+                       params->fail_mask = SND_PCM_PARAMS_SFMT;
+                       params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+                       ERR("format requested (%d) differs from configuration (%d)", params->format.sfmt, slave->sformat);
+                       err = -EINVAL;
+                       goto _end;
+               }
+               if (slave->srate >= 0)
+                       sp.format.rate = slave->srate;
                sp.xfer_mode = SND_PCM_XFER_UNSPECIFIED;
                sp.xrun_mode = SND_PCM_XRUN_NONE;
                sp.format.channels = slave->channels_count;
-               err = snd_pcm_params(slave->pcm, &sp);
+               err = snd_pcm_params_mmap(slave->pcm, &sp);
                if (err < 0)
                        goto _end;
+               ss = &slave->pcm->setup;
+               /* >= 30 ms */
+               slave->safety_threshold = ss->format.rate * 30 / 1000;
+               slave->safety_threshold += ss->frag_size - 1;
+               slave->safety_threshold -= slave->safety_threshold % ss->frag_size;
+               slave->silence_frames = slave->safety_threshold;
        }
        share->state = SND_PCM_STATE_SETUP;
        slave->setup_count++;
  _end:
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return err;
 }
 
@@ -458,6 +637,9 @@ static int snd_pcm_share_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup)
                return err;
        setup->xrun_mode = share->xrun_mode;
        setup->format.channels = share->channels_count;
+       if (share->avail_min > setup->buffer_size)
+               share->avail_min = setup->buffer_size;
+       setup->avail_min = share->avail_min;
        if (share->xfer_mode == SND_PCM_XFER_UNSPECIFIED)
                setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
        else
@@ -473,7 +655,7 @@ static int snd_pcm_share_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
        snd_pcm_share_slave_t *slave = share->slave;
        int err = 0;
        ssize_t sd = 0, d = 0;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
                status->avail = snd_pcm_mmap_playback_avail(pcm);
                if (share->state != SND_PCM_STATE_RUNNING &&
@@ -494,7 +676,7 @@ static int snd_pcm_share_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
        status->state = share->state;
        status->trigger_time = share->trigger_time;
  _end:
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return err;
 }
 
@@ -534,9 +716,9 @@ static int snd_pcm_share_delay(snd_pcm_t *pcm, ssize_t *delayp)
        snd_pcm_share_t *share = pcm->private;
        snd_pcm_share_slave_t *slave = share->slave;
        int err;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        err = _snd_pcm_share_delay(pcm, delayp);
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return err;
 }
 
@@ -544,22 +726,21 @@ static ssize_t snd_pcm_share_avail_update(snd_pcm_t *pcm)
 {
        snd_pcm_share_t *share = pcm->private;
        snd_pcm_share_slave_t *slave = share->slave;
-       ssize_t ret = 0;
-       pthread_mutex_lock(&slave->mutex);
-       ret = snd_pcm_avail_update(slave->pcm);
-       if (share->state == SND_PCM_STATE_RUNNING)
-               share->hw_ptr = *slave->pcm->hw_ptr;
-       if (ret >= 0) {
-               ret = snd_pcm_mmap_avail(pcm);
-               if ((size_t)ret > pcm->setup.buffer_size) {
-                       if (share->state == SND_PCM_STATE_RUNNING &&
-                           pcm->setup.xrun_mode != SND_PCM_XRUN_NONE)
-                               _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
-                       return -EPIPE;
+       ssize_t avail;
+       Pthread_mutex_lock(&slave->mutex);
+       if (share->state == SND_PCM_STATE_RUNNING) {
+               avail = snd_pcm_avail_update(slave->pcm);
+               if (avail < 0) {
+                       Pthread_mutex_unlock(&slave->mutex);
+                       return avail;
                }
+               share->hw_ptr = *slave->pcm->hw_ptr;
        }
-       pthread_mutex_unlock(&slave->mutex);
-       return ret;
+       Pthread_mutex_unlock(&slave->mutex);
+       avail = snd_pcm_mmap_avail(pcm);
+       if ((size_t)avail > pcm->setup.buffer_size)
+               return -EPIPE;
+       return avail;
 }
 
 /* Call it with mutex held */
@@ -584,9 +765,15 @@ static ssize_t _snd_pcm_share_mmap_forward(snd_pcm_t *pcm, size_t size)
                }
        }
        snd_pcm_mmap_appl_forward(pcm, size);
-       if (share->state == SND_PCM_STATE_RUNNING)
-               _snd_pcm_share_slave_forward(share->slave);
-       _snd_pcm_update_poll(pcm);
+       if (share->state == SND_PCM_STATE_RUNNING) {
+               ssize_t frames = _snd_pcm_share_slave_forward(slave);
+               if (frames > 0) {
+                       ssize_t err;
+                       err = snd_pcm_mmap_forward(slave->pcm, frames);
+                       assert(err == frames);
+               }
+               _snd_pcm_share_update(pcm);
+       }
        return size;
 }
 
@@ -595,9 +782,9 @@ static ssize_t snd_pcm_share_mmap_forward(snd_pcm_t *pcm, size_t size)
        snd_pcm_share_t *share = pcm->private;
        snd_pcm_share_slave_t *slave = share->slave;
        ssize_t ret;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        ret = _snd_pcm_share_mmap_forward(pcm, size);
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return ret;
 }
 
@@ -606,7 +793,7 @@ static int snd_pcm_share_prepare(snd_pcm_t *pcm)
        snd_pcm_share_t *share = pcm->private;
        snd_pcm_share_slave_t *slave = share->slave;
        int err = 0;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        if (slave->prepared_count == 0) {
                err = snd_pcm_prepare(slave->pcm);
                if (err < 0)
@@ -617,7 +804,7 @@ static int snd_pcm_share_prepare(snd_pcm_t *pcm)
        share->appl_ptr = 0;
        share->state = SND_PCM_STATE_PREPARED;
  _end:
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return err;
 }
 
@@ -628,7 +815,7 @@ static int snd_pcm_share_start(snd_pcm_t *pcm)
        int err = 0;
        if (share->state != SND_PCM_STATE_PREPARED)
                return -EBADFD;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        share->state = SND_PCM_STATE_RUNNING;
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
                size_t hw_avail = snd_pcm_mmap_playback_hw_avail(pcm);
@@ -661,7 +848,9 @@ static int snd_pcm_share_start(snd_pcm_t *pcm)
                                           pcm->setup.format.sfmt);
                        xfer += frames;
                }
-               _snd_pcm_share_mmap_forward(pcm, hw_avail);
+               snd_pcm_mmap_appl_forward(pcm, hw_avail);
+               if (slave->running_count == 0)
+                       snd_pcm_mmap_forward(slave->pcm, hw_avail);
        }
        if (slave->running_count == 0) {
                err = snd_pcm_start(slave->pcm);
@@ -669,9 +858,10 @@ static int snd_pcm_share_start(snd_pcm_t *pcm)
                        goto _end;
        }
        slave->running_count++;
+       _snd_pcm_share_update(pcm);
        gettimeofday(&share->trigger_time, 0);
  _end:
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return err;
 }
 
@@ -774,7 +964,7 @@ static ssize_t _snd_pcm_share_rewind(snd_pcm_t *pcm, size_t frames)
                n += ret;
        }
        snd_pcm_mmap_appl_backward(pcm, n);
-       _snd_pcm_update_poll(pcm);
+       _snd_pcm_share_update(pcm);
        return n;
 }
 
@@ -783,9 +973,9 @@ static ssize_t snd_pcm_share_rewind(snd_pcm_t *pcm, size_t frames)
        snd_pcm_share_t *share = pcm->private;
        snd_pcm_share_slave_t *slave = share->slave;
        ssize_t ret;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        ret = _snd_pcm_share_rewind(pcm, frames);
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return ret;
 }
 
@@ -793,10 +983,11 @@ static int snd_pcm_share_set_avail_min(snd_pcm_t *pcm, size_t frames)
 {
        snd_pcm_share_t *share = pcm->private;
        snd_pcm_share_slave_t *slave = share->slave;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        pcm->setup.avail_min = frames;
-       _snd_pcm_update_poll(pcm);
-       pthread_mutex_unlock(&slave->mutex);
+       share->avail_min = frames;
+       _snd_pcm_share_update(pcm);
+       Pthread_mutex_unlock(&slave->mutex);
        return 0;
 }
 
@@ -839,7 +1030,7 @@ static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state)
                err = snd_pcm_delay(slave->pcm, &delay);
                if (err >= 0 && delay > 0)
                        snd_pcm_rewind(slave->pcm, delay);
-               _snd_pcm_share_slave_forward(slave);
+               share->drain_silenced = 0;
        }
        share->state = state;
        slave->prepared_count--;
@@ -855,39 +1046,48 @@ static int snd_pcm_share_drain(snd_pcm_t *pcm)
        snd_pcm_share_t *share = pcm->private;
        snd_pcm_share_slave_t *slave = share->slave;
        int err = 0;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        switch (share->state) {
        case SND_PCM_STATE_OPEN:
                err = -EBADFD;
                goto _end;
        case SND_PCM_STATE_PREPARED:
                share->state = SND_PCM_STATE_SETUP;
-               break;
+               goto _end;
        case SND_PCM_STATE_SETUP:
                goto _end;
-       case SND_PCM_STATE_DRAINING:
-               break;
-       case SND_PCM_STATE_XRUN:
-               if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+       }
+       if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+               switch (share->state) {
+               case SND_PCM_STATE_XRUN:
                        share->state = SND_PCM_STATE_SETUP;
                        goto _end;
+               case SND_PCM_STATE_DRAINING:
+               case SND_PCM_STATE_RUNNING:
+                       share->state = SND_PCM_STATE_DRAINING;
+                       _snd_pcm_share_update(pcm);
+                       Pthread_mutex_unlock(&slave->mutex);
+                       if (!(pcm->mode & SND_PCM_NONBLOCK))
+                               snd_pcm_wait(pcm, -1);
+                       return 0;
                }
-               /* Fall through */
-       case SND_PCM_STATE_RUNNING:
-               if (snd_pcm_mmap_avail(pcm) <= 0) {
-                       share->state = SND_PCM_STATE_SETUP;
-                       goto _end;
+       } else {
+               switch (share->state) {
+               case SND_PCM_STATE_RUNNING:
+                       _snd_pcm_share_stop(pcm, SND_PCM_STATE_DRAINING);
+                       _snd_pcm_share_update(pcm);
+                       /* Fall through */
+               case SND_PCM_STATE_XRUN:
+               case SND_PCM_STATE_DRAINING:
+                       if (snd_pcm_mmap_capture_avail(pcm) <= 0)
+                               share->state = SND_PCM_STATE_SETUP;
+                       else
+                               share->state = SND_PCM_STATE_DRAINING;
+                       break;
                }
-               share->state = SND_PCM_STATE_DRAINING;
-               break;
-       }
-       if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
-               _snd_pcm_update_poll(pcm);
-               if (!(pcm->mode & SND_PCM_NONBLOCK))
-                       snd_pcm_wait(pcm, -1);
        }
  _end:
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return err;
 }
 
@@ -896,7 +1096,7 @@ static int snd_pcm_share_drop(snd_pcm_t *pcm)
        snd_pcm_share_t *share = pcm->private;
        snd_pcm_share_slave_t *slave = share->slave;
        int err = 0;
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slave->mutex);
        switch (share->state) {
        case SND_PCM_STATE_OPEN:
                err = -EBADFD;
@@ -911,7 +1111,7 @@ static int snd_pcm_share_drop(snd_pcm_t *pcm)
                /* Fall through */
        case SND_PCM_STATE_RUNNING:
                _snd_pcm_share_stop(pcm, SND_PCM_STATE_SETUP);
-               _snd_pcm_update_poll(pcm);
+               _snd_pcm_share_update(pcm);
                break;
        case SND_PCM_STATE_PREPARED:
        case SND_PCM_STATE_XRUN:
@@ -921,7 +1121,7 @@ static int snd_pcm_share_drop(snd_pcm_t *pcm)
        
        share->appl_ptr = share->hw_ptr = 0;
  _end:
-       pthread_mutex_unlock(&slave->mutex);
+       Pthread_mutex_unlock(&slave->mutex);
        return err;
 }
 
@@ -930,27 +1130,27 @@ static int snd_pcm_share_close(snd_pcm_t *pcm)
        snd_pcm_share_t *share = pcm->private;
        snd_pcm_share_slave_t *slave = share->slave;
        int err = 0;
-       pthread_mutex_lock(&slaves_mutex);
-       pthread_mutex_lock(&slave->mutex);
+       Pthread_mutex_lock(&slaves_mutex);
+       Pthread_mutex_lock(&slave->mutex);
        if (pcm->valid_setup)
                slave->setup_count--;
        slave->open_count--;
        if (slave->open_count == 0) {
-               err = pthread_cancel(slave->thread);
-               assert(err == 0);
+               err = snd_pcm_close(slave->pcm);
+               pthread_cond_signal(&slave->poll_cond);
+               Pthread_mutex_unlock(&slave->mutex);
                err = pthread_join(slave->thread, 0);
                assert(err == 0);
-               err = snd_pcm_close(slave->pcm);
-               pthread_mutex_unlock(&slave->mutex);
                pthread_mutex_destroy(&slave->mutex);
+               pthread_cond_destroy(&slave->poll_cond);
                list_del(&slave->list);
                free(slave);
                list_del(&share->list);
        } else {
                list_del(&share->list);
-               pthread_mutex_unlock(&slave->mutex);
+               Pthread_mutex_unlock(&slave->mutex);
        }
-       pthread_mutex_unlock(&slaves_mutex);
+       Pthread_mutex_unlock(&slaves_mutex);
        close(share->client_socket);
        close(share->slave_socket);
        free(share->slave_channels);
@@ -1012,6 +1212,7 @@ snd_pcm_fast_ops_t snd_pcm_share_fast_ops = {
 };
 
 int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
+                      int sformat, int srate,
                       size_t schannels_count,
                       size_t channels_count, int *channels_map,
                       int stream, int mode)
@@ -1060,7 +1261,14 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
                return -ENOMEM;
        }
        err = socketpair(AF_LOCAL, SOCK_STREAM, 0, sd);
-       if (err >= 0 && stream == SND_PCM_STREAM_PLAYBACK) {
+       if (err < 0) {
+               free(pcm);
+               free(share->slave_channels);
+               free(share);
+               return -errno;
+       }
+               
+       if (stream == SND_PCM_STREAM_PLAYBACK) {
                int bufsize = 1;
                err = setsockopt(sd[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
                if (err >= 0) {
@@ -1078,13 +1286,15 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
        }
        if (err < 0) {
                err = -errno;
+               close(sd[0]);
+               close(sd[1]);
                free(pcm);
                free(share->slave_channels);
                free(share);
                return err;
        }
 
-       pthread_mutex_lock(&slaves_mutex);
+       Pthread_mutex_lock(&slaves_mutex);
        for (i = slaves.next; i != &slaves; i = i->next) {
                snd_pcm_share_slave_t *s = list_entry(i, snd_pcm_share_slave_t, list);
                if (s->pcm->name && strcmp(s->pcm->name, sname) == 0) {
@@ -1096,7 +1306,7 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
                snd_pcm_t *spcm;
                err = snd_pcm_open(&spcm, sname, stream, mode);
                if (err < 0) {
-                       pthread_mutex_unlock(&slaves_mutex);
+                       Pthread_mutex_unlock(&slaves_mutex);
                        close(sd[0]);
                        close(sd[1]);
                        free(pcm);
@@ -1106,7 +1316,7 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
                }
                slave = calloc(1, sizeof(*slave));
                if (!slave) {
-                       pthread_mutex_unlock(&slaves_mutex);
+                       Pthread_mutex_unlock(&slaves_mutex);
                        snd_pcm_close(spcm);
                        close(sd[0]);
                        close(sd[1]);
@@ -1118,16 +1328,35 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
                INIT_LIST_HEAD(&slave->clients);
                slave->pcm = spcm;
                slave->channels_count = schannels_count;
+               slave->sformat = sformat;
+               slave->srate = srate;
                pthread_mutex_init(&slave->mutex, NULL);
+               pthread_cond_init(&slave->poll_cond, NULL);
                list_add_tail(&slave->list, &slaves);
+               Pthread_mutex_lock(&slave->mutex);
                err = pthread_create(&slave->thread, NULL, snd_pcm_share_slave_thread, slave);
                assert(err == 0);
+               Pthread_mutex_unlock(&slaves_mutex);
+       } else {
+               Pthread_mutex_lock(&slave->mutex);
+               Pthread_mutex_unlock(&slaves_mutex);
+               for (i = slave->clients.next; i != &slave->clients; i = i->next) {
+                       snd_pcm_share_t *sh = list_entry(i, snd_pcm_share_t, list);
+                       unsigned int k;
+                       for (k = 0; k < sh->channels_count; ++k) {
+                               if (slave_map[sh->slave_channels[k]]) {
+                                       ERR("Slave channel %d is already in use", sh->slave_channels[k]);
+                                       Pthread_mutex_unlock(&slave->mutex);
+                                       close(sd[0]);
+                                       close(sd[1]);
+                                       free(pcm);
+                                       free(share->slave_channels);
+                                       free(share);
+                                       return -EBUSY;
+                               }
+                       }
+               }
        }
-       pthread_mutex_lock(&slave->mutex);
-       pthread_mutex_unlock(&slaves_mutex);
-       slave->open_count++;
-       list_add_tail(&share->list, &slave->clients);
-       pthread_mutex_unlock(&slave->mutex);
 
        share->slave = slave;
        share->pcm = pcm;
@@ -1150,6 +1379,12 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
        pcm->poll_fd = share->client_socket;
        pcm->hw_ptr = &share->hw_ptr;
        pcm->appl_ptr = &share->appl_ptr;
+
+       slave->open_count++;
+       list_add_tail(&share->list, &slave->clients);
+
+       Pthread_mutex_unlock(&slave->mutex);
+
        *pcmp = pcm;
        return 0;
 }
@@ -1166,6 +1401,9 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
        size_t channels_count = 0;
        long schannels_count = -1;
        size_t schannel_max = 0;
+       int sformat = -1;
+       long srate = -1;
+       
        snd_config_foreach(i, conf) {
                snd_config_t *n = snd_config_entry(i);
                if (strcmp(n->id, "comment") == 0)
@@ -1182,6 +1420,20 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
                        }
                        continue;
                }
+               if (strcmp(n->id, "sformat") == 0) {
+                       char *f;
+                       err = snd_config_string_get(n, &f);
+                       if (err < 0) {
+                               ERR("Invalid type for sformat");
+                               return -EINVAL;
+                       }
+                       sformat = snd_pcm_format_value(f);
+                       if (sformat < 0) {
+                               ERR("Unknown format %s", f);
+                               return -EINVAL;
+                       }
+                       continue;
+               }
                if (strcmp(n->id, "schannels") == 0) {
                        err = snd_config_integer_get(n, &schannels_count);
                        if (err < 0) {
@@ -1190,6 +1442,14 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
                        }
                        continue;
                }
+               if (strcmp(n->id, "srate") == 0) {
+                       err = snd_config_integer_get(n, &srate);
+                       if (err < 0) {
+                               ERR("Invalid type for srate");
+                               return -EINVAL;
+                       }
+                       continue;
+               }
                if (strcmp(n->id, "binding") == 0) {
                        if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) {
                                ERR("Invalid type for binding");
@@ -1245,8 +1505,9 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
        }
        if (schannels_count <= 0)
                schannels_count = schannel_max + 1;
-           err = snd_pcm_share_open(pcmp, name, sname, schannels_count,
-                                channels_count, channels_map, stream, mode);
+           err = snd_pcm_share_open(pcmp, name, sname, sformat, srate, 
+                                    schannels_count,
+                                    channels_count, channels_map, stream, mode);
 _free:
        free(channels_map);
        return err;