]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Cleaned pcm internals. Added mmap'able plugins
authorAbramo Bagnara <abramo@alsa-project.org>
Mon, 17 Apr 2000 17:31:41 +0000 (17:31 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Mon, 17 Apr 2000 17:31:41 +0000 (17:31 +0000)
src/Makefile.am
src/pcm/pcm.c
src/pcm/pcm_local.h
src/pcm/pcm_plugin.c
src/pcm/plugin/mmap.c

index 652f5de29b97733c5c9b1c435a5f6e3fe1022545..e7d085e9dc0e4ce4bb6cb7c8c4a53527fc822e04 100644 (file)
@@ -6,7 +6,7 @@ libasound_la_SOURCES = error.c
 libasound_la_LIBADD = control/libcontrol.la mixer/libmixer.la pcm/libpcm.la \
                       rawmidi/librawmidi.la timer/libtimer.la hwdep/libhwdep.la \
                       seq/libseq.la instr/libinstr.la
-libasound_la_LDFLAGS = -version-info $(COMPATNUM)
+libasound_la_LDFLAGS = -version-info $(COMPATNUM) -lpthread
 
 control/libcontrol.la:
        $(MAKE) -C control libcontrol.la
index 8f7e1e97c69e042c086bf7dfe4348b4a0bd9fbc0..9754783e7c511bf913ea33c91cba3316de174e12 100644 (file)
@@ -139,8 +139,8 @@ int snd_pcm_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevi
        }
        pcm->card = card;
        pcm->device = device;
-       pcm->fd[SND_PCM_CHANNEL_PLAYBACK] = pfd;
-       pcm->fd[SND_PCM_CHANNEL_CAPTURE] = cfd;
+       pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd = pfd;
+       pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd = cfd;
        pcm->mode = mode;
        pcm->ver = ver;
        *handle = pcm;
@@ -155,10 +155,11 @@ int snd_pcm_close(snd_pcm_t *pcm)
        if (!pcm)
                return -EINVAL;
        for (channel = 0; channel < 2; ++channel) {
-               snd_pcm_munmap(pcm, channel);
+               snd_pcm_plugin_munmap(pcm, channel);
                snd_pcm_plugin_clear(pcm, channel);
-               if (pcm->fd[channel] >= 0)
-                       if (close(pcm->fd[channel]))
+               snd_pcm_munmap(pcm, channel);
+               if (pcm->chan[channel].fd >= 0)
+                       if (close(pcm->chan[channel].fd))
                                res = -errno;
        }
        free(pcm);
@@ -171,7 +172,7 @@ int snd_pcm_file_descriptor(snd_pcm_t *pcm, int channel)
                return -EINVAL;
        if (channel < 0 || channel > 1)
                return -EINVAL;
-       return pcm->fd[channel];
+       return pcm->chan[channel].fd;
 }
 
 int snd_pcm_nonblock_mode(snd_pcm_t *pcm, int nonblock)
@@ -182,7 +183,7 @@ int snd_pcm_nonblock_mode(snd_pcm_t *pcm, int nonblock)
        if (!pcm)
                return -EINVAL;
        for (channel = 0; channel < 2; ++channel) {
-               fd = pcm->fd[channel];
+               fd = pcm->chan[channel].fd;
                if (fd < 0)
                        continue;
                if ((flags = fcntl(fd, F_GETFL)) < 0)
@@ -207,7 +208,7 @@ int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
        if (!pcm || !info)
                return -EINVAL;
        for (channel = 0; channel < 2; ++channel) {
-               fd = pcm->fd[channel];
+               fd = pcm->chan[channel].fd;
                if (fd >= 0)
                        break;
        }
@@ -223,7 +224,7 @@ int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
                return -EINVAL;
        if (info->channel < 0 || info->channel > 1)
                return -EINVAL;
-       fd = pcm->fd[info->channel];
+       fd = pcm->chan[info->channel].fd;
        if (fd < 0)
                return -EINVAL;
        if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0)
@@ -235,43 +236,47 @@ int snd_pcm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
 {
        int err;
        int fd;
+       struct snd_pcm_chan *chan;
 
        if (!pcm || !params)
                return -EINVAL;
        if (params->channel < 0 || params->channel > 1)
                return -EINVAL;
-       fd = pcm->fd[params->channel];
+       chan = &pcm->chan[params->channel];
+       fd = chan->fd;
        if (fd < 0)
                return -EINVAL;
        if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0)
                return -errno;
-       pcm->setup_is_valid[params->channel] = 0;
-       memset(&pcm->setup[params->channel], 0, sizeof(snd_pcm_channel_setup_t));
-       pcm->setup[params->channel].channel = params->channel;
-       if ((err = snd_pcm_channel_setup(pcm, &pcm->setup[params->channel]))<0)
+       chan->setup_is_valid = 0;
+       memset(&chan->setup, 0, sizeof(snd_pcm_channel_setup_t));
+       chan->setup.channel = params->channel;
+       if ((err = snd_pcm_channel_setup(pcm, &chan->setup))<0)
                return err;
-       pcm->setup_is_valid[params->channel] = 1;
        return 0;
 }
 
 int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
 {
        int fd;
+       struct snd_pcm_chan *chan;
+
        if (!pcm || !setup)
                return -EINVAL;
        if (setup->channel < 0 || setup->channel > 1)
                return -EINVAL;
-       fd = pcm->fd[setup->channel];
+       chan = &pcm->chan[setup->channel];
+       fd = chan->fd;
        if (fd < 0)
                return -EINVAL;
-       if (pcm->setup_is_valid[setup->channel]) {
-               memcpy(setup, &pcm->setup[setup->channel], sizeof(*setup));
-       } else {
-               if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0)
-                       return -errno;
-               memcpy(&pcm->setup[setup->channel], setup, sizeof(*setup));
-               pcm->setup_is_valid[setup->channel] = 1;
+       if (chan->setup_is_valid) {
+               memcpy(setup, &chan->setup, sizeof(*setup));
+               return 0;
        }
+       if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0)
+               return -errno;
+       memcpy(&chan->setup, setup, sizeof(*setup));
+       chan->setup_is_valid = 1;
        return 0;
 }
 
@@ -282,7 +287,7 @@ int snd_pcm_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t * set
                return -EINVAL;
        if (channel < 0 || channel > 1)
                return -EINVAL;
-       fd = pcm->fd[channel];
+       fd = pcm->chan[channel].fd;
        if (fd < 0)
                return -EINVAL;
        if (ioctl(fd, SND_PCM_IOCTL_VOICE_SETUP, setup) < 0)
@@ -295,7 +300,7 @@ int snd_pcm_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t * status)
        int fd;
        if (!pcm || !status)
                return -EINVAL;
-       fd = pcm->fd[status->channel];
+       fd = pcm->chan[status->channel].fd;
        if (fd < 0)
                return -EINVAL;
        if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_STATUS, status) < 0)
@@ -307,9 +312,9 @@ int snd_pcm_playback_prepare(snd_pcm_t *pcm)
 {
        if (!pcm)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_PLAYBACK] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
                return -EINVAL;
-       if (ioctl(pcm->fd[SND_PCM_CHANNEL_PLAYBACK], SND_PCM_IOCTL_CHANNEL_PREPARE) < 0)
+       if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_PREPARE) < 0)
                return -errno;
        return 0;
 }
@@ -318,9 +323,9 @@ int snd_pcm_capture_prepare(snd_pcm_t *pcm)
 {
        if (!pcm)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_CAPTURE] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
                return -EINVAL;
-       if (ioctl(pcm->fd[SND_PCM_CHANNEL_CAPTURE], SND_PCM_IOCTL_CHANNEL_PREPARE) < 0)
+       if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_PREPARE) < 0)
                return -errno;
        return 0;
 }
