|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \file pcm/pcm_ioplug.c
|
|
Packit |
4a16fb |
* \ingroup Plugin_SDK
|
|
Packit |
4a16fb |
* \brief I/O Plugin SDK
|
|
Packit |
4a16fb |
* \author Takashi Iwai <tiwai@suse.de>
|
|
Packit |
4a16fb |
* \date 2005
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* PCM - External I/O Plugin SDK
|
|
Packit |
4a16fb |
* Copyright (c) 2005 by Takashi Iwai <tiwai@suse.de>
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* This library is free software; you can redistribute it and/or modify
|
|
Packit |
4a16fb |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit |
4a16fb |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
4a16fb |
* the License, or (at your option) any later version.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
4a16fb |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
4a16fb |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
4a16fb |
* GNU Lesser General Public License for more details.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
4a16fb |
* License along with this library; if not, write to the Free Software
|
|
Packit |
4a16fb |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
#include "pcm_local.h"
|
|
Packit |
4a16fb |
#include "pcm_ioplug.h"
|
|
Packit |
4a16fb |
#include "pcm_ext_parm.h"
|
|
Packit |
4a16fb |
#include "pcm_generic.h"
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
#ifndef PIC
|
|
Packit |
4a16fb |
/* entry for static linking */
|
|
Packit |
4a16fb |
const char *_snd_module_pcm_ioplug = "";
|
|
Packit |
4a16fb |
#endif
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
#ifndef DOC_HIDDEN
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* hw_params */
|
|
Packit |
4a16fb |
typedef struct snd_pcm_ioplug_priv {
|
|
Packit |
4a16fb |
snd_pcm_ioplug_t *data;
|
|
Packit |
4a16fb |
struct snd_ext_parm params[SND_PCM_IOPLUG_HW_PARAMS];
|
|
Packit |
4a16fb |
snd_pcm_uframes_t last_hw;
|
|
Packit |
4a16fb |
snd_pcm_uframes_t avail_max;
|
|
Packit |
4a16fb |
snd_htimestamp_t trigger_tstamp;
|
|
Packit |
4a16fb |
} ioplug_priv_t;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_drop(snd_pcm_t *pcm);
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_poll_descriptors_count(snd_pcm_t *pcm);
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* update the hw pointer */
|
|
Packit |
4a16fb |
/* called in lock */
|
|
Packit |
4a16fb |
static void snd_pcm_ioplug_hw_ptr_update(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
snd_pcm_sframes_t hw;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
hw = io->data->callback->pointer(io->data);
|
|
Packit |
4a16fb |
if (hw >= 0) {
|
|
Packit |
4a16fb |
snd_pcm_uframes_t delta;
|
|
Packit |
4a16fb |
snd_pcm_uframes_t avail;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if ((snd_pcm_uframes_t)hw >= io->last_hw)
|
|
Packit |
4a16fb |
delta = hw - io->last_hw;
|
|
Packit |
4a16fb |
else {
|
|
Packit |
4a16fb |
const snd_pcm_uframes_t wrap_point =
|
|
Packit |
4a16fb |
(io->data->flags & SND_PCM_IOPLUG_FLAG_BOUNDARY_WA) ?
|
|
Packit |
4a16fb |
pcm->boundary : pcm->buffer_size;
|
|
Packit |
4a16fb |
delta = wrap_point + hw - io->last_hw;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
snd_pcm_mmap_hw_forward(io->data->pcm, delta);
|
|
Packit |
4a16fb |
/* stop the stream if all samples are drained */
|
|
Packit |
4a16fb |
if (io->data->state == SND_PCM_STATE_DRAINING) {
|
|
Packit |
4a16fb |
avail = snd_pcm_mmap_avail(pcm);
|
|
Packit |
4a16fb |
if (avail >= pcm->buffer_size)
|
|
Packit |
4a16fb |
snd_pcm_ioplug_drop(pcm);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
io->last_hw = (snd_pcm_uframes_t)hw;
|
|
Packit |
4a16fb |
} else {
|
|
Packit |
4a16fb |
if (io->data->state == SND_PCM_STATE_DRAINING)
|
|
Packit |
4a16fb |
snd_pcm_ioplug_drop(pcm);
|
|
Packit |
4a16fb |
else
|
|
Packit |
4a16fb |
io->data->state = SNDRV_PCM_STATE_XRUN;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
memset(info, 0, sizeof(*info));
|
|
Packit |
4a16fb |
info->stream = pcm->stream;
|
|
Packit |
4a16fb |
info->card = -1;
|
|
Packit |
4a16fb |
if (pcm->name) {
|
|
Packit |
4a16fb |
snd_strlcpy((char *)info->id, pcm->name, sizeof(info->id));
|
|
Packit |
4a16fb |
snd_strlcpy((char *)info->name, pcm->name, sizeof(info->name));
|
|
Packit |
4a16fb |
snd_strlcpy((char *)info->subname, pcm->name, sizeof(info->subname));
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
info->subdevices_count = 1;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return snd_pcm_channel_info_shm(pcm, info, -1);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
memset(status, 0, sizeof(*status));
|
|
Packit |
4a16fb |
snd_pcm_ioplug_hw_ptr_update(pcm);
|
|
Packit |
4a16fb |
status->state = io->data->state;
|
|
Packit |
4a16fb |
status->trigger_tstamp = io->trigger_tstamp;
|
|
Packit |
4a16fb |
status->avail = snd_pcm_mmap_avail(pcm);
|
|
Packit |
4a16fb |
status->avail_max = io->avail_max;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_state_t snd_pcm_ioplug_state(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
return io->data->state;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_hwsync(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_pcm_ioplug_hw_ptr_update(pcm);
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->version >= 0x010001 &&
|
|
Packit |
4a16fb |
io->data->callback->delay)
|
|
Packit |
4a16fb |
return io->data->callback->delay(io->data, delayp);
|
|
Packit |
4a16fb |
else {
|
|
Packit |
4a16fb |
snd_pcm_ioplug_hw_ptr_update(pcm);
|
|
Packit |
4a16fb |
*delayp = snd_pcm_mmap_hw_avail(pcm);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_reset(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
io->data->appl_ptr = 0;
|
|
Packit |
4a16fb |
io->data->hw_ptr = 0;
|
|
Packit |
4a16fb |
io->last_hw = 0;
|
|
Packit |
4a16fb |
io->avail_max = 0;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_prepare(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
int err = 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_pcm_ioplug_reset(pcm);
|
|
Packit |
4a16fb |
if (io->data->callback->prepare) {
|
|
Packit |
4a16fb |
snd_pcm_unlock(pcm); /* to avoid deadlock */
|
|
Packit |
4a16fb |
err = io->data->callback->prepare(io->data);
|
|
Packit |
4a16fb |
snd_pcm_lock(pcm);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
io->data->state = SND_PCM_STATE_PREPARED;
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static const int hw_params_type[SND_PCM_IOPLUG_HW_PARAMS] = {
|
|
Packit |
4a16fb |
[SND_PCM_IOPLUG_HW_ACCESS] = SND_PCM_HW_PARAM_ACCESS,
|
|
Packit |
4a16fb |
[SND_PCM_IOPLUG_HW_FORMAT] = SND_PCM_HW_PARAM_FORMAT,
|
|
Packit |
4a16fb |
[SND_PCM_IOPLUG_HW_CHANNELS] = SND_PCM_HW_PARAM_CHANNELS,
|
|
Packit |
4a16fb |
[SND_PCM_IOPLUG_HW_RATE] = SND_PCM_HW_PARAM_RATE,
|
|
Packit |
4a16fb |
[SND_PCM_IOPLUG_HW_PERIOD_BYTES] = SND_PCM_HW_PARAM_PERIOD_BYTES,
|
|
Packit |
4a16fb |
[SND_PCM_IOPLUG_HW_BUFFER_BYTES] = SND_PCM_HW_PARAM_BUFFER_BYTES,
|
|
Packit |
4a16fb |
[SND_PCM_IOPLUG_HW_PERIODS] = SND_PCM_HW_PARAM_PERIODS,
|
|
Packit |
4a16fb |
};
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* x = a * b */
|
|
Packit |
4a16fb |
static int rule_mul(snd_pcm_hw_params_t *params, int x, int a, int b)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_interval_t t;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_interval_mul(hw_param_interval(params, a),
|
|
Packit |
4a16fb |
hw_param_interval(params, b), &t);
|
|
Packit |
4a16fb |
return snd_interval_refine(hw_param_interval(params, x), &t);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* x = a / b */
|
|
Packit |
4a16fb |
static int rule_div(snd_pcm_hw_params_t *params, int x, int a, int b)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_interval_t t;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_interval_div(hw_param_interval(params, a),
|
|
Packit |
4a16fb |
hw_param_interval(params, b), &t);
|
|
Packit |
4a16fb |
return snd_interval_refine(hw_param_interval(params, x), &t);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* x = a * b / k */
|
|
Packit |
4a16fb |
static int rule_muldivk(snd_pcm_hw_params_t *params, int x, int a, int b, int k)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_interval_t t;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_interval_muldivk(hw_param_interval(params, a),
|
|
Packit |
4a16fb |
hw_param_interval(params, b), k, &t);
|
|
Packit |
4a16fb |
return snd_interval_refine(hw_param_interval(params, x), &t);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* x = a * k / b */
|
|
Packit |
4a16fb |
static int rule_mulkdiv(snd_pcm_hw_params_t *params, int x, int a, int k, int b)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_interval_t t;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_interval_mulkdiv(hw_param_interval(params, a), k,
|
|
Packit |
4a16fb |
hw_param_interval(params, b), &t);
|
|
Packit |
4a16fb |
return snd_interval_refine(hw_param_interval(params, x), &t);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
#if 0
|
|
Packit |
4a16fb |
static void dump_parm(snd_pcm_hw_params_t *params)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_output_t *log;
|
|
Packit |
4a16fb |
snd_output_stdio_attach(&log, stderr, 0);
|
|
Packit |
4a16fb |
snd_pcm_hw_params_dump(params, log);
|
|
Packit |
4a16fb |
snd_output_close(log);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
#endif
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* refine *_TIME and *_SIZE, then update *_BYTES */
|
|
Packit |
4a16fb |
static int refine_time_and_size(snd_pcm_hw_params_t *params,
|
|
Packit |
4a16fb |
int time, int size, int bytes)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
int err, change1 = 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* size = time * rate / 1000000 */
|
|
Packit |
4a16fb |
err = rule_muldivk(params, size, time,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_RATE, 1000000);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
change1 |= err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* bytes = size * framebits / 8 */
|
|
Packit |
4a16fb |
err = rule_muldivk(params, bytes, size,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_FRAME_BITS, 8);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
change1 |= err;
|
|
Packit |
4a16fb |
return change1;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* refine *_TIME and *_SIZE from *_BYTES */
|
|
Packit |
4a16fb |
static int refine_back_time_and_size(snd_pcm_hw_params_t *params,
|
|
Packit |
4a16fb |
int time, int size, int bytes)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* size = bytes * 8 / framebits */
|
|
Packit |
4a16fb |
err = rule_mulkdiv(params, size, bytes, 8, SND_PCM_HW_PARAM_FRAME_BITS);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
/* time = size * 1000000 / rate */
|
|
Packit |
4a16fb |
err = rule_mulkdiv(params, time, size, 1000000, SND_PCM_HW_PARAM_RATE);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
int change = 0, change1, change2, err;
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
struct snd_ext_parm *p;
|
|
Packit |
4a16fb |
unsigned int i;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* access, format */
|
|
Packit |
4a16fb |
for (i = SND_PCM_IOPLUG_HW_ACCESS; i <= SND_PCM_IOPLUG_HW_FORMAT; i++) {
|
|
Packit |
4a16fb |
err = snd_ext_parm_mask_refine(hw_param_mask(params, hw_params_type[i]),
|
|
Packit |
4a16fb |
io->params, i);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
change |= err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
/* channels, rate */
|
|
Packit |
4a16fb |
for (; i <= SND_PCM_IOPLUG_HW_RATE; i++) {
|
|
Packit |
4a16fb |
err = snd_ext_parm_interval_refine(hw_param_interval(params, hw_params_type[i]),
|
|
Packit |
4a16fb |
io->params, i);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
change |= err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (params->rmask & ((1 << SND_PCM_HW_PARAM_ACCESS) |
|
|
Packit |
4a16fb |
(1 << SND_PCM_HW_PARAM_FORMAT) |
|
|
Packit |
4a16fb |
(1 << SND_PCM_HW_PARAM_SUBFORMAT) |
|
|
Packit |
4a16fb |
(1 << SND_PCM_HW_PARAM_CHANNELS) |
|
|
Packit |
4a16fb |
(1 << SND_PCM_HW_PARAM_RATE))) {
|
|
Packit |
4a16fb |
err = snd_pcm_hw_refine_soft(pcm, params);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
change |= err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
change1 = refine_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_PERIOD_SIZE,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_PERIOD_BYTES);
|
|
Packit |
4a16fb |
if (change1 < 0)
|
|
Packit |
4a16fb |
return change1;
|
|
Packit |
4a16fb |
err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_BYTES),
|
|
Packit |
4a16fb |
io->params, SND_PCM_IOPLUG_HW_PERIOD_BYTES);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
change1 |= err;
|
|
Packit |
4a16fb |
if (change1) {
|
|
Packit |
4a16fb |
change |= change1;
|
|
Packit |
4a16fb |
err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_PERIOD_SIZE,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_PERIOD_BYTES);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
change1 = refine_time_and_size(params, SND_PCM_HW_PARAM_BUFFER_TIME,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_BUFFER_SIZE,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_BUFFER_BYTES);
|
|
Packit |
4a16fb |
if (change1 < 0)
|
|
Packit |
4a16fb |
return change1;
|
|
Packit |
4a16fb |
change |= change1;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
do {
|
|
Packit |
4a16fb |
change2 = 0;
|
|
Packit |
4a16fb |
err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_BUFFER_BYTES),
|
|
Packit |
4a16fb |
io->params, SND_PCM_IOPLUG_HW_BUFFER_BYTES);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
change2 |= err;
|
|
Packit |
4a16fb |
/* periods = buffer_bytes / period_bytes */
|
|
Packit |
4a16fb |
err = rule_div(params, SND_PCM_HW_PARAM_PERIODS,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_BUFFER_BYTES,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_PERIOD_BYTES);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
change2 |= err;
|
|
Packit |
4a16fb |
err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIODS),
|
|
Packit |
4a16fb |
io->params, SND_PCM_IOPLUG_HW_PERIODS);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
change2 |= err;
|
|
Packit |
4a16fb |
/* buffer_bytes = periods * period_bytes */
|
|
Packit |
4a16fb |
err = rule_mul(params, SND_PCM_HW_PARAM_BUFFER_BYTES,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_PERIOD_BYTES,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_PERIODS);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
change2 |= err;
|
|
Packit |
4a16fb |
change1 |= change2;
|
|
Packit |
4a16fb |
} while (change2);
|
|
Packit |
4a16fb |
change |= change1;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (change1) {
|
|
Packit |
4a16fb |
err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_BUFFER_TIME,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_BUFFER_SIZE,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_BUFFER_BYTES);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* period_bytes = buffer_bytes / periods */
|
|
Packit |
4a16fb |
err = rule_div(params, SND_PCM_HW_PARAM_PERIOD_BYTES,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_BUFFER_BYTES,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_PERIODS);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
if (err) {
|
|
Packit |
4a16fb |
/* update period_size and period_time */
|
|
Packit |
4a16fb |
change |= err;
|
|
Packit |
4a16fb |
err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_BYTES),
|
|
Packit |
4a16fb |
io->params, SND_PCM_IOPLUG_HW_PERIOD_BYTES);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_PERIOD_SIZE,
|
|
Packit |
4a16fb |
SND_PCM_HW_PARAM_PERIOD_BYTES);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
params->info = SND_PCM_INFO_BLOCK_TRANSFER;
|
|
Packit |
4a16fb |
p = &io->params[SND_PCM_IOPLUG_HW_ACCESS];
|
|
Packit |
4a16fb |
if (p->active) {
|
|
Packit |
4a16fb |
for (i = 0; i < p->num_list; i++)
|
|
Packit |
4a16fb |
switch (p->list[i]) {
|
|
Packit |
4a16fb |
case SND_PCM_ACCESS_MMAP_INTERLEAVED:
|
|
Packit |
4a16fb |
case SND_PCM_ACCESS_RW_INTERLEAVED:
|
|
Packit |
4a16fb |
params->info |= SND_PCM_INFO_INTERLEAVED;
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
|
|
Packit |
4a16fb |
case SND_PCM_ACCESS_RW_NONINTERLEAVED:
|
|
Packit |
4a16fb |
params->info |= SND_PCM_INFO_NONINTERLEAVED;
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (io->data->callback->pause)
|
|
Packit |
4a16fb |
params->info |= SND_PCM_INFO_PAUSE;
|
|
Packit |
4a16fb |
if (io->data->callback->resume)
|
|
Packit |
4a16fb |
params->info |= SND_PCM_INFO_RESUME;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
#if 0
|
|
Packit |
4a16fb |
fprintf(stderr, "XXX\n");
|
|
Packit |
4a16fb |
dump_parm(params);
|
|
Packit |
4a16fb |
#endif
|
|
Packit |
4a16fb |
return change;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_access)(params, &io->data->access);
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_format)(params, &io->data->format);
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_channels)(params, &io->data->channels);
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_rate)(params, &io->data->rate, 0);
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_period_size)(params, &io->data->period_size, 0);
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &io->data->buffer_size);
|
|
Packit |
4a16fb |
if (io->data->callback->hw_params) {
|
|
Packit |
4a16fb |
err = io->data->callback->hw_params(io->data, params);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_access)(params, &io->data->access);
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_format)(params, &io->data->format);
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_channels)(params, &io->data->channels);
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_rate)(params, &io->data->rate, 0);
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_period_size)(params, &io->data->period_size, 0);
|
|
Packit |
4a16fb |
INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &io->data->buffer_size);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_hw_free(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->callback->hw_free)
|
|
Packit |
4a16fb |
return io->data->callback->hw_free(io->data);
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (!io->data->callback->sw_params)
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_pcm_unlock(pcm); /* to avoid deadlock */
|
|
Packit |
4a16fb |
err = io->data->callback->sw_params(io->data, params);
|
|
Packit |
4a16fb |
snd_pcm_lock(pcm);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_start(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->state != SND_PCM_STATE_PREPARED)
|
|
Packit |
4a16fb |
return -EBADFD;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = io->data->callback->start(io->data);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
gettimestamp(&io->trigger_tstamp, pcm->tstamp_type);
|
|
Packit |
4a16fb |
io->data->state = SND_PCM_STATE_RUNNING;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_drop(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->state == SND_PCM_STATE_OPEN)
|
|
Packit |
4a16fb |
return -EBADFD;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
io->data->callback->stop(io->data);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
gettimestamp(&io->trigger_tstamp, pcm->tstamp_type);
|
|
Packit |
4a16fb |
io->data->state = SND_PCM_STATE_SETUP;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int ioplug_drain_via_poll(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
while (io->data->state == SND_PCM_STATE_DRAINING) {
|
|
Packit |
4a16fb |
snd_pcm_ioplug_hw_ptr_update(pcm);
|
|
Packit |
4a16fb |
if (io->data->state != SND_PCM_STATE_DRAINING)
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
/* in non-blocking mode, let application to poll() by itself */
|
|
Packit |
4a16fb |
if (io->data->nonblock)
|
|
Packit |
4a16fb |
return -EAGAIN;
|
|
Packit |
4a16fb |
if (snd_pcm_wait_nocheck(pcm, -1) < 0)
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0; /* force to drop at error */
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* need own locking */
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_drain(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
int err = 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_pcm_lock(pcm);
|
|
Packit |
4a16fb |
switch (io->data->state) {
|
|
Packit |
4a16fb |
case SND_PCM_STATE_OPEN:
|
|
Packit |
4a16fb |
case SND_PCM_STATE_DISCONNECTED:
|
|
Packit |
4a16fb |
case SND_PCM_STATE_SUSPENDED:
|
|
Packit |
4a16fb |
snd_pcm_unlock(pcm);
|
|
Packit |
4a16fb |
return -EBADFD;
|
|
Packit |
4a16fb |
case SND_PCM_STATE_PREPARED:
|
|
Packit |
4a16fb |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit |
4a16fb |
if (!io->data->callback->drain) {
|
|
Packit |
4a16fb |
err = snd_pcm_ioplug_start(pcm);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
goto unlock;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
io->data->state = SND_PCM_STATE_DRAINING;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
case SND_PCM_STATE_RUNNING:
|
|
Packit |
4a16fb |
io->data->state = SND_PCM_STATE_DRAINING;
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
default:
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->state == SND_PCM_STATE_DRAINING) {
|
|
Packit |
4a16fb |
if (io->data->callback->drain) {
|
|
Packit |
4a16fb |
snd_pcm_unlock(pcm); /* let plugin own locking */
|
|
Packit |
4a16fb |
err = io->data->callback->drain(io->data);
|
|
Packit |
4a16fb |
snd_pcm_lock(pcm);
|
|
Packit |
4a16fb |
} else {
|
|
Packit |
4a16fb |
err = ioplug_drain_via_poll(pcm);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
unlock:
|
|
Packit |
4a16fb |
if (!err && io->data->state != SND_PCM_STATE_SETUP)
|
|
Packit |
4a16fb |
snd_pcm_ioplug_drop(pcm);
|
|
Packit |
4a16fb |
snd_pcm_unlock(pcm);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_pause(snd_pcm_t *pcm, int enable)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
static const snd_pcm_state_t states[2] = {
|
|
Packit |
4a16fb |
SND_PCM_STATE_RUNNING, SND_PCM_STATE_PAUSED
|
|
Packit |
4a16fb |
};
|
|
Packit |
4a16fb |
int prev, err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
prev = !enable;
|
|
Packit |
4a16fb |
enable = !prev;
|
|
Packit |
4a16fb |
if (io->data->state != states[prev])
|
|
Packit |
4a16fb |
return -EBADFD;
|
|
Packit |
4a16fb |
if (io->data->callback->pause) {
|
|
Packit |
4a16fb |
err = io->data->callback->pause(io->data, enable);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
io->data->state = states[enable];
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t snd_pcm_ioplug_rewindable(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return snd_pcm_mmap_hw_rewindable(pcm);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t snd_pcm_ioplug_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_pcm_mmap_appl_backward(pcm, frames);
|
|
Packit |
4a16fb |
return frames;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t snd_pcm_ioplug_forwardable(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return snd_pcm_mmap_avail(pcm);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t snd_pcm_ioplug_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_pcm_mmap_appl_forward(pcm, frames);
|
|
Packit |
4a16fb |
return frames;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* need own locking */
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_resume(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->callback->resume)
|
|
Packit |
4a16fb |
io->data->callback->resume(io->data);
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* called in lock */
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t ioplug_priv_transfer_areas(snd_pcm_t *pcm,
|
|
Packit |
4a16fb |
const snd_pcm_channel_area_t *areas,
|
|
Packit |
4a16fb |
snd_pcm_uframes_t offset,
|
|
Packit |
4a16fb |
snd_pcm_uframes_t size)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
snd_pcm_sframes_t result;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (! size)
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
if (io->data->callback->transfer)
|
|
Packit |
4a16fb |
result = io->data->callback->transfer(io->data, areas, offset, size);
|
|
Packit |
4a16fb |
else
|
|
Packit |
4a16fb |
result = size;
|
|
Packit |
4a16fb |
if (result > 0)
|
|
Packit |
4a16fb |
snd_pcm_mmap_appl_forward(pcm, result);
|
|
Packit |
4a16fb |
return result;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t snd_pcm_ioplug_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
if (pcm->mmap_rw)
|
|
Packit |
4a16fb |
return snd_pcm_mmap_writei(pcm, buffer, size);
|
|
Packit |
4a16fb |
else {
|
|
Packit |
4a16fb |
snd_pcm_channel_area_t areas[pcm->channels];
|
|
Packit |
4a16fb |
snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
|
|
Packit |
4a16fb |
return snd_pcm_write_areas(pcm, areas, 0, size,
|
|
Packit |
4a16fb |
ioplug_priv_transfer_areas);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t snd_pcm_ioplug_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
if (pcm->mmap_rw)
|
|
Packit |
4a16fb |
return snd_pcm_mmap_writen(pcm, bufs, size);
|
|
Packit |
4a16fb |
else {
|
|
Packit |
4a16fb |
snd_pcm_channel_area_t areas[pcm->channels];
|
|
Packit |
4a16fb |
snd_pcm_areas_from_bufs(pcm, areas, bufs);
|
|
Packit |
4a16fb |
return snd_pcm_write_areas(pcm, areas, 0, size,
|
|
Packit |
4a16fb |
ioplug_priv_transfer_areas);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t snd_pcm_ioplug_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
if (pcm->mmap_rw)
|
|
Packit |
4a16fb |
return snd_pcm_mmap_readi(pcm, buffer, size);
|
|
Packit |
4a16fb |
else {
|
|
Packit |
4a16fb |
snd_pcm_channel_area_t areas[pcm->channels];
|
|
Packit |
4a16fb |
snd_pcm_areas_from_buf(pcm, areas, buffer);
|
|
Packit |
4a16fb |
return snd_pcm_read_areas(pcm, areas, 0, size,
|
|
Packit |
4a16fb |
ioplug_priv_transfer_areas);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t snd_pcm_ioplug_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
if (pcm->mmap_rw)
|
|
Packit |
4a16fb |
return snd_pcm_mmap_readn(pcm, bufs, size);
|
|
Packit |
4a16fb |
else {
|
|
Packit |
4a16fb |
snd_pcm_channel_area_t areas[pcm->channels];
|
|
Packit |
4a16fb |
snd_pcm_areas_from_bufs(pcm, areas, bufs);
|
|
Packit |
4a16fb |
return snd_pcm_read_areas(pcm, areas, 0, size,
|
|
Packit |
4a16fb |
ioplug_priv_transfer_areas);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t snd_pcm_ioplug_mmap_commit(snd_pcm_t *pcm,
|
|
Packit |
4a16fb |
snd_pcm_uframes_t offset,
|
|
Packit |
4a16fb |
snd_pcm_uframes_t size)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
|
|
Packit |
4a16fb |
pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
|
|
Packit |
4a16fb |
pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
|
|
Packit |
4a16fb |
const snd_pcm_channel_area_t *areas;
|
|
Packit |
4a16fb |
snd_pcm_uframes_t ofs, frames = size;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
__snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames);
|
|
Packit |
4a16fb |
if (ofs != offset)
|
|
Packit |
4a16fb |
return -EIO;
|
|
Packit |
4a16fb |
return ioplug_priv_transfer_areas(pcm, areas, offset, frames);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_pcm_mmap_appl_forward(pcm, size);
|
|
Packit |
4a16fb |
return size;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_sframes_t snd_pcm_ioplug_avail_update(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
snd_pcm_uframes_t avail;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_pcm_ioplug_hw_ptr_update(pcm);
|
|
Packit |
4a16fb |
if (io->data->state == SND_PCM_STATE_XRUN)
|
|
Packit |
4a16fb |
return -EPIPE;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
avail = snd_pcm_mmap_avail(pcm);
|
|
Packit |
4a16fb |
if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
|
|
Packit |
4a16fb |
pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
|
|
Packit |
4a16fb |
pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
|
|
Packit |
4a16fb |
if (io->data->callback->transfer) {
|
|
Packit |
4a16fb |
const snd_pcm_channel_area_t *areas;
|
|
Packit |
4a16fb |
snd_pcm_uframes_t offset, size = UINT_MAX;
|
|
Packit |
4a16fb |
snd_pcm_sframes_t result;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
__snd_pcm_mmap_begin(pcm, &areas, &offset, &size);
|
|
Packit |
4a16fb |
result = io->data->callback->transfer(io->data, areas, offset, size);
|
|
Packit |
4a16fb |
if (result < 0)
|
|
Packit |
4a16fb |
return result;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* If the available data doesn't fit in the
|
|
Packit |
4a16fb |
contiguous area at the end of the mmap we
|
|
Packit |
4a16fb |
must transfer the remaining data to the
|
|
Packit |
4a16fb |
beginning of the mmap. */
|
|
Packit |
4a16fb |
if (size < avail) {
|
|
Packit |
4a16fb |
result = io->data->callback->transfer(io->data, areas,
|
|
Packit |
4a16fb |
0, avail - size);
|
|
Packit |
4a16fb |
if (result < 0)
|
|
Packit |
4a16fb |
return result;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (avail > io->avail_max)
|
|
Packit |
4a16fb |
io->avail_max = avail;
|
|
Packit |
4a16fb |
return (snd_pcm_sframes_t)avail;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_nonblock(snd_pcm_t *pcm, int nonblock)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
io->data->nonblock = nonblock;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_poll_descriptors_count(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
int err = 1;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->callback->poll_descriptors_count) {
|
|
Packit |
4a16fb |
snd_pcm_unlock(pcm); /* to avoid deadlock */
|
|
Packit |
4a16fb |
err = io->data->callback->poll_descriptors_count(io->data);
|
|
Packit |
4a16fb |
snd_pcm_lock(pcm);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->callback->poll_descriptors) {
|
|
Packit |
4a16fb |
snd_pcm_unlock(pcm); /* to avoid deadlock */
|
|
Packit |
4a16fb |
err = io->data->callback->poll_descriptors(io->data, pfds, space);
|
|
Packit |
4a16fb |
snd_pcm_lock(pcm);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (pcm->poll_fd < 0)
|
|
Packit |
4a16fb |
return -EIO;
|
|
Packit |
4a16fb |
if (space >= 1 && pfds) {
|
|
Packit |
4a16fb |
pfds->fd = pcm->poll_fd;
|
|
Packit |
4a16fb |
pfds->events = pcm->poll_events | POLLERR | POLLNVAL;
|
|
Packit |
4a16fb |
} else {
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return 1;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->callback->poll_revents) {
|
|
Packit |
4a16fb |
snd_pcm_unlock(pcm); /* to avoid deadlock */
|
|
Packit |
4a16fb |
err = io->data->callback->poll_revents(io->data, pfds, nfds, revents);
|
|
Packit |
4a16fb |
snd_pcm_lock(pcm);
|
|
Packit |
4a16fb |
} else {
|
|
Packit |
4a16fb |
*revents = pfds->revents;
|
|
Packit |
4a16fb |
err = 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_async(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
|
|
Packit |
4a16fb |
int sig ATTRIBUTE_UNUSED,
|
|
Packit |
4a16fb |
pid_t pid ATTRIBUTE_UNUSED)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return -ENOSYS;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_chmap_query_t **snd_pcm_ioplug_query_chmaps(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->version >= 0x010002 &&
|
|
Packit |
4a16fb |
io->data->callback->query_chmaps)
|
|
Packit |
4a16fb |
return io->data->callback->query_chmaps(io->data);
|
|
Packit |
4a16fb |
return NULL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static snd_pcm_chmap_t *snd_pcm_ioplug_get_chmap(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->version >= 0x010002 &&
|
|
Packit |
4a16fb |
io->data->callback->get_chmap)
|
|
Packit |
4a16fb |
return io->data->callback->get_chmap(io->data);
|
|
Packit |
4a16fb |
return NULL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->version >= 0x010002 &&
|
|
Packit |
4a16fb |
io->data->callback->set_chmap)
|
|
Packit |
4a16fb |
return io->data->callback->set_chmap(io->data, map);
|
|
Packit |
4a16fb |
return -ENXIO;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static void snd_pcm_ioplug_dump(snd_pcm_t *pcm, snd_output_t *out)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (io->data->callback->dump)
|
|
Packit |
4a16fb |
io->data->callback->dump(io->data, out);
|
|
Packit |
4a16fb |
else {
|
|
Packit |
4a16fb |
if (io->data->name)
|
|
Packit |
4a16fb |
snd_output_printf(out, "%s\n", io->data->name);
|
|
Packit |
4a16fb |
else
|
|
Packit |
4a16fb |
snd_output_printf(out, "IO-PCM Plugin\n");
|
|
Packit |
4a16fb |
if (pcm->setup) {
|
|
Packit |
4a16fb |
snd_output_printf(out, "Its setup is:\n");
|
|
Packit |
4a16fb |
snd_pcm_dump_setup(pcm, out);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static void clear_io_params(ioplug_priv_t *io)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
int i;
|
|
Packit |
4a16fb |
for (i = 0; i < SND_PCM_IOPLUG_HW_PARAMS; i++)
|
|
Packit |
4a16fb |
snd_ext_parm_clear(&io->params[i]);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int snd_pcm_ioplug_close(snd_pcm_t *pcm)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = pcm->private_data;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
clear_io_params(io);
|
|
Packit |
4a16fb |
if (io->data->callback->close)
|
|
Packit |
4a16fb |
io->data->callback->close(io->data);
|
|
Packit |
4a16fb |
free(io);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static const snd_pcm_ops_t snd_pcm_ioplug_ops = {
|
|
Packit |
4a16fb |
.close = snd_pcm_ioplug_close,
|
|
Packit |
4a16fb |
.nonblock = snd_pcm_ioplug_nonblock,
|
|
Packit |
4a16fb |
.async = snd_pcm_ioplug_async,
|
|
Packit |
4a16fb |
.info = snd_pcm_ioplug_info,
|
|
Packit |
4a16fb |
.hw_refine = snd_pcm_ioplug_hw_refine,
|
|
Packit |
4a16fb |
.hw_params = snd_pcm_ioplug_hw_params,
|
|
Packit |
4a16fb |
.hw_free = snd_pcm_ioplug_hw_free,
|
|
Packit |
4a16fb |
.sw_params = snd_pcm_ioplug_sw_params,
|
|
Packit |
4a16fb |
.channel_info = snd_pcm_ioplug_channel_info,
|
|
Packit |
4a16fb |
.dump = snd_pcm_ioplug_dump,
|
|
Packit |
4a16fb |
.mmap = snd_pcm_ioplug_mmap,
|
|
Packit |
4a16fb |
.munmap = snd_pcm_ioplug_munmap,
|
|
Packit |
4a16fb |
.query_chmaps = snd_pcm_ioplug_query_chmaps,
|
|
Packit |
4a16fb |
.get_chmap = snd_pcm_ioplug_get_chmap,
|
|
Packit |
4a16fb |
.set_chmap = snd_pcm_ioplug_set_chmap,
|
|
Packit |
4a16fb |
};
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static const snd_pcm_fast_ops_t snd_pcm_ioplug_fast_ops = {
|
|
Packit |
4a16fb |
.status = snd_pcm_ioplug_status,
|
|
Packit |
4a16fb |
.prepare = snd_pcm_ioplug_prepare,
|
|
Packit |
4a16fb |
.reset = snd_pcm_ioplug_reset,
|
|
Packit |
4a16fb |
.start = snd_pcm_ioplug_start,
|
|
Packit |
4a16fb |
.drop = snd_pcm_ioplug_drop,
|
|
Packit |
4a16fb |
.drain = snd_pcm_ioplug_drain,
|
|
Packit |
4a16fb |
.pause = snd_pcm_ioplug_pause,
|
|
Packit |
4a16fb |
.state = snd_pcm_ioplug_state,
|
|
Packit |
4a16fb |
.hwsync = snd_pcm_ioplug_hwsync,
|
|
Packit |
4a16fb |
.delay = snd_pcm_ioplug_delay,
|
|
Packit |
4a16fb |
.resume = snd_pcm_ioplug_resume,
|
|
Packit |
4a16fb |
.link = NULL,
|
|
Packit |
4a16fb |
.link_slaves = NULL,
|
|
Packit |
4a16fb |
.unlink = NULL,
|
|
Packit |
4a16fb |
.rewindable = snd_pcm_ioplug_rewindable,
|
|
Packit |
4a16fb |
.rewind = snd_pcm_ioplug_rewind,
|
|
Packit |
4a16fb |
.forwardable = snd_pcm_ioplug_forwardable,
|
|
Packit |
4a16fb |
.forward = snd_pcm_ioplug_forward,
|
|
Packit |
4a16fb |
.writei = snd_pcm_ioplug_writei,
|
|
Packit |
4a16fb |
.writen = snd_pcm_ioplug_writen,
|
|
Packit |
4a16fb |
.readi = snd_pcm_ioplug_readi,
|
|
Packit |
4a16fb |
.readn = snd_pcm_ioplug_readn,
|
|
Packit |
4a16fb |
.avail_update = snd_pcm_ioplug_avail_update,
|
|
Packit |
4a16fb |
.mmap_commit = snd_pcm_ioplug_mmap_commit,
|
|
Packit |
4a16fb |
.htimestamp = snd_pcm_generic_real_htimestamp,
|
|
Packit |
4a16fb |
.poll_descriptors_count = snd_pcm_ioplug_poll_descriptors_count,
|
|
Packit |
4a16fb |
.poll_descriptors = snd_pcm_ioplug_poll_descriptors,
|
|
Packit |
4a16fb |
.poll_revents = snd_pcm_ioplug_poll_revents,
|
|
Packit |
4a16fb |
};
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
#endif /* !DOC_HIDDEN */
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Exported functions
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*! \page pcm_external_plugins PCM External Plugin SDK
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
\section pcm_ioplug External Plugin: I/O Plugin
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
The I/O-type plugin is a PCM plugin to work as the input or output terminal point,
|
|
Packit |
4a16fb |
i.e. as a user-space PCM driver.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
The new plugin is created via #snd_pcm_ioplug_create() function.
|
|
Packit |
4a16fb |
The first argument is a pointer of the pluging information. Some of
|
|
Packit |
4a16fb |
this struct must be initialized in prior to call
|
|
Packit |
4a16fb |
#snd_pcm_ioplug_create(). Then the function fills other fields in
|
|
Packit |
4a16fb |
return. The rest arguments, name, stream and mode, are usually
|
|
Packit |
4a16fb |
identical with the values passed from the ALSA plugin constructor.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
The following fields are mandatory: version, name, callback.
|
|
Packit |
4a16fb |
Otherfields are optional and should be initialized with zero.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
The constant #SND_PCM_IOPLUG_VERSION must be passed to the version
|
|
Packit |
4a16fb |
field for the version check in alsa-lib. A non-NULL ASCII string
|
|
Packit |
4a16fb |
has to be passed to the name field. The callback field contains the
|
|
Packit |
4a16fb |
table of callback functions for this plugin (defined as
|
|
Packit |
4a16fb |
#snd_pcm_ioplug_callback_t).
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
flags field specifies the optional bit-flags. poll_fd and poll_events
|
|
Packit |
4a16fb |
specify the poll file descriptor and the corresponding poll events
|
|
Packit |
4a16fb |
(POLLIN, POLLOUT) for the plugin. If the plugin requires multiple
|
|
Packit |
4a16fb |
poll descriptors or poll descriptor(s) dynamically varying, set
|
|
Packit |
4a16fb |
poll_descriptors and poll_descriptors_count callbacks to the callback
|
|
Packit |
4a16fb |
table. Then the poll_fd and poll_events field are ignored.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
mmap_rw specifies whether the plugin behaves in the pseudo mmap mode.
|
|
Packit |
4a16fb |
When this value is set to 1, the plugin creates always a local buffer
|
|
Packit |
4a16fb |
and performs read/write calls using this buffer as if it's mmapped.
|
|
Packit |
4a16fb |
The address of local buffer can be obtained via
|
|
Packit |
4a16fb |
#snd_pcm_ioplug_mmap_areas() function.
|
|
Packit |
4a16fb |
When poll_fd, poll_events and mmap_rw fields are changed after
|
|
Packit |
4a16fb |
#snd_pcm_ioplug_create(), call #snd_pcm_ioplug_reinit_status() to
|
|
Packit |
4a16fb |
reflect the changes.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
The driver can set an arbitrary value (pointer) to private_data
|
|
Packit |
4a16fb |
field to refer its own data in the callbacks.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
The rest fields are filled by #snd_pcm_ioplug_create(). The pcm field
|
|
Packit |
4a16fb |
is the resultant PCM handle. The others are the current status of the
|
|
Packit |
4a16fb |
PCM.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
The callback functions in #snd_pcm_ioplug_callback_t define the real
|
|
Packit |
4a16fb |
behavior of the driver.
|
|
Packit |
4a16fb |
At least, start, stop and pointer callbacks must be given. Other
|
|
Packit |
4a16fb |
callbacks are optional. The start and stop callbacks are called when
|
|
Packit |
4a16fb |
the PCM stream is started and stopped, repsectively. The pointer
|
|
Packit |
4a16fb |
callback returns the current DMA position, which may be called at any
|
|
Packit |
4a16fb |
time.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
The transfer callback is called when any data transfer happens. It
|
|
Packit |
4a16fb |
receives the area array, offset and the size to transfer. The area
|
|
Packit |
4a16fb |
array contains the array of snd_pcm_channel_area_t with the elements
|
|
Packit |
4a16fb |
of number of channels.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
When the PCM is closed, close callback is called. If the driver
|
|
Packit |
4a16fb |
allocates any internal buffers, they should be released in this
|
|
Packit |
4a16fb |
callback. The hw_params and hw_free callbacks are called when
|
|
Packit |
4a16fb |
hw_params are set and reset, respectively. Note that they may be
|
|
Packit |
4a16fb |
called multiple times according to the application. Similarly,
|
|
Packit |
4a16fb |
sw_params callback is called when sw_params is set or changed.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
The prepare, drain, pause and resume callbacks are called when
|
|
Packit |
4a16fb |
#snd_pcm_prepare(), #snd_pcm_drain(), #snd_pcm_pause(), and
|
|
Packit |
4a16fb |
#snd_pcm_resume() are called. The poll_descriptors_count and
|
|
Packit |
4a16fb |
poll_descriptors callbacks are used to return the multiple or dynamic
|
|
Packit |
4a16fb |
poll descriptors as mentioned above. The poll_revents callback is
|
|
Packit |
4a16fb |
used to modify poll events. If the driver needs to mangle the native
|
|
Packit |
4a16fb |
poll events to proper poll events for PCM, you can do it in this
|
|
Packit |
4a16fb |
callback.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
Finally, the dump callback is used to print the status of the plugin.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
Note that some callbacks (start, stop, pointer, transfer and pause)
|
|
Packit |
4a16fb |
may be called inside the internal pthread mutex, and they shouldn't
|
|
Packit |
4a16fb |
call the PCM functions again unnecessarily from the callback itself;
|
|
Packit |
4a16fb |
otherwise it may lead to a deadlock.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
The hw_params constraints can be defined via either
|
|
Packit |
4a16fb |
#snd_pcm_ioplug_set_param_minmax() and #snd_pcm_ioplug_set_param_list()
|
|
Packit |
4a16fb |
functions after calling #snd_pcm_ioplug_create().
|
|
Packit |
4a16fb |
The former defines the minimal and maximal acceptable values for the
|
|
Packit |
4a16fb |
given hw_params parameter (SND_PCM_IOPLUG_HW_XXX).
|
|
Packit |
4a16fb |
This function can't be used for the format parameter. The latter
|
|
Packit |
4a16fb |
function specifies the available parameter values as the list.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
To clear the parameter constraints, call #snd_pcm_ioplug_params_reset() function.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \brief Create an ioplug instance
|
|
Packit |
4a16fb |
* \param ioplug the ioplug handle
|
|
Packit |
4a16fb |
* \param name name of PCM
|
|
Packit |
4a16fb |
* \param stream stream direction
|
|
Packit |
4a16fb |
* \param mode PCM open mode
|
|
Packit |
4a16fb |
* \return 0 if successful, or a negative error code
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Creates the ioplug instance.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* The callback is the mandatory field of ioplug handle. At least, start, stop and
|
|
Packit |
4a16fb |
* pointer callbacks must be set before calling this function.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int snd_pcm_ioplug_create(snd_pcm_ioplug_t *ioplug, const char *name,
|
|
Packit |
4a16fb |
snd_pcm_stream_t stream, int mode)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
snd_pcm_t *pcm;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
assert(ioplug && ioplug->callback);
|
|
Packit |
4a16fb |
assert(ioplug->callback->start &&
|
|
Packit |
4a16fb |
ioplug->callback->stop &&
|
|
Packit |
4a16fb |
ioplug->callback->pointer);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* We support 1.0.0 to current */
|
|
Packit |
4a16fb |
if (ioplug->version < 0x010000 ||
|
|
Packit |
4a16fb |
ioplug->version > SND_PCM_IOPLUG_VERSION) {
|
|
Packit |
4a16fb |
SNDERR("ioplug: Plugin version mismatch: 0x%x\n",
|
|
Packit |
4a16fb |
ioplug->version);
|
|
Packit |
4a16fb |
return -ENXIO;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
io = calloc(1, sizeof(*io));
|
|
Packit |
4a16fb |
if (! io)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
io->data = ioplug;
|
|
Packit |
4a16fb |
ioplug->state = SND_PCM_STATE_OPEN;
|
|
Packit |
4a16fb |
ioplug->stream = stream;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = snd_pcm_new(&pcm, SND_PCM_TYPE_IOPLUG, name, stream, mode);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
free(io);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
ioplug->pcm = pcm;
|
|
Packit |
4a16fb |
pcm->ops = &snd_pcm_ioplug_ops;
|
|
Packit |
4a16fb |
pcm->fast_ops = &snd_pcm_ioplug_fast_ops;
|
|
Packit |
4a16fb |
pcm->private_data = io;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_pcm_set_hw_ptr(pcm, &ioplug->hw_ptr, -1, 0);
|
|
Packit |
4a16fb |
snd_pcm_set_appl_ptr(pcm, &ioplug->appl_ptr, -1, 0);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_pcm_ioplug_reinit_status(ioplug);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \brief Delete the ioplug instance
|
|
Packit |
4a16fb |
* \param ioplug the ioplug handle
|
|
Packit |
4a16fb |
* \return 0 if successful, or a negative error code
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int snd_pcm_ioplug_delete(snd_pcm_ioplug_t *ioplug)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return snd_pcm_close(ioplug->pcm);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \brief Reset ioplug parameters
|
|
Packit |
4a16fb |
* \param ioplug the ioplug handle
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Resets the all parameters for the given ioplug handle.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
void snd_pcm_ioplug_params_reset(snd_pcm_ioplug_t *ioplug)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = ioplug->pcm->private_data;
|
|
Packit |
4a16fb |
clear_io_params(io);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \brief Set parameter as the list
|
|
Packit |
4a16fb |
* \param ioplug the ioplug handle
|
|
Packit |
4a16fb |
* \param type parameter type
|
|
Packit |
4a16fb |
* \param num_list number of available values
|
|
Packit |
4a16fb |
* \param list the list of available values
|
|
Packit |
4a16fb |
* \return 0 if successful, or a negative error code
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Sets the parameter as the list.
|
|
Packit |
4a16fb |
* The available values of the given parameter type is restricted to the ones of the given list.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int snd_pcm_ioplug_set_param_list(snd_pcm_ioplug_t *ioplug, int type, unsigned int num_list, const unsigned int *list)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = ioplug->pcm->private_data;
|
|
Packit |
4a16fb |
if (type < 0 || type >= SND_PCM_IOPLUG_HW_PARAMS) {
|
|
Packit |
4a16fb |
SNDERR("IOPLUG: invalid parameter type %d", type);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (type == SND_PCM_IOPLUG_HW_PERIODS)
|
|
Packit |
4a16fb |
io->params[type].integer = 1;
|
|
Packit |
4a16fb |
return snd_ext_parm_set_list(&io->params[type], num_list, list);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \brief Set parameter as the min/max values
|
|
Packit |
4a16fb |
* \param ioplug the ioplug handle
|
|
Packit |
4a16fb |
* \param type parameter type
|
|
Packit |
4a16fb |
* \param min the minimum value
|
|
Packit |
4a16fb |
* \param max the maximum value
|
|
Packit |
4a16fb |
* \return 0 if successful, or a negative error code
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Sets the parameter as the min/max values.
|
|
Packit |
4a16fb |
* The available values of the given parameter type is restricted between the given
|
|
Packit |
4a16fb |
* minimum and maximum values.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int snd_pcm_ioplug_set_param_minmax(snd_pcm_ioplug_t *ioplug, int type, unsigned int min, unsigned int max)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug_priv_t *io = ioplug->pcm->private_data;
|
|
Packit |
4a16fb |
if (type < 0 || type >= SND_PCM_IOPLUG_HW_PARAMS) {
|
|
Packit |
4a16fb |
SNDERR("IOPLUG: invalid parameter type %d", type);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (type == SND_PCM_IOPLUG_HW_ACCESS || type == SND_PCM_IOPLUG_HW_FORMAT) {
|
|
Packit |
4a16fb |
SNDERR("IOPLUG: invalid parameter type %d", type);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (type == SND_PCM_IOPLUG_HW_PERIODS)
|
|
Packit |
4a16fb |
io->params[type].integer = 1;
|
|
Packit |
4a16fb |
return snd_ext_parm_set_minmax(&io->params[type], min, max);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \brief Reinitialize the poll and mmap status
|
|
Packit |
4a16fb |
* \param ioplug the ioplug handle
|
|
Packit |
4a16fb |
* \return 0 if successful, or a negative error code
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Reinitializes the poll and the mmap status of the PCM.
|
|
Packit |
4a16fb |
* Call this function to propagate the status change in the ioplug instance to
|
|
Packit |
4a16fb |
* its PCM internals.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int snd_pcm_ioplug_reinit_status(snd_pcm_ioplug_t *ioplug)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug->pcm->poll_fd = ioplug->poll_fd;
|
|
Packit |
4a16fb |
ioplug->pcm->poll_events = ioplug->poll_events;
|
|
Packit |
4a16fb |
if (ioplug->flags & SND_PCM_IOPLUG_FLAG_MONOTONIC)
|
|
Packit |
4a16fb |
ioplug->pcm->tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC;
|
|
Packit |
4a16fb |
else
|
|
Packit |
4a16fb |
ioplug->pcm->tstamp_type = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
|
|
Packit |
4a16fb |
ioplug->pcm->mmap_rw = ioplug->mmap_rw;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \brief Get mmap area of ioplug
|
|
Packit |
4a16fb |
* \param ioplug the ioplug handle
|
|
Packit |
4a16fb |
* \return the mmap channel areas if available, or NULL
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Returns the mmap channel areas if available. When mmap_rw field is not set,
|
|
Packit |
4a16fb |
* this function always returns NULL.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
const snd_pcm_channel_area_t *snd_pcm_ioplug_mmap_areas(snd_pcm_ioplug_t *ioplug)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
if (ioplug->mmap_rw)
|
|
Packit |
4a16fb |
return snd_pcm_mmap_areas(ioplug->pcm);
|
|
Packit |
4a16fb |
return NULL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \brief Change the ioplug PCM status
|
|
Packit |
4a16fb |
* \param ioplug the ioplug handle
|
|
Packit |
4a16fb |
* \param state the PCM status
|
|
Packit |
4a16fb |
* \return zero if successful or a negative error code
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Changes the PCM status of the ioplug to the given value.
|
|
Packit |
4a16fb |
* This function can be used for external plugins to notify the status
|
|
Packit |
4a16fb |
* change, e.g. XRUN.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int snd_pcm_ioplug_set_state(snd_pcm_ioplug_t *ioplug, snd_pcm_state_t state)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
ioplug->state = state;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \brief Get the available frames. This function can be used to calculate the
|
|
Packit |
4a16fb |
* the available frames before calling #snd_pcm_avail_update()
|
|
Packit |
4a16fb |
* \param ioplug the ioplug handle
|
|
Packit |
4a16fb |
* \param hw_ptr hardware pointer in frames
|
|
Packit |
4a16fb |
* \param appl_ptr application pointer in frames
|
|
Packit |
4a16fb |
* \return available frames for the application
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
snd_pcm_uframes_t snd_pcm_ioplug_avail(const snd_pcm_ioplug_t * const ioplug,
|
|
Packit |
4a16fb |
const snd_pcm_uframes_t hw_ptr,
|
|
Packit |
4a16fb |
const snd_pcm_uframes_t appl_ptr)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return __snd_pcm_avail(ioplug->pcm, hw_ptr, appl_ptr);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/**
|
|
Packit |
4a16fb |
* \brief Get the available frames. This function can be used to calculate the
|
|
Packit |
4a16fb |
* the available frames before calling #snd_pcm_avail_update()
|
|
Packit |
4a16fb |
* \param ioplug the ioplug handle
|
|
Packit |
4a16fb |
* \param hw_ptr hardware pointer in frames
|
|
Packit |
4a16fb |
* \param appl_ptr application pointer in frames
|
|
Packit |
4a16fb |
* \return available frames for the hardware
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
snd_pcm_uframes_t snd_pcm_ioplug_hw_avail(const snd_pcm_ioplug_t * const ioplug,
|
|
Packit |
4a16fb |
const snd_pcm_uframes_t hw_ptr,
|
|
Packit |
4a16fb |
const snd_pcm_uframes_t appl_ptr)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
/* available data/space which can be transferred by the user
|
|
Packit |
4a16fb |
* application
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
const snd_pcm_uframes_t user_avail = snd_pcm_ioplug_avail(ioplug,
|
|
Packit |
4a16fb |
hw_ptr,
|
|
Packit |
4a16fb |
appl_ptr);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (user_avail > ioplug->pcm->buffer_size) {
|
|
Packit |
4a16fb |
/* there was an Xrun */
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
/* available data/space which can be transferred by the DMA */
|
|
Packit |
4a16fb |
return ioplug->pcm->buffer_size - user_avail;
|
|
Packit |
4a16fb |
}
|