2000-05-08 18:53:38 +00:00
|
|
|
/*
|
|
|
|
|
* PCM - Plug
|
|
|
|
|
* 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 <errno.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <sys/uio.h>
|
|
|
|
|
#include "pcm_local.h"
|
|
|
|
|
|
|
|
|
|
/* snd_pcm_plug helpers */
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
void *snd_pcm_plug_buf_alloc(snd_pcm_t *pcm, int stream, size_t size)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
int idx;
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr = &plug->stream[stream];
|
2000-05-08 18:53:38 +00:00
|
|
|
|
|
|
|
|
for (idx = 0; idx < 2; idx++) {
|
2000-05-27 16:52:17 +00:00
|
|
|
if (plugstr->alloc_lock[idx])
|
2000-05-08 18:53:38 +00:00
|
|
|
continue;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (plugstr->alloc_size[idx] >= size) {
|
|
|
|
|
plugstr->alloc_lock[idx] = 1;
|
|
|
|
|
return plugstr->alloc_ptr[idx];
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (idx = 0; idx < 2; idx++) {
|
2000-05-27 16:52:17 +00:00
|
|
|
if (plugstr->alloc_lock[idx])
|
2000-05-08 18:53:38 +00:00
|
|
|
continue;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (plugstr->alloc_ptr[idx] != NULL)
|
|
|
|
|
free(plugstr->alloc_ptr[idx]);
|
|
|
|
|
plugstr->alloc_ptr[idx] = malloc(size);
|
|
|
|
|
if (plugstr->alloc_ptr[idx] == NULL)
|
2000-05-08 18:53:38 +00:00
|
|
|
return NULL;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr->alloc_size[idx] = size;
|
|
|
|
|
plugstr->alloc_lock[idx] = 1;
|
|
|
|
|
return plugstr->alloc_ptr[idx];
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
void snd_pcm_plug_buf_unlock(snd_pcm_t *pcm, int stream, void *ptr)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
|
|
snd_pcm_plug_t *plug;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr;
|
2000-05-08 18:53:38 +00:00
|
|
|
|
|
|
|
|
if (!ptr)
|
|
|
|
|
return;
|
|
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr = &plug->stream[stream];
|
2000-05-08 18:53:38 +00:00
|
|
|
|
|
|
|
|
for (idx = 0; idx < 2; idx++) {
|
2000-05-27 16:52:17 +00:00
|
|
|
if (plugstr->alloc_ptr[idx] == ptr) {
|
|
|
|
|
plugstr->alloc_lock[idx] = 0;
|
2000-05-08 18:53:38 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* snd_pcm_plugin externs */
|
|
|
|
|
|
|
|
|
|
int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr;
|
2000-05-08 18:53:38 +00:00
|
|
|
snd_pcm_t *pcm;
|
|
|
|
|
if (!plugin)
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
pcm = plugin->handle;
|
|
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr = &plug->stream[plugin->stream];
|
|
|
|
|
plugin->next = plugstr->first;
|
2000-05-08 18:53:38 +00:00
|
|
|
plugin->prev = NULL;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (plugstr->first) {
|
|
|
|
|
plugstr->first->prev = plugin;
|
|
|
|
|
plugstr->first = plugin;
|
2000-05-08 18:53:38 +00:00
|
|
|
} else {
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr->last =
|
|
|
|
|
plugstr->first = plugin;
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr;
|
2000-05-08 18:53:38 +00:00
|
|
|
snd_pcm_t *pcm;
|
|
|
|
|
if (!plugin)
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
pcm = plugin->handle;
|
|
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr = &plug->stream[plugin->stream];
|
2000-05-08 18:53:38 +00:00
|
|
|
|
|
|
|
|
plugin->next = NULL;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugin->prev = plugstr->last;
|
|
|
|
|
if (plugstr->last) {
|
|
|
|
|
plugstr->last->next = plugin;
|
|
|
|
|
plugstr->last = plugin;
|
2000-05-08 18:53:38 +00:00
|
|
|
} else {
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr->last =
|
|
|
|
|
plugstr->first = plugin;
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
int snd_pcm_plugin_remove_to(snd_pcm_plugin_t *plugin)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plugin_t *plugin1, *plugin1_prev;
|
|
|
|
|
snd_pcm_plug_t *plug;
|
|
|
|
|
snd_pcm_t *pcm;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr;
|
2000-05-08 18:53:38 +00:00
|
|
|
if (!plugin)
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
pcm = plugin->handle;
|
|
|
|
|
|
|
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr = &plug->stream[plugin->stream];
|
2000-05-08 18:53:38 +00:00
|
|
|
|
|
|
|
|
plugin1 = plugin;
|
|
|
|
|
while (plugin1->prev)
|
|
|
|
|
plugin1 = plugin1->prev;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (plugstr->first != plugin1)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EINVAL;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr->first = plugin;
|
2000-05-08 18:53:38 +00:00
|
|
|
plugin1 = plugin->prev;
|
|
|
|
|
plugin->prev = NULL;
|
|
|
|
|
while (plugin1) {
|
|
|
|
|
plugin1_prev = plugin1->prev;
|
|
|
|
|
snd_pcm_plugin_free(plugin1);
|
|
|
|
|
plugin1 = plugin1_prev;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
int snd_pcm_plug_remove_first(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plugin_t *plugin;
|
|
|
|
|
snd_pcm_plug_t *plug;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr;
|
2000-05-08 18:53:38 +00:00
|
|
|
if (!pcm)
|
|
|
|
|
return -EFAULT;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (stream < 0 || stream > 1)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EINVAL;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (!pcm->stream[stream].open)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EBADFD;
|
|
|
|
|
|
|
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr = &plug->stream[stream];
|
2000-05-08 18:53:38 +00:00
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
plugin = plugstr->first;
|
2000-05-08 18:53:38 +00:00
|
|
|
if (plugin->next) {
|
|
|
|
|
plugin = plugin->next;
|
|
|
|
|
} else {
|
2000-05-27 16:52:17 +00:00
|
|
|
return snd_pcm_plug_clear(pcm, stream);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
return snd_pcm_plugin_remove_to(plugin);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* snd_pcm_plug externs */
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
int snd_pcm_plug_clear(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plugin_t *plugin, *plugin_next;
|
|
|
|
|
snd_pcm_plug_t *plug;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr;
|
2000-05-08 18:53:38 +00:00
|
|
|
int idx;
|
|
|
|
|
|
|
|
|
|
if (!pcm)
|
|
|
|
|
return -EINVAL;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (stream < 0 || stream > 1)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EINVAL;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (!pcm->stream[stream].open)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EBADFD;
|
|
|
|
|
|
|
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr = &plug->stream[stream];
|
|
|
|
|
plugin = plugstr->first;
|
|
|
|
|
plugstr->first = NULL;
|
|
|
|
|
plugstr->last = NULL;
|
2000-05-08 18:53:38 +00:00
|
|
|
while (plugin) {
|
|
|
|
|
plugin_next = plugin->next;
|
|
|
|
|
snd_pcm_plugin_free(plugin);
|
|
|
|
|
plugin = plugin_next;
|
|
|
|
|
}
|
|
|
|
|
for (idx = 0; idx < 2; idx++) {
|
2000-05-27 16:52:17 +00:00
|
|
|
if (plugstr->alloc_ptr[idx]) {
|
|
|
|
|
free(plugstr->alloc_ptr[idx]);
|
|
|
|
|
plugstr->alloc_ptr[idx] = 0;
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr->alloc_size[idx] = 0;
|
|
|
|
|
plugstr->alloc_lock[idx] = 0;
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
snd_pcm_plugin_t *snd_pcm_plug_first(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr;
|
2000-05-08 18:53:38 +00:00
|
|
|
if (!pcm)
|
|
|
|
|
return NULL;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (stream < 0 || stream > 1)
|
2000-05-08 18:53:38 +00:00
|
|
|
return NULL;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (!pcm->stream[stream].open)
|
2000-05-08 18:53:38 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr = &plug->stream[stream];
|
2000-05-08 18:53:38 +00:00
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
return plugstr->first;
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
snd_pcm_plugin_t *snd_pcm_plug_last(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr;
|
2000-05-08 18:53:38 +00:00
|
|
|
if (!pcm)
|
|
|
|
|
return NULL;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (stream < 0 || stream > 1)
|
2000-05-08 18:53:38 +00:00
|
|
|
return NULL;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (!pcm->stream[stream].open)
|
2000-05-08 18:53:38 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr = &plug->stream[stream];
|
2000-05-08 18:53:38 +00:00
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
return plugstr->last;
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
int snd_pcm_plug_direct(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
2000-05-27 16:52:17 +00:00
|
|
|
return snd_pcm_plug_first(pcm, stream) == NULL;
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
2000-05-27 16:52:17 +00:00
|
|
|
double snd_pcm_plug_client_ratio(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
ssize_t client;
|
|
|
|
|
if (!pcm)
|
|
|
|
|
return -EFAULT;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (stream < 0 || stream > 1)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EINVAL;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (!pcm->stream[stream].open)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EBADFD;
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
client = snd_pcm_plug_client_size(pcm, stream, 1000000);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (client < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
return (double)client / (double)1000000;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
double snd_pcm_plug_slave_ratio(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
ssize_t slave;
|
|
|
|
|
if (!pcm)
|
|
|
|
|
return -EFAULT;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (stream < 0 || stream > 1)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EINVAL;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (!pcm->stream[stream].open)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EBADFD;
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
slave = snd_pcm_plug_slave_size(pcm, stream, 1000000);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (slave < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
return (double)slave / (double)1000000;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_close(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
snd_pcm_plug_clear(pcm, stream);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (plug->close_slave)
|
2000-05-27 16:52:17 +00:00
|
|
|
return snd_pcm_stream_close(plug->slave, stream);
|
2000-05-08 18:53:38 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_nonblock(snd_pcm_t *pcm, int stream, int nonblock)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
return snd_pcm_stream_nonblock(plug->slave, stream, nonblock);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
return snd_pcm_info(plug->slave, info);
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_info(snd_pcm_t *pcm, snd_pcm_stream_info_t *info)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_stream *str;
|
2000-05-08 18:53:38 +00:00
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
if ((err = snd_pcm_stream_info(plug->slave, info)) < 0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return err;
|
|
|
|
|
info->formats = snd_pcm_plug_formats(info->formats);
|
|
|
|
|
info->min_rate = 4000;
|
|
|
|
|
info->max_rate = 192000;
|
2000-05-27 16:52:17 +00:00
|
|
|
info->min_channels = 1;
|
|
|
|
|
info->max_channels = 32;
|
2000-05-08 18:53:38 +00:00
|
|
|
info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000;
|
2000-05-27 16:52:17 +00:00
|
|
|
info->flags |= SND_PCM_STREAM_INFO_INTERLEAVE | SND_PCM_STREAM_INFO_NONINTERLEAVE;
|
|
|
|
|
|
|
|
|
|
str = &pcm->stream[info->stream];
|
|
|
|
|
if (pcm->stream[info->stream].valid_setup) {
|
|
|
|
|
info->buffer_size = snd_pcm_plug_client_size(pcm, info->stream, info->buffer_size);
|
|
|
|
|
info->min_fragment_size = snd_pcm_plug_client_size(pcm, info->stream, info->min_fragment_size);
|
|
|
|
|
info->max_fragment_size = snd_pcm_plug_client_size(pcm, info->stream, info->max_fragment_size);
|
|
|
|
|
info->fragment_align = snd_pcm_plug_client_size(pcm, info->stream, info->fragment_align);
|
|
|
|
|
info->fifo_size = snd_pcm_plug_client_size(pcm, info->stream, info->fifo_size);
|
|
|
|
|
info->transfer_block_size = snd_pcm_plug_client_size(pcm, info->stream, info->transfer_block_size);
|
|
|
|
|
if (str->setup.mode == SND_PCM_MODE_FRAGMENT)
|
|
|
|
|
info->mmap_size = str->setup.buffer_size;
|
2000-05-08 18:53:38 +00:00
|
|
|
else
|
2000-05-27 16:52:17 +00:00
|
|
|
info->mmap_size = snd_pcm_plug_client_size(pcm, info->stream, info->mmap_size);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
2000-05-27 16:52:17 +00:00
|
|
|
if (!snd_pcm_plug_direct(pcm, info->stream))
|
|
|
|
|
info->flags &= ~(SND_PCM_STREAM_INFO_MMAP | SND_PCM_STREAM_INFO_MMAP_VALID);
|
2000-05-08 18:53:38 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_action(snd_pcm_t *pcm, int stream, int action,
|
2000-05-08 18:53:38 +00:00
|
|
|
unsigned long data)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plugin_t *plugin;
|
|
|
|
|
int err;
|
|
|
|
|
snd_pcm_plug_t *plug;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr;
|
2000-05-08 18:53:38 +00:00
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
plugstr = &plug->stream[stream];
|
2000-05-08 18:53:38 +00:00
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
plugin = plugstr->first;
|
2000-05-08 18:53:38 +00:00
|
|
|
while (plugin) {
|
|
|
|
|
if (plugin->action) {
|
|
|
|
|
if ((err = plugin->action(plugin, action, data))<0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
plugin = plugin->next;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_params(snd_pcm_t *pcm, snd_pcm_stream_params_t *params)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
2000-05-27 16:52:17 +00:00
|
|
|
snd_pcm_stream_params_t slave_params, params1;
|
|
|
|
|
snd_pcm_stream_info_t slave_info;
|
2000-05-08 18:53:38 +00:00
|
|
|
snd_pcm_plugin_t *plugin;
|
|
|
|
|
snd_pcm_plug_t *plug;
|
|
|
|
|
int err;
|
2000-05-27 16:52:17 +00:00
|
|
|
int stream = params->stream;
|
2000-05-08 18:53:38 +00:00
|
|
|
|
|
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* try to decide, if a conversion is required
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
memset(&slave_info, 0, sizeof(slave_info));
|
2000-05-27 16:52:17 +00:00
|
|
|
slave_info.stream = stream;
|
|
|
|
|
if ((err = snd_pcm_stream_info(plug->slave, &slave_info)) < 0) {
|
|
|
|
|
snd_pcm_plug_clear(pcm, stream);
|
2000-05-08 18:53:38 +00:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((err = snd_pcm_plug_slave_params(params, &slave_info, &slave_params)) < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
snd_pcm_plug_clear(pcm, stream);
|
2000-05-08 18:53:38 +00:00
|
|
|
|
|
|
|
|
/* add necessary plugins */
|
|
|
|
|
memcpy(¶ms1, params, sizeof(*params));
|
|
|
|
|
if ((err = snd_pcm_plug_format(pcm, ¶ms1, &slave_params)) < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
|
|
|
|
return snd_pcm_stream_params(plug->slave, params);
|
2000-05-08 18:53:38 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* I/O plugins
|
|
|
|
|
*/
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
if (slave_info.flags & SND_PCM_STREAM_INFO_MMAP) {
|
2000-05-23 12:52:06 +00:00
|
|
|
pdprintf("params mmap plugin\n");
|
2000-05-27 16:52:17 +00:00
|
|
|
err = snd_pcm_plugin_build_mmap(pcm, stream, plug->slave, &slave_params.format, &plugin);
|
2000-05-08 18:53:38 +00:00
|
|
|
} else {
|
2000-05-23 12:52:06 +00:00
|
|
|
pdprintf("params I/O plugin\n");
|
2000-05-27 16:52:17 +00:00
|
|
|
err = snd_pcm_plugin_build_io(pcm, stream, plug->slave, &slave_params.format, &plugin);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (stream == SND_PCM_STREAM_PLAYBACK) {
|
2000-05-08 18:53:38 +00:00
|
|
|
err = snd_pcm_plugin_append(plugin);
|
|
|
|
|
} else {
|
|
|
|
|
err = snd_pcm_plugin_insert(plugin);
|
|
|
|
|
}
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
snd_pcm_plugin_free(plugin);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* compute right sizes */
|
2000-05-27 16:52:17 +00:00
|
|
|
slave_params.buffer_size = snd_pcm_plug_slave_size(pcm, stream, slave_params.buffer_size);
|
|
|
|
|
slave_params.frag_size = snd_pcm_plug_slave_size(pcm, stream, slave_params.frag_size);
|
|
|
|
|
slave_params.bytes_fill_max = snd_pcm_plug_slave_size(pcm, stream, slave_params.bytes_fill_max);
|
|
|
|
|
slave_params.bytes_min = snd_pcm_plug_slave_size(pcm, stream, slave_params.bytes_min);
|
|
|
|
|
slave_params.bytes_xrun_max = snd_pcm_plug_slave_size(pcm, stream, slave_params.bytes_xrun_max);
|
|
|
|
|
slave_params.bytes_align = snd_pcm_plug_slave_size(pcm, stream, slave_params.bytes_align);
|
|
|
|
|
|
|
|
|
|
pdprintf("params requested params: format = %i, rate = %i, channels = %i\n", slave_params.format.format, slave_params.format.rate, slave_params.format.channels);
|
|
|
|
|
err = snd_pcm_stream_params(plug->slave, &slave_params);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
err = snd_pcm_plug_action(pcm, stream, INIT, 0);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_setup(snd_pcm_t *pcm, snd_pcm_stream_setup_t *setup)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_plug_stream *plugstr;
|
2000-05-08 18:53:38 +00:00
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
err = snd_pcm_stream_setup(plug->slave, setup);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, setup->stream))
|
2000-05-08 18:53:38 +00:00
|
|
|
return 0;
|
2000-05-23 12:52:06 +00:00
|
|
|
setup->byte_boundary /= setup->frag_size;
|
2000-05-27 16:52:17 +00:00
|
|
|
setup->frag_size = snd_pcm_plug_client_size(pcm, setup->stream, setup->frag_size);
|
2000-05-23 12:52:06 +00:00
|
|
|
setup->byte_boundary *= setup->frag_size;
|
2000-05-16 18:04:44 +00:00
|
|
|
setup->buffer_size = setup->frags * setup->frag_size;
|
2000-05-27 16:52:17 +00:00
|
|
|
setup->bytes_min = snd_pcm_plug_client_size(pcm, setup->stream, setup->bytes_min);
|
|
|
|
|
setup->bytes_align = snd_pcm_plug_client_size(pcm, setup->stream, setup->bytes_align);
|
|
|
|
|
setup->bytes_xrun_max = snd_pcm_plug_client_size(pcm, setup->stream, setup->bytes_xrun_max);
|
|
|
|
|
setup->bytes_fill_max = snd_pcm_plug_client_size(pcm, setup->stream, setup->bytes_fill_max);
|
|
|
|
|
|
|
|
|
|
plugstr = &plug->stream[setup->stream];
|
|
|
|
|
if (setup->stream == SND_PCM_STREAM_PLAYBACK)
|
|
|
|
|
setup->format = plugstr->first->src_format;
|
2000-05-08 18:53:38 +00:00
|
|
|
else
|
2000-05-27 16:52:17 +00:00
|
|
|
setup->format = plugstr->last->dst_format;
|
2000-05-08 18:53:38 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_status(snd_pcm_t *pcm, snd_pcm_stream_status_t *status)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
err = snd_pcm_stream_status(plug->slave, status);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, status->stream))
|
2000-05-08 18:53:38 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* FIXME: may overflow */
|
2000-05-27 16:52:17 +00:00
|
|
|
status->byte_io = snd_pcm_plug_client_size(pcm, status->stream, status->byte_io);
|
|
|
|
|
status->byte_data = snd_pcm_plug_client_size(pcm, status->stream, status->byte_data);
|
|
|
|
|
status->bytes_used = snd_pcm_plug_client_size(pcm, status->stream, status->bytes_used);
|
2000-05-08 18:53:38 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_update(snd_pcm_t *pcm, int stream)
|
2000-05-09 10:46:43 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
int err;
|
2000-05-27 16:52:17 +00:00
|
|
|
err = snd_pcm_stream_update(plug->slave, stream);
|
2000-05-09 10:46:43 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
2000-05-09 10:46:43 +00:00
|
|
|
return 0;
|
|
|
|
|
#if 0
|
|
|
|
|
/* To think more about that */
|
2000-05-27 16:52:17 +00:00
|
|
|
if ((err = snd_pcm_plug_action(pcm, stream, UPDATE, 0))<0)
|
2000-05-09 10:46:43 +00:00
|
|
|
return err;
|
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_prepare(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
int err;
|
2000-05-27 16:52:17 +00:00
|
|
|
err = snd_pcm_stream_prepare(plug->slave, stream);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
2000-05-08 18:53:38 +00:00
|
|
|
return 0;
|
2000-05-27 16:52:17 +00:00
|
|
|
if ((err = snd_pcm_plug_action(pcm, stream, PREPARE, 0))<0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return err;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_go(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
return snd_pcm_stream_go(plug->slave, stream);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_plug_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
return snd_pcm_sync_go(plug->slave, sync);
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_drain(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
int err;
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
if ((err = snd_pcm_stream_drain(plug->slave, stream)) < 0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return err;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
2000-05-08 18:53:38 +00:00
|
|
|
return 0;
|
2000-05-27 16:52:17 +00:00
|
|
|
if ((err = snd_pcm_plug_action(pcm, stream, DRAIN, 0))<0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return err;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_flush(snd_pcm_t *pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
int err;
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
if ((err = snd_pcm_stream_flush(plug->slave, stream)) < 0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return err;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
2000-05-08 18:53:38 +00:00
|
|
|
return 0;
|
2000-05-27 16:52:17 +00:00
|
|
|
if ((err = snd_pcm_plug_action(pcm, stream, FLUSH, 0))<0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return err;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_stream_pause(snd_pcm_t *pcm, int stream, int enable)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
int err;
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
if ((err = snd_pcm_stream_pause(plug->slave, stream, enable)) < 0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return err;
|
2000-05-27 16:52:17 +00:00
|
|
|
if ((err = snd_pcm_plug_action(pcm, stream, PAUSE, 0))<0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return err;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_channel_setup(snd_pcm_t *pcm, int stream, snd_pcm_channel_setup_t *setup)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_stream *str;
|
|
|
|
|
unsigned int channel;
|
2000-05-08 18:53:38 +00:00
|
|
|
int width;
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
|
|
|
|
return snd_pcm_channel_setup(plug->slave, stream, setup);
|
2000-05-08 18:53:38 +00:00
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
channel = setup->channel;
|
2000-05-08 18:53:38 +00:00
|
|
|
memset(setup, 0, sizeof(*setup));
|
2000-05-27 16:52:17 +00:00
|
|
|
setup->channel = channel;
|
|
|
|
|
str = &pcm->stream[stream];
|
|
|
|
|
if (!str->mmap_data) {
|
2000-05-16 15:20:34 +00:00
|
|
|
setup->area.addr = 0;
|
2000-05-08 18:53:38 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
2000-05-27 16:52:17 +00:00
|
|
|
if (channel >= str->setup.format.channels)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
if (str->setup.format.interleave) {
|
|
|
|
|
setup->area.addr = str->mmap_data;
|
|
|
|
|
setup->area.first = setup->channel * str->sample_width;
|
|
|
|
|
setup->area.step = str->bits_per_frame;
|
2000-05-08 18:53:38 +00:00
|
|
|
} else {
|
2000-05-27 16:52:17 +00:00
|
|
|
size_t size = str->mmap_data_size / str->setup.format.channels;
|
|
|
|
|
setup->area.addr = str->mmap_data + setup->channel * size;
|
2000-05-16 15:20:34 +00:00
|
|
|
setup->area.first = 0;
|
2000-05-27 16:52:17 +00:00
|
|
|
setup->area.step = str->sample_width;
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-29 19:53:30 +00:00
|
|
|
static ssize_t snd_pcm_plug_stream_seek(snd_pcm_t *pcm, int stream, off_t offset)
|
|
|
|
|
{
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
2000-05-30 11:40:22 +00:00
|
|
|
return snd_pcm_stream_seek(plug->slave, stream, offset);
|
2000-05-29 19:53:30 +00:00
|
|
|
if (offset < 0) {
|
|
|
|
|
offset = snd_pcm_plug_slave_size(pcm, stream, -offset);
|
|
|
|
|
if (offset < 0)
|
|
|
|
|
return offset;
|
|
|
|
|
offset = -offset;
|
|
|
|
|
} else {
|
|
|
|
|
offset = snd_pcm_plug_slave_size(pcm, stream, offset);
|
|
|
|
|
if (offset < 0)
|
|
|
|
|
return offset;
|
|
|
|
|
}
|
2000-05-30 11:40:22 +00:00
|
|
|
ret = snd_pcm_stream_seek(plug->slave, stream, offset);
|
2000-05-29 19:53:30 +00:00
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
return snd_pcm_plug_client_size(pcm, stream, ret);
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-08 18:53:38 +00:00
|
|
|
ssize_t snd_pcm_plug_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_stream *str = &pcm->stream[SND_PCM_STREAM_PLAYBACK];
|
|
|
|
|
unsigned int k, step, channels;
|
2000-05-08 18:53:38 +00:00
|
|
|
int size = 0;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, SND_PCM_STREAM_PLAYBACK))
|
2000-05-08 18:53:38 +00:00
|
|
|
return snd_pcm_writev(plug->slave, vector, count);
|
2000-05-27 16:52:17 +00:00
|
|
|
channels = str->setup.format.channels;
|
|
|
|
|
if (str->setup.format.interleave)
|
2000-05-08 18:53:38 +00:00
|
|
|
step = 1;
|
|
|
|
|
else {
|
2000-05-27 16:52:17 +00:00
|
|
|
step = channels;
|
|
|
|
|
if (count % channels != 0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
for (k = 0; k < count; k += step, vector += step) {
|
2000-05-27 16:52:17 +00:00
|
|
|
snd_pcm_plugin_channel_t *channels;
|
2000-05-08 18:53:38 +00:00
|
|
|
int expected, ret;
|
2000-05-27 16:52:17 +00:00
|
|
|
expected = snd_pcm_plug_client_channels_iovec(pcm, SND_PCM_STREAM_PLAYBACK, vector, count, &channels);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (expected < 0)
|
|
|
|
|
return expected;
|
2000-05-27 16:52:17 +00:00
|
|
|
ret = snd_pcm_plug_write_transfer(pcm, channels, expected);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
|
if (size > 0)
|
|
|
|
|
return size;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
size += ret;
|
|
|
|
|
if (ret != expected)
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ssize_t snd_pcm_plug_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
struct snd_pcm_stream *str = &pcm->stream[SND_PCM_STREAM_CAPTURE];
|
|
|
|
|
unsigned int k, step, channels;
|
2000-05-08 18:53:38 +00:00
|
|
|
int size = 0;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, SND_PCM_STREAM_CAPTURE))
|
2000-05-08 18:53:38 +00:00
|
|
|
return snd_pcm_readv(plug->slave, vector, count);
|
2000-05-27 16:52:17 +00:00
|
|
|
channels = str->setup.format.channels;
|
|
|
|
|
if (str->setup.format.interleave)
|
2000-05-08 18:53:38 +00:00
|
|
|
step = 1;
|
|
|
|
|
else {
|
2000-05-27 16:52:17 +00:00
|
|
|
step = channels;
|
|
|
|
|
if (count % channels != 0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
for (k = 0; k < count; k += step) {
|
2000-05-27 16:52:17 +00:00
|
|
|
snd_pcm_plugin_channel_t *channels;
|
2000-05-08 18:53:38 +00:00
|
|
|
int expected, ret;
|
2000-05-27 16:52:17 +00:00
|
|
|
expected = snd_pcm_plug_client_channels_iovec(pcm, SND_PCM_STREAM_CAPTURE, vector, count, &channels);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (expected < 0)
|
|
|
|
|
return expected;
|
2000-05-27 16:52:17 +00:00
|
|
|
ret = snd_pcm_plug_read_transfer(pcm, channels, expected);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
|
if (size > 0)
|
|
|
|
|
return size;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
size += ret;
|
|
|
|
|
if (ret != expected)
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ssize_t snd_pcm_plug_write(snd_pcm_t *pcm, const void *buf, size_t count)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
int expected;
|
2000-05-27 16:52:17 +00:00
|
|
|
snd_pcm_plugin_channel_t *channels;
|
2000-05-08 18:53:38 +00:00
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, SND_PCM_STREAM_PLAYBACK))
|
2000-05-08 18:53:38 +00:00
|
|
|
return snd_pcm_write(plug->slave, buf, count);
|
2000-05-27 16:52:17 +00:00
|
|
|
expected = snd_pcm_plug_client_channels_buf(pcm, SND_PCM_STREAM_PLAYBACK, (char *)buf, count, &channels);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (expected < 0)
|
|
|
|
|
return expected;
|
2000-05-27 16:52:17 +00:00
|
|
|
return snd_pcm_plug_write_transfer(pcm, channels, expected);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ssize_t snd_pcm_plug_read(snd_pcm_t *pcm, void *buf, size_t count)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
int expected;
|
2000-05-27 16:52:17 +00:00
|
|
|
snd_pcm_plugin_channel_t *channels;
|
2000-05-08 18:53:38 +00:00
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, SND_PCM_STREAM_CAPTURE))
|
2000-05-08 18:53:38 +00:00
|
|
|
return snd_pcm_read(plug->slave, buf, count);
|
2000-05-27 16:52:17 +00:00
|
|
|
expected = snd_pcm_plug_client_channels_buf(pcm, SND_PCM_STREAM_CAPTURE, buf, count, &channels);
|
2000-05-08 18:53:38 +00:00
|
|
|
if (expected < 0)
|
|
|
|
|
return expected;
|
2000-05-27 16:52:17 +00:00
|
|
|
return snd_pcm_plug_read_transfer(pcm, channels, expected);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_mmap_control(snd_pcm_t *pcm, int stream, snd_pcm_mmap_control_t **control, size_t csize UNUSED)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
|
|
|
|
return snd_pcm_mmap_control(plug->slave, stream, control);
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EBADFD;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_mmap_data(snd_pcm_t *pcm, int stream, void **buffer, size_t bsize UNUSED)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
|
|
|
|
return snd_pcm_mmap_data(plug->slave, stream, buffer);
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EBADFD;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_munmap_control(snd_pcm_t *pcm, int stream, snd_pcm_mmap_control_t *control UNUSED, size_t csize UNUSED)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
|
|
|
|
return snd_pcm_munmap_control(plug->slave, stream);
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EBADFD;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_munmap_data(snd_pcm_t *pcm, int stream, void *buffer UNUSED, size_t size UNUSED)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
|
|
|
|
return snd_pcm_munmap_data(plug->slave, stream);
|
2000-05-08 18:53:38 +00:00
|
|
|
return -EBADFD;
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
static int snd_pcm_plug_channels_mask(snd_pcm_t *pcm, int stream,
|
2000-05-08 18:53:38 +00:00
|
|
|
bitset_t *client_vmask)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
if (snd_pcm_plug_direct(pcm, stream))
|
|
|
|
|
return snd_pcm_channels_mask(plug->slave, stream, client_vmask);
|
|
|
|
|
if (stream == SND_PCM_STREAM_PLAYBACK)
|
|
|
|
|
return snd_pcm_plug_playback_channels_mask(pcm, client_vmask);
|
2000-05-08 18:53:38 +00:00
|
|
|
else
|
2000-05-27 16:52:17 +00:00
|
|
|
return snd_pcm_plug_capture_channels_mask(pcm, client_vmask);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
2000-05-27 16:52:17 +00:00
|
|
|
int snd_pcm_plug_file_descriptor(snd_pcm_t* pcm, int stream)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
|
2000-05-27 16:52:17 +00:00
|
|
|
return snd_pcm_file_descriptor(plug->slave, stream);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct snd_pcm_ops snd_pcm_plug_ops = {
|
2000-05-27 16:52:17 +00:00
|
|
|
stream_close: snd_pcm_plug_stream_close,
|
|
|
|
|
stream_nonblock: snd_pcm_plug_stream_nonblock,
|
2000-05-08 18:53:38 +00:00
|
|
|
info: snd_pcm_plug_info,
|
2000-05-27 16:52:17 +00:00
|
|
|
stream_info: snd_pcm_plug_stream_info,
|
|
|
|
|
stream_params: snd_pcm_plug_stream_params,
|
|
|
|
|
stream_setup: snd_pcm_plug_stream_setup,
|
2000-05-08 18:53:38 +00:00
|
|
|
channel_setup: snd_pcm_plug_channel_setup,
|
2000-05-27 16:52:17 +00:00
|
|
|
stream_status: snd_pcm_plug_stream_status,
|
|
|
|
|
stream_update: snd_pcm_plug_stream_update,
|
|
|
|
|
stream_prepare: snd_pcm_plug_stream_prepare,
|
|
|
|
|
stream_go: snd_pcm_plug_stream_go,
|
2000-05-08 18:53:38 +00:00
|
|
|
sync_go: snd_pcm_plug_sync_go,
|
2000-05-27 16:52:17 +00:00
|
|
|
stream_drain: snd_pcm_plug_stream_drain,
|
|
|
|
|
stream_flush: snd_pcm_plug_stream_flush,
|
|
|
|
|
stream_pause: snd_pcm_plug_stream_pause,
|
2000-05-29 19:53:30 +00:00
|
|
|
stream_seek: snd_pcm_plug_stream_seek,
|
2000-05-08 18:53:38 +00:00
|
|
|
write: snd_pcm_plug_write,
|
|
|
|
|
writev: snd_pcm_plug_writev,
|
|
|
|
|
read: snd_pcm_plug_read,
|
|
|
|
|
readv: snd_pcm_plug_readv,
|
|
|
|
|
mmap_control: snd_pcm_plug_mmap_control,
|
|
|
|
|
mmap_data: snd_pcm_plug_mmap_data,
|
|
|
|
|
munmap_control: snd_pcm_plug_munmap_control,
|
|
|
|
|
munmap_data: snd_pcm_plug_munmap_data,
|
|
|
|
|
file_descriptor: snd_pcm_plug_file_descriptor,
|
2000-05-27 16:52:17 +00:00
|
|
|
channels_mask: snd_pcm_plug_channels_mask,
|
2000-05-08 18:53:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int snd_pcm_plug_connect(snd_pcm_t **handle, snd_pcm_t *slave, int mode, int close_slave)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_t *pcm;
|
|
|
|
|
snd_pcm_plug_t *plug;
|
|
|
|
|
int err;
|
|
|
|
|
err = snd_pcm_abstract_open(handle, mode, SND_PCM_TYPE_PLUG, sizeof(snd_pcm_plug_t));
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
if (close_slave)
|
|
|
|
|
snd_pcm_close(slave);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
pcm = *handle;
|
|
|
|
|
pcm->ops = &snd_pcm_plug_ops;
|
|
|
|
|
plug = (snd_pcm_plug_t*) &pcm->private;
|
|
|
|
|
plug->slave = slave;
|
|
|
|
|
plug->close_slave = close_slave;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_plug_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_t *slave;
|
|
|
|
|
int err;
|
|
|
|
|
err = snd_pcm_open_subdevice(&slave, card, device, subdevice, mode);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return snd_pcm_plug_connect(handle, slave, mode, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_plug_open(snd_pcm_t **handle, int card, int device, int mode)
|
|
|
|
|
{
|
|
|
|
|
return snd_pcm_plug_open_subdevice(handle, card, device, -1, mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|