@@ -341,9 +346,9 @@ int snd_pcm_playback_go(snd_pcm_t *pcm)
 {
        if (!pcm)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_PLAYBACK] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
                return -EINVAL;
-       if (ioctl(pcm->fd[SND_PCM_CHANNEL_PLAYBACK], SND_PCM_IOCTL_CHANNEL_GO) < 0)
+       if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_GO) < 0)
                return -errno;
        return 0;
 }
@@ -352,9 +357,9 @@ int snd_pcm_capture_go(snd_pcm_t *pcm)
 {
        if (!pcm)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_CAPTURE] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
                return -EINVAL;
-       if (ioctl(pcm->fd[SND_PCM_CHANNEL_CAPTURE], SND_PCM_IOCTL_CHANNEL_GO) < 0)
+       if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_GO) < 0)
                return -errno;
        return 0;
 }
@@ -375,9 +380,9 @@ int snd_pcm_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync)
 {
        if (!pcm || !sync)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_PLAYBACK] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
                return -EINVAL;
-       if (ioctl(pcm->fd[SND_PCM_CHANNEL_PLAYBACK], SND_PCM_IOCTL_SYNC_GO, sync) < 0)
+       if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_SYNC_GO, sync) < 0)
                return -errno;
        return 0;
 }
@@ -386,9 +391,9 @@ int snd_pcm_playback_drain(snd_pcm_t *pcm)
 {
        if (!pcm)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_PLAYBACK] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
                return -EINVAL;
-       if (ioctl(pcm->fd[SND_PCM_CHANNEL_PLAYBACK], SND_PCM_IOCTL_CHANNEL_DRAIN) < 0)
+       if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_DRAIN) < 0)
                return -errno;
        return 0;
 }
@@ -397,9 +402,9 @@ int snd_pcm_playback_flush(snd_pcm_t *pcm)
 {
        if (!pcm)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_PLAYBACK] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
                return -EINVAL;
-       if (ioctl(pcm->fd[SND_PCM_CHANNEL_PLAYBACK], SND_PCM_IOCTL_CHANNEL_FLUSH) < 0)
+       if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_FLUSH) < 0)
                return -errno;
        return 0;
 }
@@ -408,9 +413,9 @@ int snd_pcm_capture_flush(snd_pcm_t *pcm)
 {
        if (!pcm)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_CAPTURE] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
                return -EINVAL;
-       if (ioctl(pcm->fd[SND_PCM_CHANNEL_CAPTURE], SND_PCM_IOCTL_CHANNEL_FLUSH) < 0)
+       if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_FLUSH) < 0)
                return -errno;
        return 0;
 }
@@ -431,22 +436,24 @@ int snd_pcm_playback_pause(snd_pcm_t *pcm, int enable)
 {
        if (!pcm)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_PLAYBACK] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
                return -EINVAL;
-       if (ioctl(pcm->fd[SND_PCM_CHANNEL_PLAYBACK], SND_PCM_IOCTL_CHANNEL_PAUSE, &enable) < 0)
+       if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_PAUSE, &enable) < 0)
                return -errno;
        return 0;
 }
 
 ssize_t snd_pcm_transfer_size(snd_pcm_t *pcm, int channel)
 {
+       struct snd_pcm_chan *chan;
        if (!pcm || channel < 0 || channel > 1)
                return -EINVAL;
-       if (!pcm->setup_is_valid[channel])
+       chan = &pcm->chan[channel];
+       if (!chan->setup_is_valid)
                return -EBADFD;
-       if (pcm->setup[channel].mode != SND_PCM_MODE_BLOCK)
+       if (chan->setup.mode != SND_PCM_MODE_BLOCK)
                return -EBADFD;
-       return pcm->setup[channel].buf.block.frag_size;
+       return chan->setup.buf.block.frag_size;
 }
 
 ssize_t snd_pcm_write(snd_pcm_t *pcm, const void *buffer, size_t size)
@@ -455,9 +462,9 @@ ssize_t snd_pcm_write(snd_pcm_t *pcm, const void *buffer, size_t size)
 
        if (!pcm || (!buffer && size > 0) || size < 0)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_PLAYBACK] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
                return -EINVAL;
-       result = write(pcm->fd[SND_PCM_CHANNEL_PLAYBACK], buffer, size);
+       result = write(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, buffer, size);
        if (result < 0)
                return -errno;
        return result;
@@ -469,16 +476,16 @@ ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, int count)
 
        if (!pcm || (!vector && count > 0) || count < 0)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_PLAYBACK] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
                return -EINVAL;
 #if 0
-       result = writev(pcm->fd[SND_PCM_CHANNEL_PLAYBACK], vector, count);
+       result = writev(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, vector, count);
 #else
        {
                snd_v_args_t args;
                args.vector = vector;
                args.count = count;
-               result = ioctl(pcm->fd[SND_PCM_CHANNEL_PLAYBACK], SND_IOCTL_WRITEV, &args);
+               result = ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_IOCTL_WRITEV, &args);
        }
 #endif
        if (result < 0)
@@ -492,9 +499,9 @@ ssize_t snd_pcm_read(snd_pcm_t *pcm, void *buffer, size_t size)
 
        if (!pcm || (!buffer && size > 0) || size < 0)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_CAPTURE] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
                return -EINVAL;
-       result = read(pcm->fd[SND_PCM_CHANNEL_CAPTURE], buffer, size);
+       result = read(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, buffer, size);
        if (result < 0)
                return -errno;
        return result;
@@ -506,16 +513,16 @@ ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, int count)
 
        if (!pcm || (!vector && count > 0) || count < 0)
                return -EINVAL;
-       if (pcm->fd[SND_PCM_CHANNEL_CAPTURE] < 0)
+       if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
                return -EINVAL;
 #if 0
-       result = readv(pcm->fd[SND_PCM_CHANNEL_CAPTURE], vector, count);
+       result = readv(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, vector, count);
 #else
        {
                snd_v_args_t args;
                args.vector = vector;
                args.count = count;
-               result = ioctl(pcm->fd[SND_PCM_CHANNEL_CAPTURE], SND_IOCTL_READV, &args);
+               result = ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_IOCTL_READV, &args);
        }
 #endif
        if (result < 0)
@@ -528,6 +535,7 @@ int snd_pcm_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control,
        snd_pcm_channel_info_t info;
        int err, fd, prot;
        void *caddr, *daddr;
+       struct snd_pcm_chan *chan;
 
        if (control)
                *control = NULL;
@@ -535,7 +543,8 @@ int snd_pcm_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control,
                *buffer = NULL;
        if (!pcm || channel < 0 || channel > 1 || !control || !buffer)
                return -EINVAL;
-       fd = pcm->fd[channel];
+       chan = &pcm->chan[channel];
+       fd = chan->fd;
        if (fd < 0)
                return -EINVAL;
        memset(&info, 0, sizeof(info));
@@ -552,24 +561,26 @@ int snd_pcm_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control,
                munmap(caddr, sizeof(snd_pcm_mmap_control_t));
                return err;
        }
