1999-11-06 23:47:07 +00:00
|
|
|
/*
|
|
|
|
|
* PCM MMAP Plug-In Interface
|
|
|
|
|
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* 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 <errno.h>
|
2000-01-28 22:45:18 +00:00
|
|
|
#include <sys/poll.h>
|
2000-03-29 20:26:06 +00:00
|
|
|
#include <sys/uio.h>
|
1999-11-06 23:47:07 +00:00
|
|
|
#include "../pcm_local.h"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Basic mmap plugin
|
|
|
|
|
*/
|
|
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
typedef struct mmap_private_data {
|
1999-11-06 23:47:07 +00:00
|
|
|
int channel;
|
|
|
|
|
snd_pcm_mmap_control_t *control;
|
|
|
|
|
char *buffer;
|
|
|
|
|
int frag;
|
2000-03-21 17:36:27 +00:00
|
|
|
int start_mode, stop_mode;
|
|
|
|
|
int frags, frags_used;
|
|
|
|
|
int frags_min, frags_max;
|
|
|
|
|
unsigned int lastblock;
|
2000-03-29 20:26:06 +00:00
|
|
|
struct iovec *vector;
|
|
|
|
|
int vector_count;
|
|
|
|
|
} mmap_t;
|
1999-11-06 23:47:07 +00:00
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
static int playback_ok(snd_pcm_plugin_t *plugin)
|
2000-03-21 17:36:27 +00:00
|
|
|
{
|
2000-03-29 20:26:06 +00:00
|
|
|
mmap_t *data = (mmap_t *)plugin->extra_data;
|
2000-03-21 17:36:27 +00:00
|
|
|
snd_pcm_mmap_control_t *control = data->control;
|
|
|
|
|
int delta = control->status.block;
|
|
|
|
|
|
|
|
|
|
if (delta < data->lastblock) {
|
|
|
|
|
delta += (~0 - data->lastblock) + 1;
|
|
|
|
|
} else {
|
|
|
|
|
delta -= data->lastblock;
|
|
|
|
|
}
|
|
|
|
|
data->frags_used -= delta;
|
|
|
|
|
if (data->frags_used < 0) {
|
|
|
|
|
/* correction for rollover */
|
|
|
|
|
data->frag += -data->frags_used;
|
|
|
|
|
data->frag %= data->frags;
|
|
|
|
|
data->frags_used = 0;
|
|
|
|
|
}
|
|
|
|
|
data->lastblock += delta;
|
|
|
|
|
return data->frags_used <= data->frags_max &&
|
|
|
|
|
(data->frags - data->frags_used) >= data->frags_min;
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-28 22:45:18 +00:00
|
|
|
static int poll_playback(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct pollfd pfd;
|
|
|
|
|
|
|
|
|
|
if (pcm->mode & SND_PCM_OPEN_NONBLOCK)
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
pfd.fd = pcm->fd[SND_PCM_CHANNEL_PLAYBACK];
|
|
|
|
|
pfd.events = POLLOUT;
|
|
|
|
|
pfd.revents = 0;
|
|
|
|
|
err = poll(&pfd, 1, 1000);
|
|
|
|
|
return err < 0 ? err : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
static int query_playback(snd_pcm_plugin_t *plugin, int not_use_poll)
|
2000-01-28 22:45:18 +00:00
|
|
|
{
|
2000-03-29 20:26:06 +00:00
|
|
|
mmap_t *data = (mmap_t *)plugin->extra_data;
|
2000-03-21 17:36:27 +00:00
|
|
|
snd_pcm_mmap_control_t *control = data->control;
|
2000-01-28 22:45:18 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
switch (control->status.status) {
|
|
|
|
|
case SND_PCM_STATUS_PREPARED:
|
2000-03-21 17:36:27 +00:00
|
|
|
if (data->start_mode == SND_PCM_START_GO)
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
if ((data->start_mode == SND_PCM_START_DATA &&
|
2000-03-29 20:26:06 +00:00
|
|
|
playback_ok(plugin)) ||
|
2000-03-21 17:36:27 +00:00
|
|
|
(data->start_mode == SND_PCM_START_FULL &&
|
|
|
|
|
data->frags_used == data->frags)) {
|
2000-03-29 20:26:06 +00:00
|
|
|
err = snd_pcm_channel_go(plugin->handle, data->channel);
|
2000-03-21 17:36:27 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2000-01-28 22:45:18 +00:00
|
|
|
break;
|
|
|
|
|
case SND_PCM_STATUS_RUNNING:
|
|
|
|
|
if (!not_use_poll) {
|
|
|
|
|
control->status.expblock = control->status.block + 1;
|
2000-03-29 20:26:06 +00:00
|
|
|
err = poll_playback(plugin->handle);
|
2000-01-28 22:45:18 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_STATUS_UNDERRUN:
|
2000-02-22 17:36:34 +00:00
|
|
|
return -EPIPE;
|
2000-01-28 22:45:18 +00:00
|
|
|
default:
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
static int capture_ok(snd_pcm_plugin_t *plugin)
|
2000-03-21 17:36:27 +00:00
|
|
|
{
|
2000-03-29 20:26:06 +00:00
|
|
|
mmap_t *data = (mmap_t *)plugin->extra_data;
|
2000-03-21 17:36:27 +00:00
|
|
|
snd_pcm_mmap_control_t *control = data->control;
|
|
|
|
|
int delta = control->status.block;
|
|
|
|
|
|
|
|
|
|
if (delta < data->lastblock) {
|
|
|
|
|
delta += (~0 - data->lastblock) + 1;
|
|
|
|
|
} else {
|
|
|
|
|
delta -= data->lastblock;
|
|
|
|
|
}
|
|
|
|
|
data->frags_used += delta;
|
|
|
|
|
if (data->frags_used > data->frags) {
|
|
|
|
|
/* correction for rollover */
|
|
|
|
|
data->frag += data->frags_used - data->frags;
|
|
|
|
|
data->frag %= data->frags;
|
|
|
|
|
data->frags_used = data->frags;
|
|
|
|
|
}
|
|
|
|
|
data->lastblock += delta;
|
|
|
|
|
return data->frags_used >= data->frags_min;
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-28 22:45:18 +00:00
|
|
|
static int poll_capture(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct pollfd pfd;
|
|
|
|
|
|
|
|
|
|
if (pcm->mode & SND_PCM_OPEN_NONBLOCK)
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
pfd.fd = pcm->fd[SND_PCM_CHANNEL_CAPTURE];
|
|
|
|
|
pfd.events = POLLIN;
|
|
|
|
|
pfd.revents = 0;
|
|
|
|
|
err = poll(&pfd, 1, 1000);
|
|
|
|
|
return err < 0 ? err : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
static int query_capture(snd_pcm_plugin_t *plugin, int not_use_poll)
|
2000-01-28 22:45:18 +00:00
|
|
|
{
|
2000-03-29 20:26:06 +00:00
|
|
|
mmap_t *data = (mmap_t *)plugin->extra_data;
|
2000-03-21 17:36:27 +00:00
|
|
|
snd_pcm_mmap_control_t *control = data->control;
|
2000-01-28 22:45:18 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
switch (control->status.status) {
|
|
|
|
|
case SND_PCM_STATUS_PREPARED:
|
2000-03-21 17:36:27 +00:00
|
|
|
if (data->start_mode != SND_PCM_START_DATA)
|
|
|
|
|
return -EAGAIN;
|
2000-03-29 20:26:06 +00:00
|
|
|
err = snd_pcm_channel_go(plugin->handle, data->channel);
|
2000-01-28 22:45:18 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_STATUS_RUNNING:
|
|
|
|
|
if (!not_use_poll) {
|
2000-03-21 17:36:27 +00:00
|
|
|
control->status.expblock = control->status.block + data->frags_min;
|
2000-03-29 20:26:06 +00:00
|
|
|
err = poll_capture(plugin->handle);
|
2000-01-28 22:45:18 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_STATUS_OVERRUN:
|
2000-02-22 17:36:34 +00:00
|
|
|
return -EPIPE;
|
2000-01-28 22:45:18 +00:00
|
|
|
default:
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
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))
|
1999-11-06 23:47:07 +00:00
|
|
|
{
|
2000-03-29 20:26:06 +00:00
|
|
|
mmap_t *data;
|
1999-11-06 23:47:07 +00:00
|
|
|
snd_pcm_mmap_control_t *control;
|
2000-03-29 20:26:06 +00:00
|
|
|
snd_pcm_plugin_voice_t *v;
|
|
|
|
|
int err;
|
1999-11-06 23:47:07 +00:00
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
if (plugin == NULL || voices == NULL)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
*voices = NULL;
|
|
|
|
|
data = (mmap_t *)plugin->extra_data;
|
|
|
|
|
if (data->channel != SND_PCM_CHANNEL_PLAYBACK)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
if ((control = data->control) == NULL)
|
|
|
|
|
return -EBADFD;
|
|
|
|
|
/* wait until the block is not free */
|
|
|
|
|
while (!playback_ok(plugin)) {
|
|
|
|
|
err = query_playback(plugin, 0);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
v = plugin->voices;
|
|
|
|
|
if (plugin->src_format.interleave) {
|
|
|
|
|
void *addr;
|
|
|
|
|
int voice;
|
|
|
|
|
if (control->status.frag_size != snd_pcm_plugin_dst_samples_to_size(plugin, samples))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
addr = data->buffer + control->fragments[data->frag].addr;
|
|
|
|
|
for (voice = 0; voice < plugin->src_format.voices; voice++) {
|
|
|
|
|
v->aptr = NULL;
|
|
|
|
|
v->addr = addr;
|
|
|
|
|
v->offset = voice * plugin->src_width;
|
|
|
|
|
v->next = plugin->src_format.voices * plugin->src_width;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
int frag, voice;
|
|
|
|
|
if (control->status.frag_size != snd_pcm_plugin_src_samples_to_size(plugin, samples) / plugin->src_format.voices)
|
1999-11-06 23:47:07 +00:00
|
|
|
return -EINVAL;
|
2000-03-29 20:26:06 +00:00
|
|
|
for (voice = 0; voice < plugin->src_format.voices; voice++) {
|
|
|
|
|
frag = data->frag + (voice * data->frags);
|
|
|
|
|
v->aptr = NULL;
|
|
|
|
|
v->addr = data->buffer + control->fragments[frag].addr;
|
|
|
|
|
v->offset = 0;
|
|
|
|
|
v->next = plugin->src_width;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*voices = plugin->voices;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
{
|
|
|
|
|
mmap_t *data;
|
|
|
|
|
snd_pcm_mmap_control_t *control;
|
|
|
|
|
snd_pcm_plugin_voice_t *v;
|
|
|
|
|
|
|
|
|
|
if (plugin == NULL || voices == NULL)
|
1999-11-06 23:47:07 +00:00
|
|
|
return -EINVAL;
|
2000-03-29 20:26:06 +00:00
|
|
|
*voices = NULL;
|
|
|
|
|
data = (mmap_t *)plugin->extra_data;
|
|
|
|
|
if (data->channel != SND_PCM_CHANNEL_CAPTURE)
|
1999-11-06 23:47:07 +00:00
|
|
|
return -EINVAL;
|
2000-03-29 20:26:06 +00:00
|
|
|
if ((control = data->control) == NULL)
|
|
|
|
|
return -EBADFD;
|
|
|
|
|
v = plugin->voices;
|
|
|
|
|
if (plugin->dst_format.interleave) {
|
|
|
|
|
void *addr;
|
|
|
|
|
int voice;
|
|
|
|
|
if (control->status.frag_size != snd_pcm_plugin_dst_samples_to_size(plugin, samples))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
addr = data->buffer + control->fragments[data->frag].addr;
|
|
|
|
|
for (voice = 0; voice < plugin->dst_format.voices; voice++) {
|
|
|
|
|
v->addr = addr;
|
|
|
|
|
v->offset = voice * plugin->src_width;
|
|
|
|
|
v->next = plugin->dst_format.voices * plugin->dst_width;
|
1999-11-06 23:47:07 +00:00
|
|
|
}
|
|
|
|
|
} else {
|
2000-03-29 20:26:06 +00:00
|
|
|
int frag, voice;
|
|
|
|
|
if (control->status.frag_size != snd_pcm_plugin_dst_samples_to_size(plugin, samples) / plugin->dst_format.voices)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
for (voice = 0; voice < plugin->dst_format.voices; voice++) {
|
|
|
|
|
frag = data->frag + (voice * data->frags);
|
|
|
|
|
v->addr = data->buffer + control->fragments[frag].addr;
|
|
|
|
|
v->offset = 0;
|
|
|
|
|
v->next = plugin->dst_width;
|
|
|
|
|
}
|
1999-11-06 23:47:07 +00:00
|
|
|
}
|
2000-03-29 20:26:06 +00:00
|
|
|
*voices = plugin->voices;
|
1999-11-06 23:47:07 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
|
2000-03-29 20:26:06 +00:00
|
|
|
const snd_pcm_plugin_voice_t *src_voices,
|
|
|
|
|
const snd_pcm_plugin_voice_t *dst_voices,
|
|
|
|
|
size_t samples)
|
1999-11-06 23:47:07 +00:00
|
|
|
{
|
2000-03-29 20:26:06 +00:00
|
|
|
mmap_t *data;
|
1999-11-06 23:47:07 +00:00
|
|
|
snd_pcm_mmap_control_t *control;
|
2000-03-29 20:26:06 +00:00
|
|
|
ssize_t size;
|
|
|
|
|
int voice, err;
|
1999-11-06 23:47:07 +00:00
|
|
|
char *addr;
|
|
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
if (plugin == NULL)
|
1999-11-06 23:47:07 +00:00
|
|
|
return -EINVAL;
|
2000-03-29 20:26:06 +00:00
|
|
|
data = (mmap_t *)plugin->extra_data;
|
1999-11-06 23:47:07 +00:00
|
|
|
control = data->control;
|
|
|
|
|
if (control == NULL)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
2000-03-29 20:26:06 +00:00
|
|
|
if (src_voices == NULL)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
while (!playback_ok(plugin)) {
|
|
|
|
|
err = query_playback(plugin, 0);
|
2000-01-28 22:45:18 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2000-03-29 20:26:06 +00:00
|
|
|
size = snd_pcm_plugin_src_samples_to_size(plugin, samples);
|
|
|
|
|
if (size < 0)
|
|
|
|
|
return size;
|
|
|
|
|
if (plugin->src_format.interleave) {
|
|
|
|
|
if (size != control->status.frag_size)
|
|
|
|
|
return -EINVAL;
|
1999-11-06 23:47:07 +00:00
|
|
|
addr = data->buffer + control->fragments[data->frag].addr;
|
2000-03-29 20:26:06 +00:00
|
|
|
if (src_voices->addr != addr)
|
|
|
|
|
memcpy(addr, src_voices->addr, size);
|
1999-11-06 23:47:07 +00:00
|
|
|
control->fragments[data->frag++].data = 1;
|
|
|
|
|
data->frag %= control->status.frags;
|
2000-03-21 17:36:27 +00:00
|
|
|
data->frags_used++;
|
2000-03-29 20:26:06 +00:00
|
|
|
return samples;
|
1999-11-06 23:47:07 +00:00
|
|
|
} else {
|
|
|
|
|
int frag;
|
|
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
if ((size / plugin->src_format.voices) != control->status.frag_size)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
for (voice = 0; voice < plugin->src_format.voices; voice++) {
|
2000-03-21 17:36:27 +00:00
|
|
|
frag = data->frag + (voice * data->frags);
|
1999-11-06 23:47:07 +00:00
|
|
|
while (control->fragments[frag].data) {
|
2000-03-29 20:26:06 +00:00
|
|
|
err = query_playback(plugin, 1);
|
2000-01-28 22:45:18 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
1999-11-06 23:47:07 +00:00
|
|
|
}
|
|
|
|
|
addr = data->buffer + control->fragments[frag].addr;
|
2000-03-29 20:26:06 +00:00
|
|
|
if (src_voices[voice].addr != addr)
|
|
|
|
|
memcpy(addr, src_voices[voice].addr, control->status.frag_size);
|
1999-11-06 23:47:07 +00:00
|
|
|
control->fragments[frag].data = 1;
|
|
|
|
|
}
|
|
|
|
|
data->frag++;
|
2000-03-21 17:36:27 +00:00
|
|
|
data->frag %= data->frags;
|
|
|
|
|
data->frags_used++;
|
2000-03-29 20:26:06 +00:00
|
|
|
return samples;
|
1999-11-06 23:47:07 +00:00
|
|
|
}
|
|
|
|
|
} else if (data->channel == SND_PCM_CHANNEL_CAPTURE) {
|
2000-03-29 20:26:06 +00:00
|
|
|
if (dst_voices == NULL)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
while (!capture_ok(plugin)) {
|
|
|
|
|
err = query_capture(plugin, 0);
|
2000-01-28 22:45:18 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2000-03-29 20:26:06 +00:00
|
|
|
size = snd_pcm_plugin_dst_samples_to_size(plugin, samples);
|
|
|
|
|
if (size < 0)
|
|
|
|
|
return size;
|
|
|
|
|
if (plugin->dst_format.interleave) {
|
|
|
|
|
if (size != control->status.frag_size)
|
|
|
|
|
return -EINVAL;
|
1999-11-06 23:47:07 +00:00
|
|
|
addr = data->buffer + control->fragments[data->frag].addr;
|
2000-03-29 20:26:06 +00:00
|
|
|
if (dst_voices->addr != addr)
|
|
|
|
|
memcpy(dst_voices->addr, addr, size);
|
1999-11-06 23:47:07 +00:00
|
|
|
control->fragments[data->frag++].data = 0;
|
|
|
|
|
data->frag %= control->status.frags;
|
2000-03-21 17:36:27 +00:00
|
|
|
data->frags_used--;
|
2000-03-29 20:26:06 +00:00
|
|
|
return samples;
|
1999-11-06 23:47:07 +00:00
|
|
|
} else {
|
2000-03-21 17:36:27 +00:00
|
|
|
int frag;
|
|
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
if ((size / plugin->dst_format.voices) != control->status.frag_size)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
for (voice = 0; voice < plugin->dst_format.voices; voice++) {
|
2000-03-21 17:36:27 +00:00
|
|
|
frag = data->frag + (voice * data->frags);
|
1999-11-06 23:47:07 +00:00
|
|
|
while (!control->fragments[data->frag].data) {
|
2000-03-29 20:26:06 +00:00
|
|
|
err = query_capture(plugin, 1);
|
2000-01-28 22:45:18 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
1999-11-06 23:47:07 +00:00
|
|
|
}
|
2000-03-21 17:36:27 +00:00
|
|
|
addr = data->buffer + control->fragments[frag].addr;
|
2000-03-29 20:26:06 +00:00
|
|
|
if (dst_voices[voice].addr != addr)
|
|
|
|
|
memcpy(dst_voices[voice].addr, addr, control->status.frag_size);
|
2000-03-21 17:36:27 +00:00
|
|
|
control->fragments[frag].data = 0;
|
1999-11-06 23:47:07 +00:00
|
|
|
}
|
2000-03-21 17:36:27 +00:00
|
|
|
data->frag++;
|
|
|
|
|
data->frag %= data->frags;
|
|
|
|
|
data->frags_used--;
|
2000-03-29 20:26:06 +00:00
|
|
|
return samples;
|
1999-11-06 23:47:07 +00:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2000-03-21 17:36:27 +00:00
|
|
|
static int mmap_action(snd_pcm_plugin_t *plugin,
|
|
|
|
|
snd_pcm_plugin_action_t action,
|
|
|
|
|
unsigned long udata)
|
1999-11-06 23:47:07 +00:00
|
|
|
{
|
|
|
|
|
struct mmap_private_data *data;
|
|
|
|
|
|
|
|
|
|
if (plugin == NULL)
|
|
|
|
|
return -EINVAL;
|
2000-03-29 20:26:06 +00:00
|
|
|
data = (mmap_t *)plugin->extra_data;
|
1999-11-06 23:47:07 +00:00
|
|
|
if (action == INIT) {
|
2000-03-21 17:36:27 +00:00
|
|
|
snd_pcm_channel_params_t *params;
|
|
|
|
|
snd_pcm_channel_setup_t setup;
|
2000-03-29 20:26:06 +00:00
|
|
|
int result;
|
2000-03-21 17:36:27 +00:00
|
|
|
|
1999-11-06 23:47:07 +00:00
|
|
|
if (data->control)
|
2000-03-29 20:26:06 +00:00
|
|
|
snd_pcm_munmap(plugin->handle, data->channel);
|
|
|
|
|
result = snd_pcm_mmap(plugin->handle, data->channel, &data->control, (void **)&data->buffer);
|
2000-03-21 17:36:27 +00:00
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
params = (snd_pcm_channel_params_t *)udata;
|
|
|
|
|
data->start_mode = params->start_mode;
|
|
|
|
|
data->stop_mode = params->stop_mode;
|
|
|
|
|
memset(&setup, 0, sizeof(setup));
|
|
|
|
|
setup.channel = data->channel;
|
2000-03-29 20:26:06 +00:00
|
|
|
if ((result = snd_pcm_channel_setup(plugin->handle, &setup)) < 0)
|
2000-03-21 17:36:27 +00:00
|
|
|
return result;
|
|
|
|
|
data->frags = setup.buf.block.frags;
|
|
|
|
|
data->frags_min = setup.buf.block.frags_min;
|
|
|
|
|
data->frags_max = setup.buf.block.frags_max;
|
|
|
|
|
if (data->frags_min < 0)
|
|
|
|
|
data->frags_min = 0;
|
|
|
|
|
if (data->frags_min >= setup.buf.block.frags)
|
|
|
|
|
data->frags_min = setup.buf.block.frags - 1;
|
|
|
|
|
if (data->frags_max < 0)
|
|
|
|
|
data->frags_max = setup.buf.block.frags + data->frags_max;
|
|
|
|
|
if (data->frags_max < data->frags_min)
|
|
|
|
|
data->frags_max = data->frags_min;
|
|
|
|
|
if (data->frags_max < 1)
|
|
|
|
|
data->frags_max = 1;
|
|
|
|
|
if (data->frags_max > setup.buf.block.frags)
|
|
|
|
|
data->frags_max = setup.buf.block.frags;
|
|
|
|
|
return 0;
|
1999-11-08 23:19:36 +00:00
|
|
|
} else if (action == PREPARE) {
|
|
|
|
|
data->frag = 0;
|
2000-03-21 17:36:27 +00:00
|
|
|
data->lastblock = 0;
|
1999-11-06 23:47:07 +00:00
|
|
|
} else if (action == DRAIN && data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
|
|
|
|
data->frag = 0;
|
2000-03-21 17:36:27 +00:00
|
|
|
data->lastblock = 0;
|
1999-11-06 23:47:07 +00:00
|
|
|
} else if (action == FLUSH) {
|
|
|
|
|
data->frag = 0;
|
2000-03-21 17:36:27 +00:00
|
|
|
data->lastblock = 0;
|
1999-11-06 23:47:07 +00:00
|
|
|
}
|
|
|
|
|
return 0; /* silenty ignore other actions */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mmap_free(snd_pcm_plugin_t *plugin, void *private_data)
|
|
|
|
|
{
|
|
|
|
|
struct mmap_private_data *data;
|
|
|
|
|
|
|
|
|
|
if (plugin == NULL)
|
|
|
|
|
return;
|
2000-03-29 20:26:06 +00:00
|
|
|
data = (mmap_t *)plugin->extra_data;
|
1999-11-06 23:47:07 +00:00
|
|
|
if (data->control)
|
2000-03-29 20:26:06 +00:00
|
|
|
snd_pcm_munmap(plugin->handle, data->channel);
|
1999-11-06 23:47:07 +00:00
|
|
|
}
|
|
|
|
|
|
2000-03-29 20:26:06 +00:00
|
|
|
int snd_pcm_plugin_build_mmap(snd_pcm_t *pcm, int channel,
|
|
|
|
|
snd_pcm_format_t *format,
|
|
|
|
|
snd_pcm_plugin_t **r_plugin)
|
1999-11-06 23:47:07 +00:00
|
|
|
{
|
2000-03-29 20:26:06 +00:00
|
|
|
mmap_t *data;
|
1999-11-06 23:47:07 +00:00
|
|
|
snd_pcm_plugin_t *plugin;
|
|
|
|
|
|
|
|
|
|
if (r_plugin == NULL)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
*r_plugin = NULL;
|
|
|
|
|
if (!pcm || channel < 0 || channel > 1)
|
|
|
|
|
return -EINVAL;
|
2000-03-29 20:26:06 +00:00
|
|
|
plugin = snd_pcm_plugin_build(pcm,
|
|
|
|
|
channel == SND_PCM_CHANNEL_PLAYBACK ?
|
1999-11-06 23:47:07 +00:00
|
|
|
"I/O mmap playback" :
|
|
|
|
|
"I/O mmap capture",
|
2000-03-29 20:26:06 +00:00
|
|
|
format, format,
|
|
|
|
|
sizeof(mmap_t));
|
1999-11-06 23:47:07 +00:00
|
|
|
if (plugin == NULL)
|
|
|
|
|
return -ENOMEM;
|
2000-03-29 20:26:06 +00:00
|
|
|
data = (mmap_t *)plugin->extra_data;
|
1999-11-06 23:47:07 +00:00
|
|
|
data->channel = channel;
|
2000-03-29 20:26:06 +00:00
|
|
|
plugin->src_voices = mmap_src_voices;
|
|
|
|
|
plugin->dst_voices = mmap_dst_voices;
|
1999-11-06 23:47:07 +00:00
|
|
|
plugin->transfer = mmap_transfer;
|
|
|
|
|
plugin->action = mmap_action;
|
|
|
|
|
plugin->private_free = mmap_free;
|
|
|
|
|
*r_plugin = plugin;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|