]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Added support for async. Added error callback (and begun to use it). First implementa...
authorAbramo Bagnara <abramo@alsa-project.org>
Wed, 11 Oct 2000 12:37:27 +0000 (12:37 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Wed, 11 Oct 2000 12:37:27 +0000 (12:37 +0000)
20 files changed:
aserver/aserver.c
include/aserver.h
include/pcm.h
src/pcm/Makefile.am
src/pcm/pcm.c
src/pcm/pcm_adpcm.c
src/pcm/pcm_alaw.c
src/pcm/pcm_client.c
src/pcm/pcm_file.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_plugin.h
src/pcm/pcm_rate.c
src/pcm/pcm_route.c
src/pcm/pcm_share.c [new file with mode: 0644]

index eef9a675c71229e9904fc3e44eeef51b8faf0dad..7f54885e0f450b0f26481b846942af1f7a3f1519 100644 (file)
@@ -353,6 +353,9 @@ int pcm_shm_cmd(client_t *client)
        ctrl->cmd = 0;
        pcm = client->device.pcm.handle;
        switch (cmd) {
+       case SND_PCM_IOCTL_ASYNC:
+               ctrl->result = snd_pcm_async(pcm, ctrl->u.async.sig, ctrl->u.async.pid);
+               break;
        case SND_PCM_IOCTL_INFO:
                ctrl->result = snd_pcm_info(pcm, &ctrl->u.info);
                break;
index 138534f59c4cb58e9b9b9f0b407cf192bbc1ea3a..d48caf52ba85809d52e2089fe8e5d2b0dab3f53e 100644 (file)
 #define SND_PCM_IOCTL_MUNMAP_DATA      _IO ('A', 0xf4)
 #define SND_PCM_IOCTL_MUNMAP_CONTROL   _IO ('A', 0xf5)
 #define SND_PCM_IOCTL_MUNMAP_STATUS    _IO ('A', 0xf6)
-#define SND_PCM_IOCTL_MMAP_FORWARD     _IOW('A', 0xf7, size_t)
+#define SND_PCM_IOCTL_MMAP_FORWARD     _IO ('A', 0xf7)
 #define SND_PCM_IOCTL_AVAIL_UPDATE     _IO ('A', 0xf8)
-#define SND_PCM_IOCTL_CLOSE            _IO ('A', 0xf9)
+#define SND_PCM_IOCTL_ASYNC            _IO ('A', 0xf9)
+#define SND_PCM_IOCTL_CLOSE            _IO ('A', 0xfa)
 
 typedef struct {
        long result;
        int cmd;
        union {
+               struct {
+                       int sig;
+                       pid_t pid;
+               } async;
                snd_pcm_info_t info;
                snd_pcm_params_t params;
                snd_pcm_params_info_t params_info;
index 55c2c7ce653d0eb2187bc14ed88287efbe3fd3ca..1671d9e2195dbd932779457b312b32d82bdab3bb 100644 (file)
@@ -6,6 +6,7 @@
  ****************************************************************************/
 
 #define SND_PCM_NONBLOCK               0x0001
+#define SND_PCM_ASYNC                  0x0002
 
 #ifdef __cplusplus
 extern "C" {
@@ -117,6 +118,7 @@ typedef enum {
        SND_PCM_TYPE_LBSERVER,
 } snd_pcm_type_t;
 
+extern void (*snd_pcm_error)(const char *file, int line, const char *function, const char *fmt, ...);
 
 int snd_pcm_open(snd_pcm_t **handle, char *name, 
                 int stream, int mode);
@@ -136,6 +138,7 @@ snd_pcm_type_t snd_pcm_type(snd_pcm_t *pcm);
 int snd_pcm_close(snd_pcm_t *pcm);
 int snd_pcm_poll_descriptor(snd_pcm_t *pcm);
 int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock);
+int snd_pcm_async(snd_pcm_t *pcm, int sig, pid_t pid);
 int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info);
 int snd_pcm_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info);
 int snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params);
index 82da9063212a1898506acd2537f48761801e064b..e932d5195ee3180c857c58e1a59d56eb6f5b6cec 100644 (file)
@@ -3,7 +3,8 @@ EXTRA_LTLIBRARIES = libpcm.la
 
 libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plugin.c pcm_linear.c pcm_route.c \
                    pcm_mulaw.c pcm_alaw.c pcm_adpcm.c pcm_rate.c pcm_plug.c \
-                   pcm_misc.c pcm_mmap.c pcm_multi.c pcm_client.c pcm_file.c
+                   pcm_misc.c pcm_mmap.c pcm_multi.c pcm_client.c pcm_file.c \
+                   pcm_share.c
 noinst_HEADERS = pcm_local.h pcm_plugin.h
 
 all: libpcm.la
index e40e5e573b94e81e3c59eac6fd06638ce6188897..4288d3d56b78d3ddd9e75ec26c7cefa13fa72deb 100644 (file)
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <malloc.h>
 #include <errno.h>
+#include <stdarg.h>
 #include <sys/ioctl.h>
 #include <sys/poll.h>
 #include <dlfcn.h>
@@ -83,7 +84,7 @@ int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock)
 {
        int err;
        assert(pcm);
-       if ((err = pcm->ops->nonblock(pcm->fast_op_arg, nonblock)) < 0)
+       if ((err = pcm->ops->nonblock(pcm->op_arg, nonblock)) < 0)
                return err;
        if (nonblock)
                pcm->mode |= SND_PCM_NONBLOCK;
@@ -92,6 +93,12 @@ int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock)
        return 0;
 }
 
+int snd_pcm_async(snd_pcm_t *pcm, int sig, pid_t pid)
+{
+       assert(pcm);
+       return pcm->ops->async(pcm->op_arg, sig, pid);
+}
+
 int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
 {
        assert(pcm && info);
@@ -1037,3 +1044,14 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
        return err;
 }
 