-       *control = pcm->mmap_caddr[channel] = caddr;
-       *buffer = pcm->mmap_daddr[channel] = daddr;
-       pcm->mmap_size[channel] = info.mmap_size;
+       *control = chan->mmap_control = caddr;
+       *buffer = chan->mmap_data = daddr;
+       chan->mmap_size = info.mmap_size;
        return 0;
 }
 
 int snd_pcm_munmap(snd_pcm_t *pcm, int channel)
 {
+       struct snd_pcm_chan *chan;
        if (!pcm || channel < 0 || channel > 1)
                return -EINVAL;
-       if (pcm->mmap_caddr[channel]) {
-               munmap(pcm->mmap_caddr[channel], sizeof(snd_pcm_mmap_control_t));
-               pcm->mmap_caddr[channel] = NULL;
+       chan = &pcm->chan[channel];
+       if (chan->mmap_control) {
+               munmap(chan->mmap_control, sizeof(snd_pcm_mmap_control_t));
+               chan->mmap_control = NULL;
        }
-       if (pcm->mmap_daddr[channel]) {
-               munmap(pcm->mmap_daddr[channel], pcm->mmap_size[channel]);
-               pcm->mmap_daddr[channel] = NULL;
-               pcm->mmap_size[channel] = 0;
+       if (chan->mmap_data) {
+               munmap(chan->mmap_data, chan->mmap_size);
+               chan->mmap_data = NULL;
+               chan->mmap_size = 0;
        }
        return 0;
 }
index 1a9758d4692601de312e0cc1535e9855625f016a..eca8cedb9a1cf343505ce0aa23da8d0ff1cc5d9e 100644 (file)
  *
  */
 
+#include <pthread.h>
 #include "asoundlib.h"
   
+struct snd_pcm_plug {
+       snd_pcm_plugin_t *first;
+       snd_pcm_plugin_t *last;
+       void *alloc_ptr[2];
+       long alloc_size[2];
+       int alloc_lock[2];
+       snd_pcm_mmap_control_t *mmap_control;
+       char *mmap_data;
+       long mmap_size;
+       pthread_t thread;
+       int thread_stop;
+       int setup_is_valid;
+       snd_pcm_channel_setup_t setup;
+       int hwstatus;
+};
+
+struct snd_pcm_chan {
+       int fd;
+       int setup_is_valid;
+       snd_pcm_channel_setup_t setup;
+       snd_pcm_mmap_control_t *mmap_control;
+       char *mmap_data;
+       long mmap_size;
+       struct snd_pcm_plug plug;
+};
+
 struct snd_pcm {
        int card;
        int device;
        int mode;
        int ver;
-       int fd[2];
-       int setup_is_valid[2];
-       snd_pcm_channel_setup_t setup[2];
-       snd_pcm_mmap_control_t *mmap_caddr[2];
-       char *mmap_daddr[2];
-       long mmap_size[2];
-       snd_pcm_plugin_t *plugin_first[2];
-       snd_pcm_plugin_t *plugin_last[2];
-       void *plugin_alloc_ptr[4];
-       long plugin_alloc_size[4];
-       int plugin_alloc_lock[4];
-       void *plugin_alloc_xptr[2];
-       long plugin_alloc_xsize[2];
-       int plugin_alloc_xchannel;
+       struct snd_pcm_chan chan[2];
 };
 
 unsigned int snd_pcm_plugin_formats(unsigned int formats);
index d0f1afd8b0b79045f00c5b8efe28d25f8cbd7fe2..edaa114f34946f42d452bef1196b79f103e2f697 100644 (file)
 #include <errno.h>
 #include <math.h>
 #include <sys/uio.h>
+#include <sys/poll.h>
 #include "pcm_local.h"
 
-static void *snd_pcm_plugin_buf_alloc(snd_pcm_t *pcm, size_t size);
-static void snd_pcm_plugin_buf_free(snd_pcm_t *pcm, void *ptr);
-static void *snd_pcm_plugin_ptr_alloc(snd_pcm_t *pcm, size_t size);
+static void *snd_pcm_plugin_buf_alloc(snd_pcm_t *pcm, int channel, size_t size);
+static void snd_pcm_plugin_buf_free(snd_pcm_t *pcm, int channel, void *ptr);
 
 snd_pcm_plugin_t *snd_pcm_plugin_build(snd_pcm_plugin_handle_t *handle,
                                       const char *name,
@@ -64,6 +64,8 @@ snd_pcm_plugin_t *snd_pcm_plugin_build(snd_pcm_plugin_handle_t *handle,
        plugin->handle = handle;
        plugin->voices = (snd_pcm_plugin_voice_t *)((char *)plugin + sizeof(*plugin));
        plugin->extra_data = (char *)plugin->voices + voices * sizeof(snd_pcm_plugin_voice_t);
+       plugin->src_voices = snd_pcm_plugin_src_voices;
+       plugin->dst_voices = snd_pcm_plugin_dst_voices;
        return plugin;
 }
 
@@ -82,29 +84,27 @@ int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin)
 int snd_pcm_plugin_clear(snd_pcm_t *pcm, int channel)
 {
        snd_pcm_plugin_t *plugin, *plugin_next;
+       struct snd_pcm_plug *plug;
        int idx;
        
        if (!pcm)
                return -EINVAL;
-       plugin = pcm->plugin_first[channel];
-       pcm->plugin_first[channel] = NULL;
-       pcm->plugin_last[channel] = NULL;
+       plugin = pcm->chan[channel].plug.first;
+       pcm->chan[channel].plug.first = NULL;
+       pcm->chan[channel].plug.last = NULL;
        while (plugin) {
                plugin_next = plugin->next;
                snd_pcm_plugin_free(plugin);
                plugin = plugin_next;
        }
-       for (idx = 0; idx < 4; idx++) {
-               if (pcm->plugin_alloc_ptr[idx])
-                       free(pcm->plugin_alloc_ptr[idx]);
-               pcm->plugin_alloc_ptr[idx] = 0;
-               pcm->plugin_alloc_size[idx] = 0;
-               pcm->plugin_alloc_lock[idx] = 0;
-       }
-       if (pcm->plugin_alloc_xptr[channel])
-               free(pcm->plugin_alloc_xptr[channel]);
-       pcm->plugin_alloc_xptr[channel] = NULL;
-       pcm->plugin_alloc_xsize[channel] = 0;
+       plug = &pcm->chan[channel].plug;
+       for (idx = 0; idx < 2; idx++) {
+               if (plug->alloc_ptr[idx])
+                       free(plug->alloc_ptr[idx]);
+               plug->alloc_ptr[idx] = 0;
+               plug->alloc_size[idx] = 0;
+               plug->alloc_lock[idx] = 0;
+       }
        return 0;
 }
 
@@ -112,14 +112,14 @@ int snd_pcm_plugin_insert(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t *plugin)
 {
        if (!pcm || channel < 0 || channel > 1 || !plugin)
                return -EINVAL;
-       plugin->next = pcm->plugin_first[channel];
+       plugin->next = pcm->chan[channel].plug.first;
        plugin->prev = NULL;
-       if (pcm->plugin_first[channel]) {
-               pcm->plugin_first[channel]->prev = plugin;
-               pcm->plugin_first[channel] = plugin;
+       if (pcm->chan[channel].plug.first) {
+               pcm->chan[channel].plug.first->prev = plugin;
+               pcm->chan[channel].plug.first = plugin;
        } else {
-               pcm->plugin_last[channel] =
-               pcm->plugin_first[channel] = plugin;
+               pcm->chan[channel].plug.last =
+               pcm->chan[channel].plug.first = plugin;
        }
        return 0;
 }
@@ -129,13 +129,13 @@ int snd_pcm_plugin_append(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t *plugin)
        if (!pcm || channel < 0 || channel > 1 || !plugin)
                return -EINVAL;
        plugin->next = NULL;
-       plugin->prev = pcm->plugin_last[channel];
-       if (pcm->plugin_last[channel]) {
-               pcm->plugin_last[channel]->next = plugin;
-               pcm->plugin_last[channel] = plugin;
+       plugin->prev = pcm->chan[channel].plug.last;
+       if (pcm->chan[channel].plug.last) {
+               pcm->chan[channel].plug.last->next = plugin;
+               pcm->chan[channel].plug.last = plugin;
        } else {
-               pcm->plugin_last[channel] =
-               pcm->plugin_first[channel] = plugin;
+               pcm->chan[channel].plug.last =
+               pcm->chan[channel].plug.first = plugin;
        }
        return 0;
 }
@@ -149,9 +149,9 @@ int snd_pcm_plugin_remove_to(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t *plug
        plugin1 = plugin;
        while (plugin1->prev)
                plugin1 = plugin1->prev;
-       if (pcm->plugin_first[channel] != plugin1)
+       if (pcm->chan[channel].plug.first != plugin1)
                return -EINVAL;
-       pcm->plugin_first[channel] = plugin;
+       pcm->chan[channel].plug.first = plugin;
        plugin1 = plugin->prev;
        plugin->prev = NULL;
        while (plugin1) {
@@ -166,7 +166,7 @@ int snd_pcm_plugin_remove_first(snd_pcm_t *pcm, int channel)
 {
        snd_pcm_plugin_t *plugin;
 
-       plugin = pcm->plugin_first[channel];
+       plugin = pcm->chan[channel].plug.first;
        if (plugin->next) {
                plugin = plugin->next;
        } else {
@@ -179,14 +179,14 @@ snd_pcm_plugin_t *snd_pcm_plugin_first(snd_pcm_t *pcm, int channel)
 {
        if (!pcm || channel < 0 || channel > 1)
                return NULL;
-       return pcm->plugin_first[channel];
+       return pcm->chan[channel].plug.first;
 }
 
 snd_pcm_plugin_t *snd_pcm_plugin_last(snd_pcm_t *pcm, int channel)
 {
        if (!pcm || channel < 0 || channel > 1)
                return NULL;
-       return pcm->plugin_last[channel];
+       return pcm->chan[channel].plug.last;
 }
 
 
@@ -223,7 +223,16 @@ int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
        info->formats = snd_pcm_plugin_formats(info->formats);
        info->min_rate = 4000;
        info->max_rate = 192000;
-       info->rates = SND_PCM_RATE_8000_48000;
+       info->min_voices = 1;
+       info->max_voices = 32;
+       info->rates = SND_PCM_RATE_CONTINUOUS;
+       info->buffer_size = snd_pcm_plugin_client_size(pcm, info->channel, info->buffer_size);
+       info->min_fragment_size = snd_pcm_plugin_client_size(pcm, info->channel, info->min_fragment_size);
+       info->max_fragment_size = snd_pcm_plugin_client_size(pcm, info->channel, info->max_fragment_size);
+       info->fragment_align = snd_pcm_plugin_client_size(pcm, info->channel, info->fragment_align);
+       info->fifo_size = snd_pcm_plugin_client_size(pcm, info->channel, info->fifo_size);
+       info->transfer_block_size = snd_pcm_plugin_client_size(pcm, info->channel, info->transfer_block_size);
+       info->mmap_size = snd_pcm_plugin_client_size(pcm, info->channel, info->mmap_size);
        return 0;
 }
 
@@ -233,7 +242,7 @@ static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action,
        snd_pcm_plugin_t *plugin;
        int err;
 
-       plugin = pcm->plugin_first[channel];
+       plugin = pcm->chan[channel].plug.first;
        while (plugin) {
                if (plugin->action) {
                        if ((err = plugin->action(plugin, action, data))<0)
@@ -249,11 +258,16 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
        snd_pcm_channel_params_t hwparams, params1;
        snd_pcm_channel_info_t hwinfo;
        snd_pcm_plugin_t *plugin;
+       struct snd_pcm_plug *plug;
        int err;
        
        if (!pcm || !params || params->channel < 0 || params->channel > 1)
                return -EINVAL;
 
+       plug = &pcm->chan[params->channel].plug;
+       if (plug->mmap_data)
+               return -EBADFD;
+
        /*
         *  try to decide, if a conversion is required
          */
@@ -276,6 +290,9 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
        if ((err = snd_pcm_plugin_format(pcm, &params1, &hwparams, &hwinfo)) < 0)
                return err;
 
+       if (snd_pcm_plugin_first(pcm, params->channel) == NULL)
+               return snd_pcm_channel_params(pcm, params);
+
        /*
         *  I/O plugins
         */
@@ -323,6 +340,12 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
        err = snd_pcm_channel_params(pcm, &hwparams);
        if (err < 0)
                return err;
+
+       plug->setup_is_valid = 0;
+       memset(&plug->setup, 0, sizeof(snd_pcm_channel_setup_t));
+       plug->setup.channel = params->channel;
+       if ((err = snd_pcm_plugin_setup(pcm, &plug->setup))<0)
+               return err;
        err = snd_pcm_plugin_action(pcm, hwparams.channel, INIT, (long)&hwparams);
        if (err < 0)
                return err;
@@ -332,9 +355,17 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
 int snd_pcm_plugin_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
 {
        int err;
+       struct snd_pcm_plug *plug;
        
        if (!pcm || !setup || setup->channel < 0 || setup->channel > 1)
                return -EINVAL;
+       plug = &pcm->chan[setup->channel].plug;
+       if (plug->first == NULL)
+               return snd_pcm_channel_setup(pcm, setup);
+       if (plug->setup_is_valid) {
+               memcpy(setup, &plug->setup, sizeof(*setup));
+               return 0;
+       }
        err = snd_pcm_channel_setup(pcm, setup);
        if (err < 0)
                return err;
@@ -349,12 +380,17 @@ int snd_pcm_plugin_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
        } else {
                return -EINVAL;
        }
+       if (setup->channel == SND_PCM_CHANNEL_PLAYBACK)
+               setup->format = plug->first->src_format;
+       else
+               setup->format = plug->last->dst_format;
+       memcpy(&plug->setup, setup, sizeof(*setup));
+       plug->setup_is_valid = 1;
        return 0;       
 }
 
 int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status)
 {
-       double ratio;
        int err;
        
        if (!pcm || !status || status->channel < 0 || status->channel > 1)
@@ -362,9 +398,6 @@ int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status)
        err = snd_pcm_channel_status(pcm, status);
        if (err < 0)
                return err;
-       ratio = snd_pcm_plugin_transfer_ratio(pcm, status->channel);
-       if (ratio <= 0)
-               return -EINVAL;
        /* FIXME: scount may overflow */
        status->scount = snd_pcm_plugin_client_size(pcm, status->channel, status->scount);
        status->count = snd_pcm_plugin_client_size(pcm, status->channel, status->count);
@@ -372,13 +405,100 @@ int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status)
        return 0;       
 }
 
+static void mmap_clear(struct snd_pcm_plug *plug)
+{
+       int idx;
+       snd_pcm_mmap_fragment_t *f;
+
+       f = plug->mmap_control->fragments;
+       for (idx = 0; idx < plug->setup.buf.block.frags; idx++) {
+               f->data = 0;
+               f->io = 0;
+               f->res[0] = 0;
+               f->res[1] = 0;
+               f++;
+       }
+       plug->mmap_control->status.frag_io = 0;
+       plug->mmap_control->status.block = 0;
+       plug->mmap_control->status.expblock = 0;
+}
+
+static void snd_pcm_plugin_status_change(snd_pcm_t *pcm, int channel)
+{
+       struct snd_pcm_chan *chan;
+       struct snd_pcm_plug *plug;
+       snd_pcm_channel_status_t status;
+       int newstatus;
+
+       chan = &pcm->chan[channel];
+       plug = &chan->plug;
+       if (!plug->mmap_data)
+               return;
+       if (chan->mmap_control) {
+               newstatus = chan->mmap_control->status.status;
+       } else {
+               status.channel = channel;
+               if (snd_pcm_channel_status(pcm, &status) < 0)
+                       newstatus = SND_PCM_STATUS_NOTREADY;
+               else
+                       newstatus = status.status;
+       }
+       if (plug->mmap_control->status.status == SND_PCM_STATUS_RUNNING &&
+           newstatus != SND_PCM_STATUS_RUNNING) {
+               mmap_clear(plug);
+       }
+       plug->mmap_control->status.status = newstatus;
+}
+
 int snd_pcm_plugin_prepare(snd_pcm_t *pcm, int channel)
 {
        int err;
 
        if ((err = snd_pcm_plugin_action(pcm, channel, PREPARE, 0))<0)
                return err;
-       return snd_pcm_channel_prepare(pcm, channel);
+       if ((err = snd_pcm_channel_prepare(pcm, channel)) < 0)
+               return err;
+       snd_pcm_plugin_status_change(pcm, channel);
+       return 0;
+}
+
+int snd_pcm_plugin_go(snd_pcm_t *pcm, int channel)
+{
+       struct snd_pcm_chan *chan;
+       struct snd_pcm_plug *plug;
+       if (!pcm || channel < 0 || channel > 1)
+               return -EINVAL;
+       chan = &pcm->chan[channel];
+       plug = &chan->plug;
+       if (plug->first == NULL)
+               return snd_pcm_channel_go(pcm, channel);
+       if (plug->mmap_control) {
+               if (plug->mmap_control->status.status != SND_PCM_STATUS_PREPARED)
+                       return -EBADFD;
+               if (channel == SND_PCM_CHANNEL_PLAYBACK) {
+                       if (!plug->mmap_control->fragments[0].data)
+                               return -EIO;
+               } else {
+                       if (plug->mmap_control->fragments[0].data)
+                               return -EIO;
+               }
+               plug->mmap_control->status.status = SND_PCM_STATUS_RUNNING;
+       }
+       return 0;
+}
+
+int snd_pcm_plugin_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync)
+{
+       int err;
+       if (!pcm || !sync)
+               return -EINVAL;
+       if (snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK) ||
+           snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_CAPTURE)) {
+               if ((err = snd_pcm_plugin_go(pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0)
+                       return err;
+               return snd_pcm_plugin_go(pcm, SND_PCM_CHANNEL_CAPTURE);
+       }
+       return snd_pcm_sync_go(pcm, sync);
 }
 
 int snd_pcm_plugin_playback_drain(snd_pcm_t *pcm)
@@ -387,7 +507,10 @@ int snd_pcm_plugin_playback_drain(snd_pcm_t *pcm)
 
        if ((err = snd_pcm_plugin_action(pcm, SND_PCM_CHANNEL_PLAYBACK, DRAIN, 0))<0)
                return err;
-       return snd_pcm_playback_drain(pcm);
+       if ((err = snd_pcm_playback_drain(pcm)) < 0)
+               return err;
+       snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK);
+       return 0;
 }
 
 int snd_pcm_plugin_flush(snd_pcm_t *pcm, int channel)
@@ -397,73 +520,75 @@ int snd_pcm_plugin_flush(snd_pcm_t *pcm, int channel)
        pdprintf("flush\n");
        if ((err = snd_pcm_plugin_action(pcm, channel, FLUSH, 0))<0)
                return err;
-       return snd_pcm_channel_flush(pcm, channel);
+       if ((err = snd_pcm_channel_flush(pcm, channel)) < 0)
+               return err;
+       snd_pcm_plugin_status_change(pcm, channel);
+       return 0;
 }
 
-ssize_t snd_pcm_plugin_transfer_size(snd_pcm_t *pcm, int channel)
+int snd_pcm_plugin_pause(snd_pcm_t *pcm, int enable)
 {
-       ssize_t result;
-
-       if ((result = snd_pcm_transfer_size(pcm, channel)) < 0)
-               return result;
-       return snd_pcm_plugin_client_size(pcm, channel, result);
+       int err;
+       
+       if ((err = snd_pcm_playback_pause(pcm, enable)) < 0)
+               return err;
+       snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK);
+       return 0;
 }
 
-int snd_pcm_plugin_pointer(snd_pcm_t *pcm, int channel, void **ptr, size_t *size)
+ssize_t snd_pcm_plugin_transfer_size(snd_pcm_t *pcm, int channel)
 {
-       snd_pcm_plugin_t *plugin = NULL;
-       snd_pcm_plugin_voice_t *voices;
-       size_t samples;
-       int width;
+       struct snd_pcm_plug *plug;
+       if (!pcm || channel < 0 || channel > 1)
+               return -EINVAL;
+       plug = &pcm->chan[channel].plug;
+       if (plug->first == NULL)
+               return snd_pcm_plugin_transfer_size(pcm, channel);
+       if (!plug->setup_is_valid)
+               return -EBADFD;
+       if (plug->setup.mode != SND_PCM_MODE_BLOCK)
+               return -EBADFD;
+       return plug->setup.buf.block.frag_size;
+}
 