+void snd_pcm_error_default(const char *file, int line, const char *function, const char *fmt, ...)
+{
+       va_list arg;
+       va_start(arg, fmt);
+       fprintf(stderr, "ALSA PCM lib %s:%i:(%s) ", file, line, function);
+       vfprintf(stderr, fmt, arg);
+       putc('\n', stderr);
+       va_end(arg);
+}
+
+void (*snd_pcm_error)(const char *file, int line, const char *function, const char *fmt, ...) = snd_pcm_error_default;
index 4c916ab3f0644177c5177fab288e1ae658abe3f6..56d9e76f93edaba330fbcc829ae198dfd9790385 100644 (file)
@@ -544,6 +544,7 @@ struct snd_pcm_ops snd_pcm_adpcm_ops = {
        channel_setup: snd_pcm_plugin_channel_setup,
        dump: snd_pcm_adpcm_dump,
        nonblock: snd_pcm_plugin_nonblock,
+       async: snd_pcm_plugin_async,
        mmap_status: snd_pcm_plugin_mmap_status,
        mmap_control: snd_pcm_plugin_mmap_control,
        mmap_data: snd_pcm_plugin_mmap_data,
index 27f616e675113f3ae9cf14e79628628d99fe4002..7fae5be0727187a1010765f495fd3181aeadb7db 100644 (file)
@@ -412,6 +412,7 @@ struct snd_pcm_ops snd_pcm_alaw_ops = {
        channel_setup: snd_pcm_plugin_channel_setup,
        dump: snd_pcm_alaw_dump,
        nonblock: snd_pcm_plugin_nonblock,
+       async: snd_pcm_plugin_async,
        mmap_status: snd_pcm_plugin_mmap_status,
        mmap_control: snd_pcm_plugin_mmap_control,
        mmap_data: snd_pcm_plugin_mmap_data,
index 2652ab1a6f69f54ffff95691566c406c6fea6fe7..0222891e3690c9d7d2c0db07410e0e2999233503 100644 (file)
@@ -175,6 +175,22 @@ static int snd_pcm_client_shm_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonb
        return 0;
 }
 
+static int snd_pcm_client_async(snd_pcm_t *pcm, int sig, pid_t pid)
+{
+       snd_pcm_client_t *client = pcm->private;
+       snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
+       int err;
+       ctrl->cmd = SND_PCM_IOCTL_ASYNC;
+       ctrl->u.async.sig = sig;
+       if (pid == 0)
+               pid = getpid();
+       ctrl->u.async.pid = pid;
+       err = snd_pcm_client_shm_action(pcm);
+       if (err < 0)
+               return err;
+       return ctrl->result;
+}
+
 static int snd_pcm_client_shm_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
 {
        snd_pcm_client_t *client = pcm->private;
@@ -550,6 +566,7 @@ struct snd_pcm_ops snd_pcm_client_ops = {
        channel_setup: snd_pcm_client_shm_channel_setup,
        dump: snd_pcm_client_dump,
        nonblock: snd_pcm_client_shm_nonblock,
+       async: snd_pcm_client_async,
        mmap_status: snd_pcm_client_shm_mmap_status,
        mmap_control: snd_pcm_client_shm_mmap_control,
        mmap_data: snd_pcm_client_shm_mmap_data,
index 3e7ea2a7f9e59708a34a451635eed3f5f4dc0fde..0391554b24c6aaf5ed08bc5fe2ab2c694f6b876b 100644 (file)
@@ -50,6 +50,12 @@ static int snd_pcm_file_nonblock(snd_pcm_t *pcm, int nonblock)
        return snd_pcm_nonblock(file->slave, nonblock);
 }
 
+static int snd_pcm_file_async(snd_pcm_t *pcm, int sig, pid_t pid)
+{
+       snd_pcm_file_t *file = pcm->private;
+       return snd_pcm_async(file->slave, sig, pid);
+}
+
 static int snd_pcm_file_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
 {
        snd_pcm_file_t *file = pcm->private;
@@ -342,6 +348,7 @@ struct snd_pcm_ops snd_pcm_file_ops = {
        channel_setup: snd_pcm_file_channel_setup,
        dump: snd_pcm_file_dump,
        nonblock: snd_pcm_file_nonblock,
+       async: snd_pcm_file_async,
        mmap_status: snd_pcm_file_mmap_status,
        mmap_control: snd_pcm_file_mmap_control,
        mmap_data: snd_pcm_file_mmap_data,
index 9eb565790abbd9ee9464a67490987a0c1140cc5b..30fd375072b3936643259e4623b36b26b838b28c 100644 (file)
@@ -22,6 +22,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <signal.h>
 #include <string.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/mman.h>
 #include "pcm_local.h"
 
+#ifndef F_SETSIG
+#define F_SETSIG 10
+#endif
+
 typedef struct {
        int fd;
        int card, device, subdevice;
@@ -44,9 +49,10 @@ static int snd_pcm_hw_close(snd_pcm_t *pcm)
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
        free(hw);
-       if (fd >= 0)
-               if (close(fd))
-                       return -errno;
+       if (close(fd)) {
+               ERR("close failed\n");
+               return -errno;
+       }
        return 0;
 }
 
@@ -56,14 +62,53 @@ static int snd_pcm_hw_nonblock(snd_pcm_t *pcm, int nonblock)
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
 
-       if ((flags = fcntl(fd, F_GETFL)) < 0)
+       if ((flags = fcntl(fd, F_GETFL)) < 0) {
+               ERR("F_GETFL failed");
                return -errno;
+       }
        if (nonblock)
                flags |= O_NONBLOCK;
        else
                flags &= ~O_NONBLOCK;
-       if (fcntl(fd, F_SETFL, flags) < 0)
+       if (fcntl(fd, F_SETFL, flags) < 0) {
+               ERR("F_SETFL for O_NONBLOCK failed");
+               return -errno;
+       }
+       return 0;
+}
+
+static int snd_pcm_hw_async(snd_pcm_t *pcm, int sig, pid_t pid)
+{
+       long flags;
+       snd_pcm_hw_t *hw = pcm->private;
+       int fd = hw->fd;
+
+       if ((flags = fcntl(fd, F_GETFL)) < 0) {
+               ERR("F_GETFL failed");
+               return -errno;
+       }
+       if (sig >= 0)
+               flags |= O_ASYNC;
+       else
+               flags &= ~O_ASYNC;
+       if (fcntl(fd, F_SETFL, flags) < 0) {
+               ERR("F_SETFL for O_ASYNC failed");
                return -errno;
+       }
+       if (sig < 0)
+               return 0;
+       if (sig == 0)
+               sig = SIGIO;
+       if (fcntl(fd, F_SETSIG, sig) < 0) {
+               ERR("F_SETSIG failed");
+               return -errno;
+       }
+       if (pid == 0)
+               pid = getpid();
+       if (fcntl(fd, F_SETOWN, pid) < 0) {
+               ERR("F_SETOWN failed");
+               return -errno;
+       }
        return 0;
 }
 
@@ -71,8 +116,10 @@ static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_INFO, info) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_INFO, info) < 0) {
+               ERR("SND_PCM_IOCTL_INFO failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -80,8 +127,10 @@ static int snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_PARAMS_INFO, info) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_PARAMS_INFO, info) < 0) {
+               ERR("SND_PCM_IOCTL_PARAMS_INFO failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -89,8 +138,10 @@ static int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_PARAMS, params) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_PARAMS, params) < 0) {
+               ERR("SND_PCM_IOCTL_PARAMS failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -98,8 +149,10 @@ static int snd_pcm_hw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_SETUP, setup) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_SETUP, setup) < 0) {
+               ERR("SND_PCM_IOCTL_SETUP failed");
                return -errno;
+       }
        if (setup->mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
                if (setup->xfer_mode == SND_PCM_XFER_INTERLEAVED)
                        setup->mmap_shape = SND_PCM_MMAP_INTERLEAVED;
@@ -115,8 +168,10 @@ static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0) {
+               ERR("SND_PCM_IOCTL_CHANNEL_INFO failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -124,8 +179,10 @@ static int snd_pcm_hw_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0) {
+               ERR("SND_PCM_IOCTL_CHANNEL_PARAMS failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -133,8 +190,10 @@ static int snd_pcm_hw_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * se
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0) {
+               ERR("SND_PCM_IOCTL_CHANNEL_SETUP failed");
                return -errno;
+       }
        if (hw->mmap_emulation) {
                if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) {
                        setup->running_area.addr = pcm->mmap_data;
@@ -157,8 +216,10 @@ static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_STATUS, status) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_STATUS, status) < 0) {
+               ERR("SND_PCM_IOCTL_STATUS failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -171,8 +232,10 @@ static int snd_pcm_hw_delay(snd_pcm_t *pcm, ssize_t *delayp)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_DELAY, delayp) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_DELAY, delayp) < 0) {
+               ERR("SND_PCM_IOCTL_DELAY failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -180,8 +243,10 @@ static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_PREPARE) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_PREPARE) < 0) {
+               ERR("SND_PCM_IOCTL_PREPARE failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -189,8 +254,10 @@ static int snd_pcm_hw_start(snd_pcm_t *pcm)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_START) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_START) < 0) {
+               ERR("SND_PCM_IOCTL_START failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -198,8 +265,10 @@ static int snd_pcm_hw_drop(snd_pcm_t *pcm)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_DROP) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_DROP) < 0) {
+               ERR("SND_PCM_IOCTL_DROP failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -207,8 +276,10 @@ static int snd_pcm_hw_drain(snd_pcm_t *pcm)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_DRAIN) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_DRAIN) < 0) {
+               ERR("SND_PCM_IOCTL_DRAIN failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -216,8 +287,10 @@ static int snd_pcm_hw_pause(snd_pcm_t *pcm, int enable)
 {
        snd_pcm_hw_t *hw = pcm->private;
        int fd = hw->fd;
-       if (ioctl(fd, SND_PCM_IOCTL_PAUSE, enable) < 0)
+       if (ioctl(fd, SND_PCM_IOCTL_PAUSE, enable) < 0) {
+               ERR("SND_PCM_IOCTL_PAUSE failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -301,8 +374,10 @@ static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm)
        void *ptr;
        ptr = mmap(NULL, sizeof(snd_pcm_mmap_status_t), PROT_READ, MAP_FILE|MAP_SHARED, 
                   hw->fd, SND_PCM_MMAP_OFFSET_STATUS);
-       if (ptr == MAP_FAILED || ptr == NULL)
+       if (ptr == MAP_FAILED || ptr == NULL) {
+               ERR("status mmap failed");
                return -errno;
+       }
        pcm->mmap_status = ptr;
        return 0;
 }
@@ -313,8 +388,10 @@ static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm)
        void *ptr;
        ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, 
                   hw->fd, SND_PCM_MMAP_OFFSET_CONTROL);
-       if (ptr == MAP_FAILED || ptr == NULL)
+       if (ptr == MAP_FAILED || ptr == NULL) {
+               ERR("control mmap failed");
                return -errno;
+       }
        pcm->mmap_control = ptr;
        return 0;
 }
@@ -333,8 +410,10 @@ static int snd_pcm_hw_mmap_data(snd_pcm_t *pcm)
                ptr = mmap(NULL, pcm->setup.mmap_bytes,
                           prot, MAP_FILE|MAP_SHARED, 
                           hw->fd, SND_PCM_MMAP_OFFSET_DATA);
-               if (ptr == MAP_FAILED || ptr == NULL)
+               if (ptr == MAP_FAILED || ptr == NULL) {
+                       ERR("data mmap failed");
                        return -errno;
+               }
        }
        pcm->mmap_data = ptr;
        return 0;
@@ -342,15 +421,19 @@ static int snd_pcm_hw_mmap_data(snd_pcm_t *pcm)
 
 static int snd_pcm_hw_munmap_status(snd_pcm_t *pcm)
 {
-       if (munmap((void*)pcm->mmap_status, sizeof(*pcm->mmap_status)) < 0)
+       if (munmap((void*)pcm->mmap_status, sizeof(*pcm->mmap_status)) < 0) {
+               ERR("status munmap failed");
                return -errno;
+       }
        return 0;
 }
 
 static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm)
 {
-       if (munmap(pcm->mmap_control, sizeof(*pcm->mmap_control)) < 0)
+       if (munmap(pcm->mmap_control, sizeof(*pcm->mmap_control)) < 0) {
+               ERR("control munmap failed");
                return -errno;
+       }
        return 0;
 }
 
@@ -360,8 +443,10 @@ static int snd_pcm_hw_munmap_data(snd_pcm_t *pcm)
        if (hw->mmap_emulation)
                free(pcm->mmap_data);
        else
-               if (munmap(pcm->mmap_data, pcm->setup.mmap_bytes) < 0)
+               if (munmap(pcm->mmap_data, pcm->setup.mmap_bytes) < 0) {
+                       ERR("data munmap failed");
                        return -errno;
+               }
        return 0;
 }
 
@@ -377,15 +462,14 @@ static ssize_t snd_pcm_hw_mmap_forward(snd_pcm_t *pcm, size_t size)
 static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
 {
        snd_pcm_hw_t *hw = pcm->private;
-       int fd = hw->fd;
        size_t avail;
        ssize_t err;
        if (pcm->setup.ready_mode == SND_PCM_READY_ASAP ||
            pcm->setup.xrun_mode == SND_PCM_XRUN_ASAP) {
                ssize_t d;
-               int err = ioctl(fd, SND_PCM_IOCTL_DELAY, &d);
+               int err = snd_pcm_hw_delay(pcm, &d);
                if (err < 0)
-                       return -errno;
+                       return err;
        }
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
                avail = snd_pcm_mmap_playback_avail(pcm);
@@ -441,6 +525,7 @@ struct snd_pcm_ops snd_pcm_hw_ops = {
        channel_setup: snd_pcm_hw_channel_setup,
        dump: snd_pcm_hw_dump,
        nonblock: snd_pcm_hw_nonblock,
+       async: snd_pcm_hw_async,
        mmap_status: snd_pcm_hw_mmap_status,
        mmap_control: snd_pcm_hw_mmap_control,
        mmap_data: snd_pcm_hw_mmap_data,
@@ -509,6 +594,8 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub
        fmode = O_RDWR;
        if (mode & SND_PCM_NONBLOCK)
                fmode |= O_NONBLOCK;
+       if (mode & SND_PCM_ASYNC)
+               fmode |= O_ASYNC;
        if ((fd = open(filename, fmode)) < 0) {
                ret = -errno;
                goto __end;
index b335445e391b7aa4b7b1c79be3ffd8ea6602a872..e92e1cca3d22bfdf44fcaf7e342e3acb9829ce56 100644 (file)
@@ -255,6 +255,7 @@ struct snd_pcm_ops snd_pcm_linear_ops = {
        channel_setup: snd_pcm_plugin_channel_setup,
        dump: snd_pcm_linear_dump,
        nonblock: snd_pcm_plugin_nonblock,
+       async: snd_pcm_plugin_async,
        mmap_status: snd_pcm_plugin_mmap_status,
        mmap_control: snd_pcm_plugin_mmap_control,
        mmap_data: snd_pcm_plugin_mmap_data,
index 7772af60c1d4feb181a7f719d6814689c7996f5f..970578c949be3ffdd21fa6ef183e508e66d7dab9 100644 (file)
 #include <errno.h>
 #include "asoundlib.h"
 
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define ERR(...) snd_pcm_error(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
+#else
+#define ERR(args...) snd_pcm_error(__FILE__, __LINE__, __FUNCTION__, ##args)
+#endif
+
 struct snd_pcm_ops {
        int (*close)(snd_pcm_t *pcm);
        int (*nonblock)(snd_pcm_t *pcm, int nonblock);
+       int (*async)(snd_pcm_t *pcm, int sig, pid_t pid);
        int (*info)(snd_pcm_t *pcm, snd_pcm_info_t *info);
        int (*params_info)(snd_pcm_t *pcm, snd_pcm_params_info_t *info);
        int (*params)(snd_pcm_t *pcm, snd_pcm_params_t *params);
index 65448586b040663a5a97e95fe68aa8cecb9a292e..ba6dc888c4083ae41bdd4781f9c3e47ff6aa7878 100644 (file)
@@ -429,6 +429,7 @@ struct snd_pcm_ops snd_pcm_mulaw_ops = {
        channel_setup: snd_pcm_plugin_channel_setup,
        dump: snd_pcm_mulaw_dump,
        nonblock: snd_pcm_plugin_nonblock,
+       async: snd_pcm_plugin_async,
        mmap_status: snd_pcm_plugin_mmap_status,
        mmap_control: snd_pcm_plugin_mmap_control,
        mmap_data: snd_pcm_plugin_mmap_data,
index 9e5d7ab5f9dac7e48edfe3646735e87906d47ef9..c317c112db3ae047d939897ab0ae0b1f76aeeb23 100644 (file)
@@ -72,6 +72,13 @@ static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock
        return 0;
 }
 
+static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid)
+{
+       snd_pcm_multi_t *multi = pcm->private;
+       snd_pcm_t *slave_0 = multi->slaves[0].pcm;
+       return snd_pcm_async(slave_0, sig, pid);
+}
+
 static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
 {
        snd_pcm_multi_t *multi = pcm->private;
@@ -504,6 +511,7 @@ struct snd_pcm_ops snd_pcm_multi_ops = {
        channel_setup: snd_pcm_multi_channel_setup,
        dump: snd_pcm_multi_dump,
        nonblock: snd_pcm_multi_nonblock,
+       async: snd_pcm_multi_async,
        mmap_status: snd_pcm_multi_mmap_status,
        mmap_control: snd_pcm_multi_mmap_control,
        mmap_data: snd_pcm_multi_mmap_data,
index fd6ef7c7bae4df4dd4e552292a1809d575a77fec..36ea8930bb2b469fdc22f00ee920f6261cab38c2 100644 (file)
@@ -189,6 +189,12 @@ static int snd_pcm_plug_nonblock(snd_pcm_t *pcm, int nonblock)
        return snd_pcm_nonblock(plug->slave, nonblock);
 }
 
+static int snd_pcm_plug_async(snd_pcm_t *pcm, int sig, pid_t pid)
+{
+       snd_pcm_plug_t *plug = pcm->private;
+       return snd_pcm_async(plug->slave, sig, pid);
+}
+
 static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
 {
        snd_pcm_plug_t *plug = pcm->private;
@@ -658,6 +664,7 @@ struct snd_pcm_ops snd_pcm_plug_ops = {
        channel_setup: snd_pcm_plug_channel_setup,
        dump: snd_pcm_plug_dump,
        nonblock: snd_pcm_plug_nonblock,
+       async: snd_pcm_plug_async,
        mmap_status: snd_pcm_plug_mmap_status,
        mmap_control: snd_pcm_plug_mmap_control,
        mmap_data: snd_pcm_plug_mmap_data,
index 6a8630569b02313f59f48e8e3934bb47b0e772e4..7fb5a0b40cdcee5f97f7bfae50f82c7855034523 100644 (file)
@@ -39,6 +39,12 @@ int snd_pcm_plugin_nonblock(snd_pcm_t *pcm, int nonblock)
        return snd_pcm_nonblock(plugin->slave, nonblock);
 }
 
+int snd_pcm_plugin_async(snd_pcm_t *pcm, int sig, pid_t pid)
+{
+       snd_pcm_plugin_t *plugin = pcm->private;
+       return snd_pcm_async(plugin->slave, sig, pid);
+}
+
 int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
 {
        snd_pcm_plugin_t *plugin = pcm->private;
index 25de6d2a5959b1d666553ff5f2323b701be085e9..e86b1d06d7175821c15bca1184319da6a6b6cb38 100644 (file)
@@ -32,6 +32,7 @@ typedef struct {
 
 int snd_pcm_plugin_close(snd_pcm_t *pcm);
 int snd_pcm_plugin_nonblock(snd_pcm_t *pcm, int nonblock);
+int snd_pcm_plugin_async(snd_pcm_t *pcm, int sig, pid_t pid);
 int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_info_t * info);
 int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info);
 int snd_pcm_plugin_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params);
index 9606c2747846303525573995483198269af93a64..43e5ee87911b292046d29bb16fbd062bb50a16cd 100644 (file)
@@ -576,6 +576,7 @@ struct snd_pcm_ops snd_pcm_rate_ops = {
        channel_setup: snd_pcm_plugin_channel_setup,
        dump: snd_pcm_rate_dump,
        nonblock: snd_pcm_plugin_nonblock,
+       async: snd_pcm_plugin_async,
        mmap_status: snd_pcm_plugin_mmap_status,
        mmap_control: snd_pcm_plugin_mmap_control,
        mmap_data: snd_pcm_plugin_mmap_data,
index 341e3be64e9e7c59da1beb0fa355a40942eb4b75..347d83e028ea280a25272a11648ad8c0a0105bf9 100644 (file)
@@ -695,6 +695,7 @@ struct snd_pcm_ops snd_pcm_route_ops = {
        channel_setup: snd_pcm_route_channel_setup,
        dump: snd_pcm_route_dump,
        nonblock: snd_pcm_plugin_nonblock,
+       async: snd_pcm_plugin_async,
        mmap_status: snd_pcm_plugin_mmap_status,
        mmap_control: snd_pcm_plugin_mmap_control,
        mmap_data: snd_pcm_plugin_mmap_data,
diff --git a/src/pcm/pcm_share.c b/src/pcm/pcm_share.c
new file mode 100644 (file)
index 0000000..0b3e50f
--- /dev/null
@@ -0,0 +1,1155 @@
+/*
+ *  PCM - Share
+ *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License as
+ *   published by the Free Software Foundation; either version 2 of
+ *   the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+  
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <math.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <pthread.h>
+#include "pcm_local.h"
+#include "list.h"
+
+static LIST_HEAD(slaves);
+static pthread_mutex_t slaves_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+typedef struct {
+       struct list_head clients;
+       struct list_head list;
+       snd_pcm_t *pcm;
+       size_t channels_count;
+       size_t open_count;
+       size_t setup_count;
+       size_t prepared_count;
+       size_t running_count;
+       size_t safety_threshold;
+       pthread_t thread;
+       pthread_mutex_t mutex;
+} snd_pcm_share_slave_t;
+
+typedef struct {
+       struct list_head list;
+       snd_pcm_t *pcm;
+       snd_pcm_share_slave_t *slave;
+       size_t channels_count;
+       int *slave_channels;
+       int xfer_mode;
+       int xrun_mode;
+       int async_sig;
+       pid_t async_pid;
+       struct timeval trigger_time;
+       size_t draining_silence;
+       snd_pcm_mmap_control_t mmap_control;
+       snd_pcm_mmap_status_t mmap_status;
+       int ready;
+       int client_socket;
+       int slave_socket;
+       void *stopped_data;
+} snd_pcm_share_t;
+
+
+static void snd_pcm_share_interrupt(snd_pcm_share_slave_t *slave)
+{
+       struct list_head *i;
+       pthread_mutex_lock(&slave->mutex);
+       /* Update poll status */
+       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;
+               int ready;
+               switch (share->mmap_status.state) {
+               case SND_PCM_STATE_DRAINING:
+                       if (pcm->stream == SND_PCM_STREAM_CAPTURE)
+                               ready = 1;
+                       else {
+                               if (pcm->mode & SND_PCM_ASYNC)
+                                       kill(share->async_pid, share->async_sig);
+                               ready = 0;
+                       }
+                       break;
+               case SND_PCM_STATE_RUNNING:
+                       if (pcm->mode & SND_PCM_ASYNC)
+                               kill(share->async_pid, share->async_sig);
+                       ready = (snd_pcm_mmap_avail(pcm) >= pcm->setup.avail_min);
+                       break;
+               default:
+                       ready = 1;
+               }
+               if (ready != share->ready) {
+                       char buf[1];
+                       if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+                               if (ready)
+                                       read(share->slave_socket, buf, 1);
+                               else
+                                       write(share->client_socket, buf, 1);
+                       } else {
+                               if (ready)
+                                       write(share->slave_socket, buf, 1);
+                               else
+                                       read(share->client_socket, buf, 1);
+                       }
+                       share->ready = ready;
+               }
+       }
+       pthread_mutex_unlock(&slave->mutex);
+}
+
+
+void sigio_handler(int sig ATTRIBUTE_UNUSED)
+{
+}
+
+void *snd_pcm_share_slave_thread(void *data)
+{
+       snd_pcm_share_slave_t *slave = data;
+       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) {
+               pause();
+               snd_pcm_share_interrupt(slave);
+       }
+       return NULL;
+}
+
+/* Warning: take the mutex before to call this */
+static void snd_pcm_share_stop(snd_pcm_t *pcm, int state)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       share->mmap_status.state = state;
+       gettimeofday(&share->trigger_time, 0);
+       slave->prepared_count--;
+       slave->running_count--;
+       if (slave->running_count == 0) {
+               int err = snd_pcm_drop(slave->pcm);
+               assert(err >= 0);
+       }
+       if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
+               snd_pcm_areas_copy(pcm->running_areas, 0,
+                                  pcm->stopped_areas, 0,
+                                  pcm->setup.format.channels, pcm->setup.buffer_size,
+                                  pcm->setup.format.sfmt);
+       }
+       share->mmap_status.hw_ptr = slave->pcm->mmap_status->hw_ptr;
+       pcm->mmap_status = &share->mmap_status;
+}
+
+/* Warning: take the mutex before to call this */
+static void 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;
+       size_t min_frames, max_frames;
+       ssize_t avail;
+       int err;
+       avail = snd_pcm_avail_update(slave->pcm);
+       if (avail == 0)
+               return;
+       assert(avail > 0);
+       boundary = slave->pcm->setup.boundary;
+       buffer_size = slave->pcm->setup.buffer_size;
+       min_frames = buffer_size;
+       max_frames = 0;
+       slave_appl_ptr = slave->pcm->mmap_control->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->mmap_status.state) {
+               case SND_PCM_STATE_RUNNING:
+                       break;
+               case SND_PCM_STATE_DRAINING:
+               {
+                       size_t a, offset;
+                       if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
+                               continue;
+                       a = snd_pcm_mmap_avail(pcm);
+                       frames = a - share->draining_silence;
+                       offset = snd_pcm_mmap_offset(pcm);
+                       offset += share->draining_silence;
+                       if (offset >= buffer_size)
+                               offset -= buffer_size;
+                       while (frames > 0) {
+                               size_t f = buffer_size - offset;
+                               if (f > (size_t) frames)
+                                       f = frames;
+                               snd_pcm_areas_silence(pcm->running_areas, offset,
+                                                     pcm->setup.format.channels, 
+                                                     f, pcm->setup.format.sfmt);
+                               offset += f;
+                               if (offset == buffer_size)
+                                       offset = 0;
+                               frames -= f;
+                       }
+                       share->draining_silence = a;
+                       if (a == buffer_size)
+                               snd_pcm_share_stop(pcm, SND_PCM_STATE_SETUP);
+                       break;
+               }
+               default:
+                       continue;
+               }
+               frames = share->mmap_control.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) {
+                       if (snd_pcm_mmap_hw_avail(pcm) <= 0 &&
+                           pcm->setup.xrun_mode != SND_PCM_XRUN_NONE)
+                               snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
+                       continue;
+               }
+               if ((size_t)frames < min_frames)
+                       min_frames = frames;
+               if ((size_t)frames > max_frames)
+                       max_frames = frames;
+       }
+       if (max_frames == 0)
+               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);
+       }
+}
+
+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;
+       if (share->mmap_status.state == SND_PCM_STATE_RUNNING) {
+               if (pcm->mode & SND_PCM_NONBLOCK)
+                       snd_pcm_drop(pcm);
+               else
+                       snd_pcm_drain(pcm);
+       }
+       pthread_mutex_lock(&slave->mutex);
+       if (pcm->valid_setup)
+               slave->setup_count--;
+       slave->open_count--;
+       if (slave->open_count == 0) {
+               pthread_kill(slave->thread, SIGTERM);
+               err = snd_pcm_close(slave->pcm);
+               list_del(&slave->list);
+               pthread_mutex_destroy(&slave->mutex);
+               free(slave);
+       }
+       list_del(&share->list);
+       pthread_mutex_unlock(&slave->mutex);
+       close(share->client_socket);
+       close(share->slave_socket);
+       free(share->slave_channels);
+       free(share);
+       return err;
+}
+
+static int snd_pcm_share_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
+{
+       return 0;
+}
+
+static int snd_pcm_share_async(snd_pcm_t *pcm, int sig, pid_t pid)
+{
+       snd_pcm_share_t *share = pcm->private;
+       if (sig)
+               share->async_sig = sig;
+       else
+               share->async_sig = SIGIO;
+       if (pid)
+               share->async_pid = pid;
+       else
+               share->async_pid = getpid();
+       return -ENOSYS;
+}
+
+static int snd_pcm_share_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
+{
+       snd_pcm_share_t *share = pcm->private;
+       return snd_pcm_info(share->slave->pcm, info);
+}
+
+static int snd_pcm_share_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       int err = 0;
+       unsigned int req_mask = info->req_mask;
+       unsigned int channels = info->req.format.channels;
+       if ((req_mask & SND_PCM_PARAMS_CHANNELS) &&
+           channels != share->channels_count) {
+               info->req.fail_mask |= SND_PCM_PARAMS_CHANNELS;
+               info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+               return -EINVAL;
+       }
+       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;
+       if (slave->setup_count > 1 || 
+           (slave->setup_count == 1 && !pcm->valid_setup)) {
+               snd_pcm_setup_t *s = &slave->pcm->setup;
+               if ((req_mask & SND_PCM_PARAMS_SFMT) &&
+                   info->req.format.sfmt != s->format.sfmt) {
+                       info->req.fail_mask |= SND_PCM_PARAMS_SFMT;
+                       info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+                       return -EINVAL;
+               }
+               info->formats = 1 << s->format.sfmt;
+               info->rates = SND_PCM_RATE_CONTINUOUS;
+               info->min_rate = info->max_rate = s->format.rate;
+               info->buffer_size = s->buffer_size;
+               info->min_fragment_size = info->max_fragment_size = s->frag_size;
+               info->min_fragments = info->max_fragments = s->frags;
+               info->fragment_align = s->frag_size;
+               info->req.fail_mask = 0;
+       }
+
+       info->min_channels = info->max_channels = share->channels_count;
+       if (info->flags & SND_PCM_INFO_INTERLEAVED) {
+               info->flags &= ~SND_PCM_INFO_INTERLEAVED;
+               info->flags |= SND_PCM_INFO_COMPLEX;
+       }
+       return err;
+}
+
+static int snd_pcm_share_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       unsigned int channels = params->format.channels;
+       int err = 0;
+       if (channels != share->channels_count) {
+               params->fail_mask = SND_PCM_PARAMS_CHANNELS;
+               params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+               ERR("channels requested (%d) differs from configuration (%d)", channels, share->channels_count);
+               return -EINVAL;
+       }
+       share->xfer_mode = params->xfer_mode;
+       share->xrun_mode = params->xrun_mode;
+       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) {
+                       ERR("slave is already running with different format");
+                       params->fail_mask |= SND_PCM_PARAMS_SFMT;
+               }
+               if (params->fail_mask) {
+                       params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+                       err = -EINVAL;
+                       goto _end;
+               }
+       } else {
+               snd_pcm_params_t sp = *params;
+               if (slave->pcm->mmap_data) {
+                       err = snd_pcm_munmap_data(slave->pcm);
+                       if (err < 0)
+                               goto _end;
+               }
+               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);
+               snd_pcm_mmap_data(slave->pcm, NULL);
+               if (err < 0)
+                       goto _end;
+               if (slave->pcm->stream == SND_PCM_STREAM_PLAYBACK)
+                       snd_pcm_areas_silence(slave->pcm->running_areas, 0, slave->pcm->setup.format.channels, slave->pcm->setup.buffer_size, slave->pcm->setup.format.sfmt);
+       }
+       share->mmap_status.state = SND_PCM_STATE_SETUP;
+       slave->setup_count++;
+ _end:
+       pthread_mutex_unlock(&slave->mutex);
+       return err;
+}
+
+static int snd_pcm_share_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       int err;
+       err = snd_pcm_setup(slave->pcm, setup);
+       if (err < 0)
+               return err;
+       setup->xrun_mode = share->xrun_mode;
+       setup->format.channels = share->channels_count;
+       if (share->xfer_mode == SND_PCM_XFER_UNSPECIFIED)
+               setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
+       else
+               setup->xfer_mode = share->xfer_mode;
+       if (setup->mmap_shape != SND_PCM_MMAP_INTERLEAVED)
+               setup->mmap_shape = SND_PCM_MMAP_COMPLEX;
+       return 0;
+}
+
+static int snd_pcm_share_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       int err = 0;
+       ssize_t sd = 0, d = 0;
+       pthread_mutex_lock(&slave->mutex);
+       if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+               status->avail = snd_pcm_mmap_playback_avail(pcm);
+               if (share->mmap_status.state != SND_PCM_STATE_RUNNING &&
+                   share->mmap_status.state != SND_PCM_STATE_DRAINING)
+                       goto _notrunning;
+               d = pcm->setup.buffer_size - status->avail;
+       } else {
+               status->avail = snd_pcm_mmap_capture_avail(pcm);
+               if (share->mmap_status.state != SND_PCM_STATE_RUNNING)
+                       goto _notrunning;
+               d = status->avail;
+       }
+       err = snd_pcm_delay(slave->pcm, &sd);
+       if (err < 0)
+               goto _end;
+ _notrunning:
+       status->delay = sd + d;
+       status->state = share->mmap_status.state;
+       status->trigger_time = share->trigger_time;
+ _end:
+       pthread_mutex_unlock(&slave->mutex);
+       return err;
+}
+
+static int snd_pcm_share_state(snd_pcm_t *pcm)
+{
+       snd_pcm_share_t *share = pcm->private;
+       return share->mmap_status.state;
+}
+
+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 = 0;
+       ssize_t sd;
+       pthread_mutex_lock(&slave->mutex);
+       switch (share->mmap_status.state) {
+       case SND_PCM_STATE_XRUN:
+               err = -EPIPE;
+               goto _end;
+       case SND_PCM_STATE_RUNNING:
+               break;
+       case SND_PCM_STATE_DRAINING:
+               if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
+                       break;
+               /* Fall through */
+       default:
+               err = -EBADFD;
+               goto _end;
+       }
+       err = snd_pcm_delay(slave->pcm, &sd);
+       if (err < 0)
+               goto _end;
+       *delayp = sd + snd_pcm_mmap_delay(pcm);
+ _end:
+       pthread_mutex_unlock(&slave->mutex);
+       return 0;
+}
+
+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 (ret == -EPIPE) {
+               struct list_head *i;
+               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;
+                       if (share->mmap_status.state == SND_PCM_STATE_RUNNING &&
+                           pcm->setup.xrun_mode != SND_PCM_XRUN_NONE)
+                               snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
+               }
+       }
+       pthread_mutex_unlock(&slave->mutex);
+       if (ret >= 0) {
+               ret = snd_pcm_mmap_avail(pcm);
+               if ((size_t)ret > pcm->setup.buffer_size)
+                       return -EPIPE;
+       }
+       return ret;
+}
+
+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 = 0;
+       ssize_t frames;
+       pthread_mutex_lock(&slave->mutex);
+       if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
+           share->mmap_status.state == SND_PCM_STATE_RUNNING) {
+               frames = slave->pcm->mmap_control->appl_ptr - share->mmap_control.appl_ptr;
+               if (frames > (ssize_t)pcm->setup.buffer_size)
+                       frames -= pcm->setup.boundary;
+               else if (frames < -(ssize_t)pcm->setup.buffer_size)
+                       frames += pcm->setup.boundary;
+               if (frames > 0) {
+                       /* Latecomer PCM */
+                       ret = snd_pcm_rewind(slave->pcm, frames);
+                       if (ret < 0) {
+                               pthread_mutex_unlock(&slave->mutex);
+                               return ret;
+                       }
+                       size += ret;
+               }
+       }
+       snd_pcm_mmap_appl_forward(pcm, size);
+       snd_pcm_share_slave_forward(share->slave);
+       pthread_mutex_unlock(&slave->mutex);
+       return size;
+}
+
+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);
+       if (slave->prepared_count == 0) {
+               err = snd_pcm_prepare(slave->pcm);
+               if (err < 0)
+                       goto _end;
+       }
+       slave->prepared_count++;
+       share->mmap_status.hw_ptr = 0;
+       share->mmap_control.appl_ptr = 0;
+       share->mmap_status.state = SND_PCM_STATE_PREPARED;
+ _end:
+       pthread_mutex_unlock(&slave->mutex);
+       return err;
+}
+
+static int snd_pcm_share_start(snd_pcm_t *pcm)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       int err = 0;
+       if (share->mmap_status.state != SND_PCM_STATE_PREPARED)
+               return -EBADFD;
+       pthread_mutex_lock(&slave->mutex);
+       share->mmap_status.state = SND_PCM_STATE_RUNNING;
+       if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+               size_t hw_avail = snd_pcm_mmap_playback_hw_avail(pcm);
+               if (hw_avail == 0) {
+                       err = -EPIPE;
+                       goto _end;
+               }
+               if (slave->running_count) {
+                       ssize_t sd;
+                       err = snd_pcm_delay(slave->pcm, &sd);
+                       if (err < 0)
+                               goto _end;
+                       err = snd_pcm_rewind(slave->pcm, sd);
+                       if (err < 0)
+                               goto _end;
+               }
+               assert(share->mmap_status.hw_ptr == 0);
+               /* Use the same mmap_status of slave */
+               pcm->mmap_status = slave->pcm->mmap_status;
+               share->mmap_control.appl_ptr = slave->pcm->mmap_control->appl_ptr;
+               snd_pcm_areas_copy(pcm->stopped_areas, 0,
+                                  pcm->running_areas, snd_pcm_mmap_offset(pcm),
+                                  pcm->setup.format.channels, hw_avail,
+                                  pcm->setup.format.sfmt);
+               snd_pcm_mmap_forward(pcm, pcm->setup.buffer_size);
+       }
+       if (slave->running_count == 0) {
+               err = snd_pcm_start(slave->pcm);
+               if (err < 0)
+                       goto _end;
+       }
+       slave->running_count++;
+       gettimeofday(&share->trigger_time, 0);
+ _end:
+       pthread_mutex_unlock(&slave->mutex);
+       return err;
+}
+
+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);
+       switch (share->mmap_status.state) {
+       case SND_PCM_STATE_OPEN:
+               err = -EBADFD;
+               goto _end;
+       case SND_PCM_STATE_SETUP:
+               goto _end;
+       case SND_PCM_STATE_DRAINING:
+               if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
+                       share->mmap_status.state = SND_PCM_STATE_SETUP;
+                       break;
+               }
+               /* Fall through */
+       case SND_PCM_STATE_RUNNING:
+               snd_pcm_share_stop(pcm, SND_PCM_STATE_SETUP);
+               break;
+       case SND_PCM_STATE_PREPARED:
+       case SND_PCM_STATE_XRUN:
+               share->mmap_status.state = SND_PCM_STATE_SETUP;
+               break;
+       }
+       
+       if (slave->running_count > 0 &&
+           pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+               ssize_t delay;
+               err = snd_pcm_delay(pcm, &delay);
+               if (err < 0)
+                       goto _end;
+               if (delay > 0) {
+                       err = snd_pcm_rewind(pcm, delay);
+                       if (err < 0)
+                               goto _end;
+               }
+               snd_pcm_areas_silence(pcm->running_areas, 0, pcm->setup.format.channels,
+                                     pcm->setup.buffer_size, pcm->setup.format.sfmt);
+               snd_pcm_mmap_forward(pcm, pcm->setup.buffer_size);
+       }
+       share->mmap_control.appl_ptr = share->mmap_status.hw_ptr = 0;
+ _end:
+       pthread_mutex_unlock(&slave->mutex);
+       return err;
+}
+
+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);
+       switch (share->mmap_status.state) {
+       case SND_PCM_STATE_OPEN:
+               err = -EBADFD;
+               goto _end;
+       case SND_PCM_STATE_PREPARED:
+               share->mmap_status.state = SND_PCM_STATE_SETUP;
+               break;
+       case SND_PCM_STATE_SETUP:
+       case SND_PCM_STATE_DRAINING:
+               goto _end;
+       case SND_PCM_STATE_XRUN:
+               if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+                       share->mmap_status.state = SND_PCM_STATE_SETUP;
+                       break;
+               }
+               /* Fall through */
+       case SND_PCM_STATE_RUNNING:
+               if (snd_pcm_mmap_avail(pcm) <= 0) {
+                       share->mmap_status.state = SND_PCM_STATE_SETUP;
+                       break;
+               }
+               share->draining_silence = 0;
+               share->mmap_status.state = SND_PCM_STATE_DRAINING;
+               break;
+       }
+ _end:
+       pthread_mutex_unlock(&slave->mutex);
+       return err;
+}
+
+static int snd_pcm_share_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
+{
+       return -ENOSYS;
+}
+
+static int snd_pcm_share_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       unsigned int channel = info->channel;
+       int c = share->slave_channels[channel];
+       int err;
+       info->channel = c;
+       err = snd_pcm_channel_info(slave->pcm, info);
+       info->channel = channel;
+       return err;
+}
+
+static int snd_pcm_share_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       unsigned int channel = params->channel;
+       int c = share->slave_channels[channel];
+       int err;
+       params->channel = c;
+       err = snd_pcm_channel_params(slave->pcm, params);
+       params->channel = channel;
+       return err;
+}
+
+static int snd_pcm_share_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       unsigned int channel = setup->channel;
+       int c = share->slave_channels[channel];
+       int err;
+       setup->channel = c;
+       err = snd_pcm_channel_setup(slave->pcm, setup);
+       setup->channel = channel;
+       return err;
+}
+
+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;
+       int ret = -EBADFD;
+       ssize_t n;
+       switch (share->mmap_status.state) {
+       case SND_PCM_STATE_RUNNING:
+               break;
+       case SND_PCM_STATE_PREPARED:
+               if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
+                       return -EBADFD;
+               break;
+       case SND_PCM_STATE_DRAINING:
+               if (pcm->stream != SND_PCM_STREAM_CAPTURE)
+                       return -EBADFD;
+               break;
+       case SND_PCM_STATE_XRUN:
+               return -EPIPE;
+       default:
+               goto _err;
+       }
+       n = snd_pcm_mmap_hw_avail(pcm);
+       assert(n >= 0);
+       if (n > 0) {
+               if ((size_t)n > frames)
+                       n = frames;
+               frames -= n;
+       }
+       if (share->mmap_status.state == SND_PCM_STATE_RUNNING &&
+           frames > 0) {
+               pthread_mutex_lock(&slave->mutex);
+               ret = snd_pcm_rewind(slave->pcm, frames);
+               pthread_mutex_unlock(&slave->mutex);
+               if (ret < 0) {
+                       if (n <= 0)
+                               return ret;
+                       goto _end;
+               }
+               n += ret;
+       }
+ _end:
+       snd_pcm_mmap_appl_backward(pcm, n);
+       ret = n;
+ _err:
+       return ret;
+}
+
+static int snd_pcm_share_mmap_status(snd_pcm_t *pcm)
+{
+       snd_pcm_share_t *share = pcm->private;
+       pcm->mmap_status = &share->mmap_status;
+       return 0;
+}
+
+static int snd_pcm_share_mmap_control(snd_pcm_t *pcm)
+{
+       snd_pcm_share_t *share = pcm->private;
+       pcm->mmap_control = &share->mmap_control;
+       return 0;
+}
+
+static int snd_pcm_share_mmap_data(snd_pcm_t *pcm)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       pcm->mmap_data = slave->pcm->mmap_data;
+       return 0;
+}
+
+static int snd_pcm_share_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+       return 0;
+}
+               
+static int snd_pcm_share_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+       return 0;
+}
+               
+static int snd_pcm_share_munmap_data(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+       return 0;
+}
+               
+static int snd_pcm_share_channels_mask(snd_pcm_t *pcm, bitset_t *cmask)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       unsigned int i;
+       bitset_t m[bitset_size(slave->channels_count)];
+       int err = snd_pcm_channels_mask(slave->pcm, m);
+       if (err < 0)
+               return err;
+       for (i = 0; i < share->channels_count; ++i) {
+               if (!bitset_get(m, share->slave_channels[i]))
+                       bitset_reset(cmask, i);
+       }
+       return 0;
+}
+               
+int snd_pcm_share_poll_descriptor(snd_pcm_t *pcm)
+{
+       snd_pcm_share_t *share = pcm->private;
+       return share->client_socket;
+}
+
+static void snd_pcm_share_dump(snd_pcm_t *pcm, FILE *fp)
+{
+       snd_pcm_share_t *share = pcm->private;
+       snd_pcm_share_slave_t *slave = share->slave;
+       unsigned int k;
+       fprintf(fp, "Share PCM\n");
+       fprintf(fp, "\nChannel bindings:\n");
+       for (k = 0; k < share->channels_count; ++k)
+               fprintf(fp, "%d: %d\n", k, share->slave_channels[k]);
+       if (pcm->valid_setup) {
+               fprintf(fp, "\nIts setup is:\n");
+               snd_pcm_dump_setup(pcm, fp);
+       }
+       fprintf(fp, "Slave: ");
+       snd_pcm_dump(slave->pcm, fp);
+}
+
+struct snd_pcm_ops snd_pcm_share_ops = {
+       close: snd_pcm_share_close,
+       info: snd_pcm_share_info,
+       params_info: snd_pcm_share_params_info,
+       params: snd_pcm_share_params,
+       setup: snd_pcm_share_setup,
+       channel_info: snd_pcm_share_channel_info,
+       channel_params: snd_pcm_share_channel_params,
+       channel_setup: snd_pcm_share_channel_setup,
+       dump: snd_pcm_share_dump,
+       nonblock: snd_pcm_share_nonblock,
+       async: snd_pcm_share_async,
+       mmap_status: snd_pcm_share_mmap_status,
+       mmap_control: snd_pcm_share_mmap_control,
+       mmap_data: snd_pcm_share_mmap_data,
+       munmap_status: snd_pcm_share_munmap_status,
+       munmap_control: snd_pcm_share_munmap_control,
+       munmap_data: snd_pcm_share_munmap_data,
+};
+
+struct snd_pcm_fast_ops snd_pcm_share_fast_ops = {
+       status: snd_pcm_share_status,
+       state: snd_pcm_share_state,
+       delay: snd_pcm_share_delay,
+       prepare: snd_pcm_share_prepare,
+       start: snd_pcm_share_start,
+       drop: snd_pcm_share_drop,
+       drain: snd_pcm_share_drain,
+       pause: snd_pcm_share_pause,
+       writei: snd_pcm_mmap_writei,
+       writen: snd_pcm_mmap_writen,
+       readi: snd_pcm_mmap_readi,
+       readn: snd_pcm_mmap_readn,
+       rewind: snd_pcm_share_rewind,
+       poll_descriptor: snd_pcm_share_poll_descriptor,
+       channels_mask: snd_pcm_share_channels_mask,
+       avail_update: snd_pcm_share_avail_update,
+       mmap_forward: snd_pcm_share_mmap_forward,
+};
+
+int snd_pcm_share_open(snd_pcm_t **handlep, char *name, char *sname,
+                      size_t schannels_count,
+                      size_t channels_count, int *channels_map,
+                      int stream, int mode)
+{
+       snd_pcm_t *handle;
+       snd_pcm_share_t *share;
+       int err;
+       struct list_head *i;
+       char slave_map[32] = { 0 };
+       unsigned int k;
+       snd_pcm_share_slave_t *slave = NULL;
+       int sd[2];
+
+       assert(handlep);
+       assert(channels_count > 0 && sname && channels_map);
+
+       for (k = 0; k < channels_count; ++k) {
+               if (channels_map[k] < 0 || channels_map[k] > 31) {
+                       ERR("Invalid slave channel (%d) in binding", channels_map[k]);
+                       return -EINVAL;
+               }
+               if (slave_map[channels_map[k]]) {
+                       ERR("Repeated slave channel (%d) in binding", channels_map[k]);
+                       return -EINVAL;
+               }
+               slave_map[channels_map[k]] = 1;
+               assert((unsigned)channels_map[k] < schannels_count);
+       }
+
+       share = calloc(1, sizeof(snd_pcm_share_t));
+       if (!share)
+               return -ENOMEM;
+
+       share->channels_count = channels_count;
+       share->slave_channels = calloc(channels_count, sizeof(*share->slave_channels));
+       if (!share->slave_channels) {
+               free(share);
+               return -ENOMEM;
+       }
+       memcpy(share->slave_channels, channels_map, channels_count * sizeof(*share->slave_channels));
+
+       handle = calloc(1, sizeof(snd_pcm_t));
+       if (!handle) {
+               free(share->slave_channels);
+               free(share);
+               return -ENOMEM;
+       }
+       err = socketpair(AF_LOCAL, SOCK_STREAM, 0, sd);
+       if (err >= 0 && stream == SND_PCM_STREAM_PLAYBACK) {
+               int bufsize = 1;
+               err = setsockopt(sd[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
+               if (err >= 0) {
+                       struct pollfd pfd;
+                       pfd.fd = sd[0];
+                       pfd.events = POLLOUT;
+                       while ((err = poll(&pfd, 1, 0)) == 1) {
+                               char buf[1];
+                               err = write(sd[0], buf, 1);
+                               assert(err != 0);
+                               if (err != 1)
+                                       break;
+                       }
+               }
+       }
+       if (err < 0) {
+               err = -errno;
+               free(handle);
+               free(share->slave_channels);
+               free(share);
+               return err;
+       }
+
+       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)) {
+                       slave = s;
+                       break;
+               }
+       }
+       if (!slave) {
+               snd_pcm_t *spcm;
+               err = snd_pcm_open(&spcm, sname, stream, mode);
+               if (err < 0) {
+                       pthread_mutex_unlock(&slaves_mutex);
+                       close(sd[0]);
+                       close(sd[1]);
+                       free(handle);
+                       free(share->slave_channels);
+                       free(share);
+                       return err;
+               }
+               slave = calloc(1, sizeof(*slave));
+               if (!slave) {
+                       pthread_mutex_unlock(&slaves_mutex);
+                       snd_pcm_close(spcm);
+                       close(sd[0]);
+                       close(sd[1]);
+                       free(handle);
+                       free(share->slave_channels);
+                       free(share);
+                       return err;
+               }
+               INIT_LIST_HEAD(&slave->clients);
+               slave->pcm = spcm;
+               slave->channels_count = schannels_count;
+               pthread_mutex_init(&slave->mutex, NULL);
+               list_add_tail(&slave->list, &slaves);
+       }
+       pthread_mutex_unlock(&slaves_mutex);
+
+       pthread_mutex_lock(&slave->mutex);
+       if (slave->open_count == 0) {
+               err = pthread_create(&slave->thread, NULL, snd_pcm_share_slave_thread, slave);
+               assert(err == 0);
+       }
+       slave->open_count++;
+       list_add_tail(&share->list, &slave->clients);
+       pthread_mutex_unlock(&slave->mutex);
+
+       share->slave = slave;
+       share->pcm = handle;
+       share->client_socket = sd[0];
+       share->slave_socket = sd[1];
+       share->async_sig = SIGIO;
+       share->async_pid = getpid();
+       
+       if (name)
+               handle->name = strdup(name);
+       handle->type = SND_PCM_TYPE_SHARE;
+       handle->stream = stream;
+       handle->mode = mode;
+       handle->ops = &snd_pcm_share_ops;
+       handle->op_arg = handle;
+       handle->fast_ops = &snd_pcm_share_fast_ops;
+       handle->fast_op_arg = handle;
+       handle->private = share;
+       err = snd_pcm_init(handle);
+       if (err < 0) {
+               snd_pcm_close(handle);
+               return err;
+       }
+       *handlep = handle;
+       return 0;
+}
+
+int _snd_pcm_share_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
+                       int stream, int mode)
+{
+       snd_config_iterator_t i;
+       char *sname = NULL;
+       snd_config_t *binding = NULL;
+       int err;
+       unsigned int idx;
+       int *channels_map;
+       size_t channels_count = 0;
+       long schannels_count = -1;
+       size_t schannel_max = 0;
+       snd_config_foreach(i, conf) {
+               snd_config_t *n = snd_config_entry(i);
+               if (strcmp(n->id, "comment") == 0)
+                       continue;
+               if (strcmp(n->id, "type") == 0)
+                       continue;
+               if (strcmp(n->id, "stream") == 0)
+                       continue;
+               if (strcmp(n->id, "sname") == 0) {
+                       err = snd_config_string_get(n, &sname);
+                       if (err < 0) {
+                               ERR("Invalid type for sname");
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (strcmp(n->id, "schannels") == 0) {
+                       err = snd_config_integer_get(n, &schannels_count);
+                       if (err < 0) {
+                               ERR("Invalid type for schannels");
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (strcmp(n->id, "binding") == 0) {
+                       if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) {
+                               ERR("Invalid type for binding");
+                               return -EINVAL;
+                       }
+                       binding = n;
+                       continue;
+               }
+               ERR("Unknown field: %s", n->id);
+               return -EINVAL;
+       }
+       if (!sname) {
+               ERR("Missing sname");
+               return -EINVAL;
+       }
+       if (!binding) {
+               ERR("Missing binding");
+               return -EINVAL;
+       }
+       snd_config_foreach(i, binding) {
+               int cchannel = -1;
+               char *p;
+               snd_config_t *n = snd_config_entry(i);
+               errno = 0;
+               cchannel = strtol(n->id, &p, 10);
+               if (errno || *p || cchannel < 0) {
+                       ERR("Invalid client channel in binding: %s", n->id);
+                       return -EINVAL;
+               }
+               if ((unsigned)cchannel > channels_count)
+                       channels_count = cchannel + 1;
+       }
+       if (channels_count == 0) {
+               ERR("No bindings defined");
+               return -EINVAL;
+       }
+       channels_map = calloc(channels_count, sizeof(*channels_map));
+       for (idx = 0; idx < channels_count; ++idx)
+               channels_map[idx] = -1;
+
+       snd_config_foreach(i, binding) {
+               snd_config_t *n = snd_config_entry(i);
+               long cchannel;
+               long schannel = -1;
+               cchannel = strtol(n->id, 0, 10);
+               err = snd_config_integer_get(n, &schannel);
+               if (err < 0)
+                       goto _free;
+               assert(schannels_count <= 0 || schannel < schannels_count);
+               channels_map[cchannel] = schannel;
+               if ((unsigned)schannel > schannel_max)
+                       schannel_max = schannel;
+       }
+       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);
+_free:
+       free(channels_map);
+       return err;
+}