-       if (!ptr || !size || *size < 1)
+int snd_pcm_plugin_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup)
+{
+       int voice, width, size;
+       struct snd_pcm_plug* plug;
+       
+       if (!pcm || !setup)
                return -EINVAL;
-       *ptr = NULL;
-       if (!pcm || channel < 0 || channel > 1)
+       if (channel < 0 || channel > 1)
                return -EINVAL;
-       if ((*size = snd_pcm_plugin_transfer_size(pcm, channel)) < 0)
-               return *size;
-       if (channel == SND_PCM_CHANNEL_PLAYBACK && plugin->src_voices) {
-               plugin = pcm->plugin_first[channel];
-               if (!plugin)
-                       goto __skip;
-               if (!plugin->src_format.interleave)
-                       goto __skip;
-               if ((width = snd_pcm_format_width(plugin->src_format.format)) < 0)
-                       return width;
-               samples = *size * width;
-               if ((samples % (plugin->src_format.voices * 8)) != 0)
-                       return -EINVAL;
-               samples /= (plugin->src_format.voices * 8);
-               pcm->plugin_alloc_xchannel = SND_PCM_CHANNEL_PLAYBACK;
-               if (plugin->src_voices(plugin, &voices, samples,
-                                      snd_pcm_plugin_ptr_alloc) < 0)
-                       goto __skip;
-               *ptr = voices->addr;
-               return 0;
-       } else if (channel == SND_PCM_CHANNEL_CAPTURE && plugin->dst_voices) {
-               plugin = pcm->plugin_last[channel];
-               if (!plugin)
-                       goto __skip;
-               if (plugin->dst_format.interleave)
-                       goto __skip;
-               if ((width = snd_pcm_format_width(plugin->dst_format.format)) < 0)
-                       return width;
-               samples = *size * width;
-               if ((samples % (plugin->dst_format.voices * 8)) != 0)
-                       return -EINVAL;
-               samples /= (plugin->src_format.voices * 8);
-               pcm->plugin_alloc_xchannel = SND_PCM_CHANNEL_CAPTURE;
-               if (plugin->dst_voices(plugin, &voices, *size,
-                                      snd_pcm_plugin_ptr_alloc) < 0)
-                       goto __skip;
-               *ptr = voices->addr;
+       voice = setup->voice;
+       memset(setup, 0, sizeof(*setup));
+       setup->voice = voice;
+       if (voice < 0)
+               return -EINVAL;
+       plug = &pcm->chan[channel].plug;
+       if (plug->first == NULL)
+               return snd_pcm_voice_setup(pcm, channel, setup);
+       if (!plug->mmap_control) {
+               setup->addr = -1;
                return 0;
        }
-      __skip:
-       *ptr = snd_pcm_plugin_ptr_alloc(pcm, *size);
-       if (*ptr == NULL)
-               return -ENOMEM;
+       if (voice >= plug->setup.format.voices)
+               return -EINVAL;
+
+       width = snd_pcm_format_physical_width(plug->setup.format.format);
+        if (width < 0)
+                return width;
+       size = plug->mmap_size;
+       if (plug->setup.format.interleave) {
+                setup->addr = 0;
+                setup->first = voice * width;
+                setup->step = plug->setup.format.voices * width;
+        } else {
+                size /= plug->setup.format.voices;
+                setup->addr = setup->voice * size;
+                setup->first = 0;
+                setup->step = width;
+       }
        return 0;
 }
 
@@ -528,7 +653,7 @@ static ssize_t snd_pcm_plugin_writev1(snd_pcm_t *pcm, const struct iovec *vector
        ssize_t size;
        int idx, err;
 
-       plugin = pcm->plugin_first[SND_PCM_CHANNEL_PLAYBACK];
+       plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK);
        if ((err = snd_pcm_plugin_load_src_vector(plugin, &src_voices, vector, count)) < 0)
                return err;
        size = 0;
@@ -542,33 +667,26 @@ static ssize_t snd_pcm_plugin_writev1(snd_pcm_t *pcm, const struct iovec *vector
                        ssize_t samples1 = samples;
                        if (plugin->dst_samples)
                                samples1 = plugin->dst_samples(plugin, samples);
-                       if (next->src_voices) {
-                               if ((err = next->src_voices(next, &dst_voices, samples1, snd_pcm_plugin_buf_alloc)) < 0) {
-                                       snd_pcm_plugin_buf_free(pcm, src_voices->aptr);
-                                       return err;
-                               }
-                       } else {
-                               if ((err = snd_pcm_plugin_src_voices(next, &dst_voices, samples1)) < 0) {
-                                       snd_pcm_plugin_buf_free(pcm, src_voices->aptr);
-                                       return err;
-                               }
+                       if ((err = next->src_voices(next, &dst_voices, samples1)) < 0) {
+                               snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr);
+                               return err;
                        }
                } else {
                        dst_voices = NULL;
                }
                pdprintf("write plugin: %s, %i\n", plugin->name, samples);
                if ((samples = plugin->transfer(plugin, src_voices, dst_voices, samples)) < 0) {
-                       snd_pcm_plugin_buf_free(pcm, src_voices->aptr);
+                       snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr);
                        if (dst_voices)
-                               snd_pcm_plugin_buf_free(pcm, dst_voices->aptr);
+                               snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, dst_voices->aptr);
                        return samples;
                }
-               snd_pcm_plugin_buf_free(pcm, src_voices->aptr);
+               snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr);
                plugin = plugin->next;
                src_voices = dst_voices;
        }
        samples = snd_pcm_plugin_client_samples(pcm, SND_PCM_CHANNEL_PLAYBACK, samples);
-       size = snd_pcm_plugin_src_samples_to_size(pcm->plugin_first[SND_PCM_CHANNEL_PLAYBACK], samples);
+       size = snd_pcm_plugin_src_samples_to_size(snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK), samples);
        if (size < 0)
                return size;
        pdprintf("writev result = %i\n", size);
@@ -582,7 +700,7 @@ ssize_t snd_pcm_plugin_writev(snd_pcm_t *pcm, const struct iovec *vector, int co
        int size = 0;
        if (vector == NULL)
                return -EFAULT;
-       plugin = pcm->plugin_first[SND_PCM_CHANNEL_PLAYBACK];
+       plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK);
        if (plugin == NULL)
                return snd_pcm_readv(pcm, vector, count);
        voices = plugin->src_format.voices;
@@ -621,50 +739,42 @@ static ssize_t snd_pcm_plugin_readv1(snd_pcm_t *pcm, const struct iovec *vector,
        ssize_t size;
        int idx, err;
 
-       plugin = pcm->plugin_first[SND_PCM_CHANNEL_CAPTURE];
+       plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_CAPTURE);
        size = 0;
        for (idx = 0; idx < count; idx++)
                size += vector[idx].iov_len;
        if (size < 0)
                return size;
-       samples = snd_pcm_plugin_dst_size_to_samples(pcm->plugin_last[SND_PCM_CHANNEL_CAPTURE], size);
+       samples = snd_pcm_plugin_dst_size_to_samples(snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE), size);
        samples = snd_pcm_plugin_hardware_samples(pcm, SND_PCM_CHANNEL_CAPTURE, samples);
        while (plugin && samples > 0) {
                if ((next = plugin->next) != NULL) {
-                       if (plugin->dst_voices) {
-                               if ((err = plugin->dst_voices(plugin, &dst_voices, samples, snd_pcm_plugin_buf_alloc)) < 0) {
-                                       if (src_voices)
-                                               snd_pcm_plugin_buf_free(pcm, src_voices->aptr);
-                                       return err;
-                               }
-                       } else {
-                               if ((err = snd_pcm_plugin_dst_voices(plugin, &dst_voices, samples)) < 0) {
-                                       if (src_voices)
-                                               snd_pcm_plugin_buf_free(pcm, src_voices->aptr);
-                                       return err;
-                               }
+                       if ((err = plugin->dst_voices(plugin, &dst_voices, samples)) < 0) {
+                               if (src_voices)
+                                       snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
+                               return err;
                        }
                } else {
                        if ((err = snd_pcm_plugin_load_dst_vector(plugin, &dst_voices, vector, count)) < 0) {
                                if (src_voices)
-                                       snd_pcm_plugin_buf_free(pcm, src_voices->aptr);
+                                       snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
                                return err;
                        }
                }
                pdprintf("read plugin: %s, %i\n", plugin->name, samples);
                if ((samples = plugin->transfer(plugin, src_voices, dst_voices, samples)) < 0) {
                        if (src_voices)
-                               snd_pcm_plugin_buf_free(pcm, src_voices->aptr);
-                       snd_pcm_plugin_buf_free(pcm, dst_voices->aptr);
+                               snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
+                       snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, dst_voices->aptr);
                        return samples;
                }
                if (src_voices)
-                       snd_pcm_plugin_buf_free(pcm, src_voices->aptr);
+                       snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
                plugin = plugin->next;
                src_voices = dst_voices;
        }
-       snd_pcm_plugin_buf_free(pcm, dst_voices->aptr);
-       size = snd_pcm_plugin_dst_samples_to_size(pcm->plugin_last[SND_PCM_CHANNEL_CAPTURE], samples);
+       snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, dst_voices->aptr);
+       size = snd_pcm_plugin_dst_samples_to_size(snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE), samples);
        pdprintf("readv result = %i\n", size);
        return size;
 }
@@ -676,7 +786,7 @@ ssize_t snd_pcm_plugin_readv(snd_pcm_t *pcm, const struct iovec *vector, int cou
        int size = 0;
        if (vector == NULL)
                return -EFAULT;
-       plugin = pcm->plugin_last[SND_PCM_CHANNEL_CAPTURE];
+       plugin = snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE);
        if (plugin == NULL)
                return snd_pcm_readv(pcm, vector, count);
        voices = plugin->dst_format.voices;
@@ -712,7 +822,7 @@ ssize_t snd_pcm_plugin_write(snd_pcm_t *pcm, const void *buffer, size_t count)
        snd_pcm_plugin_t *plugin;
        int voices;
 
-       if ((plugin = pcm->plugin_first[SND_PCM_CHANNEL_PLAYBACK]) == NULL)
+       if ((plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK)) == NULL)
                return snd_pcm_write(pcm, buffer, count);
        voices = plugin->src_format.voices;
        if (count % voices != 0)
@@ -739,7 +849,7 @@ ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count)
        snd_pcm_plugin_t *plugin;
        int voices;
 
-       if ((plugin = pcm->plugin_last[SND_PCM_CHANNEL_CAPTURE]) == NULL)
+       if ((plugin = snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE)) == NULL)
                return snd_pcm_read(pcm, buffer, count);
        voices = plugin->dst_format.voices;
        if (count % voices != 0)
@@ -765,82 +875,69 @@ ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count)
  *  Plugin helpers
  */
 
-static void *snd_pcm_plugin_buf_alloc(snd_pcm_t *pcm, size_t size)
+static void *snd_pcm_plugin_buf_alloc(snd_pcm_t *pcm, int channel, size_t size)
 {
        int idx;
        void *ptr;
+       struct snd_pcm_plug *plug;
 
        if (pcm == NULL || size <= 0)
                return NULL;
-       for (idx = 0; idx < 4; idx++) {
-               if (pcm->plugin_alloc_lock[idx])
+       plug = &pcm->chan[channel].plug;
+       for (idx = 0; idx < 2; idx++) {
+               if (plug->alloc_lock[idx])
                        continue;
-               if (pcm->plugin_alloc_ptr[idx] == NULL)
+               if (plug->alloc_ptr[idx] == NULL)
                        continue;
-               if (pcm->plugin_alloc_size[idx] >= size) {
-                       pcm->plugin_alloc_lock[idx] = 1;
-                       return pcm->plugin_alloc_ptr[idx];
+               if (plug->alloc_size[idx] >= size) {
+                       plug->alloc_lock[idx] = 1;
+                       return plug->alloc_ptr[idx];
                }
        }
-       for (idx = 0; idx < 4; idx++) {
-               if (pcm->plugin_alloc_lock[idx])
+       for (idx = 0; idx < 2; idx++) {
+               if (plug->alloc_lock[idx])
                        continue;
-               if (pcm->plugin_alloc_ptr[idx] == NULL)
+               if (plug->alloc_ptr[idx] == NULL)
                        continue;
-               ptr = realloc(pcm->plugin_alloc_ptr[idx], size);
+               ptr = realloc(plug->alloc_ptr[idx], size);
                if (ptr == NULL)
                        continue;
-               pcm->plugin_alloc_size[idx] = size;
-               pcm->plugin_alloc_lock[idx] = 1;
-               return pcm->plugin_alloc_ptr[idx] = ptr;
+               plug->alloc_size[idx] = size;
+               plug->alloc_lock[idx] = 1;
+               return plug->alloc_ptr[idx] = ptr;
        }
-       for (idx = 0; idx < 4; idx++) {
-               if (pcm->plugin_alloc_ptr[idx] != NULL)
+       for (idx = 0; idx < 2; idx++) {
+               if (plug->alloc_ptr[idx] != NULL)
                        continue;
                ptr = malloc(size);
                if (ptr == NULL)
                        continue;
-               pcm->plugin_alloc_size[idx] = size;
-               pcm->plugin_alloc_lock[idx] = 1;
-               return pcm->plugin_alloc_ptr[idx] = ptr;
+               plug->alloc_size[idx] = size;
+               plug->alloc_lock[idx] = 1;
+               return plug->alloc_ptr[idx] = ptr;
        }
        return NULL;
 }
 
-static void snd_pcm_plugin_buf_free(snd_pcm_t *pcm, void *ptr)
+static void snd_pcm_plugin_buf_free(snd_pcm_t *pcm, int channel, void *ptr)
 {
        int idx;
 
+       struct snd_pcm_plug *plug;
+
        if (pcm == NULL || ptr == NULL)
                return;
-       for (idx = 0; idx < 4; idx++) {
-               if (pcm->plugin_alloc_ptr[idx] == ptr) {
-                       pcm->plugin_alloc_lock[idx] = 0;
+       plug = &pcm->chan[channel].plug;
+       for (idx = 0; idx < 2; idx++) {
+               if (plug->alloc_ptr[idx] == ptr) {
+                       plug->alloc_lock[idx] = 0;
                        return;
                }
        }
 }
 
-static void *snd_pcm_plugin_ptr_alloc(snd_pcm_t *pcm, size_t size)
-{
-       void *ptr;
-       int channel = pcm->plugin_alloc_xchannel;
-
-       if (pcm->plugin_alloc_xptr[channel]) {
-               if (pcm->plugin_alloc_xsize[channel] >= size)
-                       return pcm->plugin_alloc_xptr[channel];
-               ptr = realloc(pcm->plugin_alloc_xptr[channel], size);
-       } else {
-               ptr = malloc(size);
-       }
-       if (ptr == NULL)
-               return NULL;
-       pcm->plugin_alloc_xptr[channel] = (char *)ptr;
-       pcm->plugin_alloc_xsize[channel] = size;                        
-       return ptr;
-}
-
 static int snd_pcm_plugin_xvoices(snd_pcm_plugin_t *plugin,
+                                 int channel,
                                  snd_pcm_plugin_voice_t **voices,
                                  size_t samples,
                                  snd_pcm_format_t *format)
@@ -857,7 +954,7 @@ static int snd_pcm_plugin_xvoices(snd_pcm_plugin_t *plugin,
        if ((size % 8) != 0)
                return -EINVAL;
        size /= 8;
-       ptr = (char *)snd_pcm_plugin_buf_alloc(plugin->handle, size);
+       ptr = (char *)snd_pcm_plugin_buf_alloc(plugin->handle, channel, size);
        if (ptr == NULL)
                return -ENOMEM;
        if ((size % format->voices) != 0)
@@ -884,12 +981,212 @@ int snd_pcm_plugin_src_voices(snd_pcm_plugin_t *plugin,
                              snd_pcm_plugin_voice_t **voices,
                              size_t samples)
 {
-       return snd_pcm_plugin_xvoices(plugin, voices, samples, &plugin->src_format);
+       return snd_pcm_plugin_xvoices(plugin, SND_PCM_CHANNEL_PLAYBACK, voices, samples, &plugin->src_format);
 }
 
 int snd_pcm_plugin_dst_voices(snd_pcm_plugin_t *plugin,
                              snd_pcm_plugin_voice_t **voices,
                              size_t samples)
 {
-       return snd_pcm_plugin_xvoices(plugin, voices, samples, &plugin->dst_format);
+       return snd_pcm_plugin_xvoices(plugin, SND_PCM_CHANNEL_CAPTURE, voices, samples, &plugin->dst_format);
+}
+
+static void *playback_mmap(void *data)
+{
+       snd_pcm_t *pcm = data;
+       struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+       struct snd_pcm_plug *plug = &chan->plug;
+       snd_pcm_mmap_control_t *control;
+       void *buffer;
+       int frags;
+       int frag_size, voice_size, voice_frag_size;
+       int voices;
+       control = plug->mmap_control;
+       buffer = plug->mmap_data;
+       frags = plug->setup.buf.block.frags;
+       frag_size = plug->setup.buf.block.frag_size;
+       voices = plug->setup.format.voices;
+       voice_size = plug->mmap_size / voices;
+       voice_frag_size = voice_size / frags;
+       while (1) {
+               int err;
+               struct pollfd pfd;
+               int frag = control->status.frag_io;
+               if (plug->thread_stop)
+                       break;
+               if (control->status.status != SND_PCM_STATUS_RUNNING &&
+                   control->status.status != SND_PCM_STATUS_PREPARED) {
+                       usleep(100000);
+                       continue;
+               }
+               pfd.fd = chan->fd;
+               pfd.events = POLLOUT;
+               pfd.revents = 0;
+               err = poll(&pfd, 1, -1);
+               if (err < 0) {
+                       fprintf(stderr, "error on poll\n");
+                       continue;
+               }
+               if (!control->fragments[frag].data) {
+                       usleep(10000);
+                       continue;
+               }
+               /* NYI: status.block */
+               control->fragments[frag].io = 1;
+               if (plug->setup.format.interleave) {
+                       err = snd_pcm_plugin_write(pcm, buffer + frag * frag_size, frag_size);
+               } else {
+                       struct iovec vector[voices];
+                       struct iovec *v = vector;
+                       int voice;
+                       for (voice = 0; voice < voices; ++voice) {
+                               v->iov_base = buffer + voice_size * voice + frag * voice_frag_size;
+                               v->iov_len = voice_frag_size;
+                               v++;
+                       }
+                       err = snd_pcm_plugin_writev(pcm, vector, voice_frag_size);
+               }
+               if (err <= 0) {
+                       control->fragments[frag].io = 0;
+                       snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK);
+                       continue;
+               }
+               control->fragments[frag].io = 0;
+               control->fragments[frag].data = 0;
+               frag++;
+               if (frag == frags)
+                       frag = 0;
+               control->status.frag_io = frag;
+       }
+       return 0;
+}
+
+static void *capture_mmap(void *data)
+{
+       snd_pcm_t *pcm = data;
+       struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+       struct snd_pcm_plug *plug = &chan->plug;
+       snd_pcm_mmap_control_t *control;
+       void *buffer;
+       int frag, frags;
+       int frag_size, voice_size, voice_frag_size;
+       int voices;
+       control = plug->mmap_control;
+       buffer = plug->mmap_data;
+       frag = 0;
+       frags = plug->setup.buf.block.frags;
+       frag_size = plug->setup.buf.block.frag_size;
+       voices = plug->setup.format.voices;
+       voice_size = plug->mmap_size / voices;
+       voice_frag_size = voice_size / frags;
+       while (1) {
+               int err;
+               struct pollfd pfd;
+               if (plug->thread_stop)
+                       break;
+               if (control->status.status != SND_PCM_STATUS_RUNNING) {
+                       usleep(100000);
+                       continue;
+               }
+               pfd.fd = chan->fd;
+               pfd.events = POLLIN;
+               pfd.revents = 0;
+               err = poll(&pfd, 1, -1);
+               if (err < 0) {
+                       printf("error on poll\n");
+                       continue;
+               }
+               if (control->fragments[frag].data) {
+                       usleep(10000);
+                       continue;
+               }
+               /* NYI: status.block */
+               control->status.frag_io = frag;
+               control->fragments[frag].io = 1;
+               if (plug->setup.format.interleave) {
+                       err = snd_pcm_plugin_read(pcm, buffer + frag * frag_size, frag_size);
+               } else {
+                       struct iovec vector[voices];
+                       struct iovec *v = vector;
+                       int voice;
+                       for (voice = 0; voice < voices; ++voice) {
+                               v->iov_base = buffer + voice_size * voice + frag * voice_frag_size;
+                               v->iov_len = voice_frag_size;
+                               v++;
+                       }
+                       err = snd_pcm_plugin_readv(pcm, vector, voice_frag_size);
+               }
+               if (err < 0) {
+                       snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_CAPTURE);
+                       continue;
+               }
+               control->fragments[frag].io = 0;
+               control->fragments[frag].data = 1;
+               frag++;
+               if (frag == frags)
+                       frag = 0;
+       }
+       return 0;
+}
+
+int snd_pcm_plugin_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, void **data)
+{
+       int err;
+
+       struct snd_pcm_plug *plug;
+       if (!pcm || channel < 0 || channel > 1 || !control || !data)
+               return -EINVAL;
+
+       plug = &pcm->chan[channel].plug;
+       if (plug->first == NULL)
+               return snd_pcm_mmap(pcm, channel, control, data);
+       if (!plug->setup_is_valid)
+               return -EBADFD;
+       if (plug->setup.mode != SND_PCM_MODE_BLOCK)
+               return -EINVAL;
+
+       if (plug->mmap_data) {
+               *control = plug->mmap_control;
+               *data = plug->mmap_data;
+               return 0;
+       }
+
+       plug->mmap_control = malloc(sizeof(snd_pcm_mmap_control_t));
+       plug->mmap_size = plug->setup.buf.block.frag_size * plug->setup.buf.block.frags;
+       plug->mmap_data = malloc(plug->mmap_size);
+
+       mmap_clear(plug);
+
+       *control = plug->mmap_control;
+       *data = plug->mmap_data;
+
+       plug->thread_stop = 0;
+       if (channel == SND_PCM_CHANNEL_PLAYBACK)
+               err = pthread_create(&plug->thread, NULL, playback_mmap, pcm);
+       else
+               err = pthread_create(&plug->thread, NULL, capture_mmap, pcm);
+       if (err < 0) {
+               snd_pcm_plugin_munmap(pcm, channel);
+               return err;
+       }
+       return 0;
+}
+
+int snd_pcm_plugin_munmap(snd_pcm_t *pcm, int channel)
+{
+       struct snd_pcm_plug *plug;
+       if (!pcm || channel < 0 || channel > 1)
+               return -EINVAL;
+       plug = &pcm->chan[channel].plug;
+       if (plug->first == NULL)
+               return snd_pcm_munmap(pcm, channel);
+       if (plug->mmap_data) {
+               plug->thread_stop = 1;
+               pthread_join(plug->thread, NULL);
+               free(plug->mmap_control);
+               plug->mmap_control = 0;
+               free(plug->mmap_data);
+               plug->mmap_data = 0;
+       }
+       return 0;
 }
index f38f990e089938d3e2846d8b40e3374506e0bc42..421c530b5aac3edc098d57aff621518e4c501d6f 100644 (file)
@@ -75,7 +75,7 @@ static int poll_playback(snd_pcm_t *pcm)
        
        if (pcm->mode & SND_PCM_OPEN_NONBLOCK)
                return -EAGAIN;
-       pfd.fd = pcm->fd[SND_PCM_CHANNEL_PLAYBACK];
+       pfd.fd = pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd;
        pfd.events = POLLOUT;
        pfd.revents = 0;
        err = poll(&pfd, 1, 1000);
@@ -146,7 +146,7 @@ static int poll_capture(snd_pcm_t *pcm)
 
        if (pcm->mode & SND_PCM_OPEN_NONBLOCK)
                return -EAGAIN;
-       pfd.fd = pcm->fd[SND_PCM_CHANNEL_CAPTURE];
+       pfd.fd = pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd;
        pfd.events = POLLIN;
        pfd.revents = 0;
        err = poll(&pfd, 1, 1000);
@@ -185,8 +185,7 @@ static int query_capture(snd_pcm_plugin_t *plugin, int not_use_poll)
 
 static int mmap_src_voices(snd_pcm_plugin_t *plugin,
                           snd_pcm_plugin_voice_t **voices,
-                          size_t samples,
-                          void *(*plugin_alloc)(snd_pcm_plugin_handle_t *handle, size_t size))
+                          size_t samples)
 {
        mmap_t *data;
        int err;
@@ -221,8 +220,7 @@ static int mmap_src_voices(snd_pcm_plugin_t *plugin,
 
 static int mmap_dst_voices(snd_pcm_plugin_t *plugin,
                           snd_pcm_plugin_voice_t **voices,
-                          size_t samples,
-                          void *(*plugin_alloc)(snd_pcm_plugin_handle_t *handle, size_t size))
+                          size_t samples)
 {
        mmap_t *data;
        int voice;
@@ -275,7 +273,7 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
                size = snd_pcm_plugin_src_samples_to_size(plugin, samples);
                if (size != data->frag_size)
                        return -EINVAL;
-               if (src_voices != data->voices) {
+               if (plugin->prev == NULL) {
                        if (plugin->src_format.interleave) {
                                void *dst = data->voices[0].addr + data->frag * data->frag_size;
                                /* Paranoia: add check for src_voices */
@@ -306,7 +304,7 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
                size = snd_pcm_plugin_dst_samples_to_size(plugin, samples);
                if (size != data->frag_size)
                        return -EINVAL;
-               if (dst_voices != data->voices) {
+               if (plugin->next == NULL) {
                        if (plugin->dst_format.interleave) {
                                void *src = data->voices[0].addr + data->frag * data->frag_size;
                                /* Paranoia: add check for dst_voices */