mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-11-01 22:58:49 -04:00
Merged pcm2 branch.
This commit is contained in:
parent
986c1500d2
commit
1cd6778173
40 changed files with 5053 additions and 3045 deletions
800
src/pcm/pcm.c
800
src/pcm/pcm.c
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* PCM Interface - main file
|
||||
* Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* 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
|
||||
|
|
@ -20,567 +20,521 @@
|
|||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/uio.h>
|
||||
#include "pcm_local.h"
|
||||
|
||||
#define SND_FILE_PCM_PLAYBACK "/dev/snd/pcmC%iD%ip"
|
||||
#define SND_FILE_PCM_CAPTURE "/dev/snd/pcmC%iD%ic"
|
||||
#define SND_PCM_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0)
|
||||
|
||||
int snd_pcm_open(snd_pcm_t **handle, int card, int device, int mode)
|
||||
int snd_pcm_abstract_open(snd_pcm_t **handle, int mode,
|
||||
snd_pcm_type_t type, size_t extra)
|
||||
{
|
||||
return snd_pcm_open_subdevice(handle, card, device, -1, mode);
|
||||
}
|
||||
|
||||
static int snd_pcm_open_channel(int card, int device, int channel, int subdevice, int fmode, snd_ctl_t *ctl, int *ver)
|
||||
{
|
||||
char filename[32];
|
||||
char *filefmt;
|
||||
int err, fd;
|
||||
int attempt = 0;
|
||||
snd_pcm_channel_info_t info;
|
||||
switch (channel) {
|
||||
case SND_PCM_CHANNEL_PLAYBACK:
|
||||
filefmt = SND_FILE_PCM_PLAYBACK;
|
||||
break;
|
||||
case SND_PCM_CHANNEL_CAPTURE:
|
||||
filefmt = SND_FILE_PCM_CAPTURE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((err = snd_ctl_pcm_channel_prefer_subdevice(ctl, device, channel, subdevice)) < 0)
|
||||
return err;
|
||||
sprintf(filename, filefmt, card, device);
|
||||
|
||||
__again:
|
||||
if (attempt++ > 3) {
|
||||
snd_ctl_close(ctl);
|
||||
return -EBUSY;
|
||||
}
|
||||
if ((fd = open(filename, fmode)) < 0) {
|
||||
err = -errno;
|
||||
return err;
|
||||
}
|
||||
if (ioctl(fd, SND_PCM_IOCTL_PVERSION, ver) < 0) {
|
||||
err = -errno;
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
if (SND_PROTOCOL_INCOMPATIBLE(*ver, SND_PCM_VERSION_MAX)) {
|
||||
close(fd);
|
||||
return -SND_ERROR_INCOMPATIBLE_VERSION;
|
||||
}
|
||||
if (subdevice >= 0) {
|
||||
memset(&info, 0, sizeof(info));
|
||||
if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, &info) < 0) {
|
||||
err = -errno;
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
if (info.subdevice != subdevice) {
|
||||
close(fd);
|
||||
goto __again;
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int snd_pcm_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode)
|
||||
{
|
||||
int fmode, ver, err;
|
||||
snd_pcm_t *pcm;
|
||||
snd_ctl_t *ctl;
|
||||
int pfd = -1, cfd = -1;
|
||||
|
||||
if (!handle)
|
||||
return -EFAULT;
|
||||
*handle = NULL;
|
||||
|
||||
if (card < 0 || card >= SND_CARDS)
|
||||
return -EINVAL;
|
||||
if ((err = snd_ctl_open(&ctl, card)) < 0)
|
||||
return err;
|
||||
fmode = O_RDWR;
|
||||
if (mode & SND_PCM_OPEN_NONBLOCK)
|
||||
fmode |= O_NONBLOCK;
|
||||
|
||||
pcm = (snd_pcm_t *) calloc(1, sizeof(snd_pcm_t) + extra);
|
||||
if (pcm == NULL)
|
||||
return -ENOMEM;
|
||||
if (mode & SND_PCM_OPEN_PLAYBACK) {
|
||||
pfd = snd_pcm_open_channel(card, device, SND_PCM_CHANNEL_PLAYBACK,
|
||||
subdevice, fmode, ctl, &ver);
|
||||
if (pfd < 0) {
|
||||
snd_ctl_close(ctl);
|
||||
return pfd;
|
||||
}
|
||||
struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
|
||||
chan->open = 1;
|
||||
chan->mode = (mode & SND_PCM_NONBLOCK_PLAYBACK) ? SND_PCM_NONBLOCK : 0;
|
||||
}
|
||||
if (mode & SND_PCM_OPEN_CAPTURE) {
|
||||
cfd = snd_pcm_open_channel(card, device, SND_PCM_CHANNEL_CAPTURE,
|
||||
subdevice, fmode, ctl, &ver);
|
||||
if (cfd < 0) {
|
||||
if (pfd >= 0)
|
||||
close(pfd);
|
||||
snd_ctl_close(ctl);
|
||||
return cfd;
|
||||
}
|
||||
struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
|
||||
chan->open = 1;
|
||||
chan->mode = (mode & SND_PCM_NONBLOCK_CAPTURE) ? SND_PCM_NONBLOCK : 0;
|
||||
}
|
||||
snd_ctl_close(ctl);
|
||||
if (pfd < 0 && cfd < 0)
|
||||
return -EINVAL;
|
||||
pcm = (snd_pcm_t *) calloc(1, sizeof(snd_pcm_t));
|
||||
if (pcm == NULL) {
|
||||
if (pfd >= 0)
|
||||
close(pfd);
|
||||
if (cfd >= 0)
|
||||
close(cfd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
pcm->card = card;
|
||||
pcm->device = device;
|
||||
pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd = pfd;
|
||||
pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd = cfd;
|
||||
pcm->mode = mode;
|
||||
pcm->ver = ver;
|
||||
pcm->type = type;
|
||||
pcm->mode = mode & SND_PCM_OPEN_DUPLEX;
|
||||
*handle = pcm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
snd_pcm_type_t snd_pcm_type(snd_pcm_t *handle)
|
||||
{
|
||||
return handle->type;
|
||||
}
|
||||
|
||||
int snd_pcm_channel_close(snd_pcm_t *pcm, int channel)
|
||||
{
|
||||
int ret = 0;
|
||||
int err;
|
||||
struct snd_pcm_chan *chan;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
chan = &pcm->chan[channel];
|
||||
if (!chan->open)
|
||||
return -EBADFD;
|
||||
if (chan->mmap_control) {
|
||||
if ((err = snd_pcm_munmap_control(pcm, channel)) < 0)
|
||||
ret = err;
|
||||
}
|
||||
if (chan->mmap_data) {
|
||||
if ((err = snd_pcm_munmap_data(pcm, channel)) < 0)
|
||||
ret = err;
|
||||
}
|
||||
if ((err = pcm->ops->channel_close(pcm, channel)) < 0)
|
||||
ret = err;
|
||||
chan->open = 0;
|
||||
chan->valid_setup = 0;
|
||||
if (chan->valid_voices_setup) {
|
||||
chan->valid_voices_setup = 0;
|
||||
free(chan->voices_setup);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int snd_pcm_close(snd_pcm_t *pcm)
|
||||
{
|
||||
int res = 0;
|
||||
int err, ret = 0;
|
||||
int channel;
|
||||
|
||||
if (!pcm)
|
||||
return -EINVAL;
|
||||
return -EFAULT;
|
||||
for (channel = 0; channel < 2; ++channel) {
|
||||
snd_pcm_plugin_munmap(pcm, channel);
|
||||
snd_pcm_plugin_clear(pcm, channel);
|
||||
snd_pcm_munmap(pcm, channel);
|
||||
if (pcm->chan[channel].fd >= 0)
|
||||
if (close(pcm->chan[channel].fd))
|
||||
res = -errno;
|
||||
if (pcm->chan[channel].open) {
|
||||
if ((err = snd_pcm_channel_close(pcm, channel)) < 0)
|
||||
ret = err;
|
||||
}
|
||||
}
|
||||
free(pcm);
|
||||
return res;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int snd_pcm_file_descriptor(snd_pcm_t *pcm, int channel)
|
||||
{
|
||||
if (!pcm)
|
||||
return -EINVAL;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
return pcm->chan[channel].fd;
|
||||
}
|
||||
|
||||
int snd_pcm_nonblock_mode(snd_pcm_t *pcm, int nonblock)
|
||||
{
|
||||
long flags;
|
||||
int fd, channel;
|
||||
|
||||
if (!pcm)
|
||||
return -EINVAL;
|
||||
for (channel = 0; channel < 2; ++channel) {
|
||||
fd = pcm->chan[channel].fd;
|
||||
if (fd < 0)
|
||||
continue;
|
||||
if ((flags = fcntl(fd, F_GETFL)) < 0)
|
||||
return -errno;
|
||||
if (nonblock)
|
||||
flags |= O_NONBLOCK;
|
||||
else
|
||||
flags &= ~O_NONBLOCK;
|
||||
if (fcntl(fd, F_SETFL, flags) < 0)
|
||||
return -errno;
|
||||
if (nonblock)
|
||||
pcm->mode |= SND_PCM_OPEN_NONBLOCK;
|
||||
else
|
||||
pcm->mode &= ~SND_PCM_OPEN_NONBLOCK;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
|
||||
{
|
||||
int fd, channel;
|
||||
if (!pcm || !info)
|
||||
return -EINVAL;
|
||||
for (channel = 0; channel < 2; ++channel) {
|
||||
fd = pcm->chan[channel].fd;
|
||||
if (fd >= 0)
|
||||
break;
|
||||
}
|
||||
if (ioctl(fd, SND_PCM_IOCTL_INFO, info) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
|
||||
{
|
||||
int fd;
|
||||
if (!pcm || !info)
|
||||
return -EINVAL;
|
||||
if (info->channel < 0 || info->channel > 1)
|
||||
return -EINVAL;
|
||||
fd = pcm->chan[info->channel].fd;
|
||||
if (fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
|
||||
int snd_pcm_channel_nonblock(snd_pcm_t *pcm, int channel, int nonblock)
|
||||
{
|
||||
int err;
|
||||
int fd;
|
||||
struct snd_pcm_chan *chan;
|
||||
|
||||
if (!pcm || !params)
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
if (!pcm->chan[channel].open)
|
||||
return -EBADFD;
|
||||
if ((err = pcm->ops->channel_nonblock(pcm, channel, nonblock)) < 0)
|
||||
return err;
|
||||
if (nonblock)
|
||||
pcm->chan[channel].mode |= SND_PCM_NONBLOCK;
|
||||
else
|
||||
pcm->chan[channel].mode &= ~SND_PCM_NONBLOCK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
|
||||
{
|
||||
if (!pcm || !info)
|
||||
return -EFAULT;
|
||||
return pcm->ops->info(pcm, info);
|
||||
}
|
||||
|
||||
int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
|
||||
{
|
||||
if (!pcm || !info)
|
||||
return -EFAULT;
|
||||
if (info->channel < 0 || info->channel > 1)
|
||||
return -EINVAL;
|
||||
if (!pcm->chan[info->channel].open)
|
||||
return -EBADFD;
|
||||
return pcm->ops->channel_info(pcm, info);
|
||||
}
|
||||
|
||||
int snd_pcm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
|
||||
{
|
||||
int err;
|
||||
snd_pcm_channel_setup_t setup;
|
||||
struct snd_pcm_chan *chan;
|
||||
if (!pcm || !params)
|
||||
return -EFAULT;
|
||||
if (params->channel < 0 || params->channel > 1)
|
||||
return -EINVAL;
|
||||
chan = &pcm->chan[params->channel];
|
||||
fd = chan->fd;
|
||||
if (fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0)
|
||||
return -errno;
|
||||
chan->setup_is_valid = 0;
|
||||
memset(&chan->setup, 0, sizeof(snd_pcm_channel_setup_t));
|
||||
chan->setup.channel = params->channel;
|
||||
if ((err = snd_pcm_channel_setup(pcm, &chan->setup))<0)
|
||||
if (!chan->open)
|
||||
return -EBADFD;
|
||||
if (chan->mmap_control)
|
||||
return -EBADFD;
|
||||
if ((err = pcm->ops->channel_params(pcm, params)) < 0)
|
||||
return err;
|
||||
return 0;
|
||||
chan->valid_setup = 0;
|
||||
setup.channel = params->channel;
|
||||
return snd_pcm_channel_setup(pcm, &setup);
|
||||
}
|
||||
|
||||
int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
|
||||
int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
|
||||
{
|
||||
int fd;
|
||||
int err;
|
||||
struct snd_pcm_chan *chan;
|
||||
|
||||
if (!pcm || !setup)
|
||||
return -EINVAL;
|
||||
return -EFAULT;
|
||||
if (setup->channel < 0 || setup->channel > 1)
|
||||
return -EINVAL;
|
||||
chan = &pcm->chan[setup->channel];
|
||||
fd = chan->fd;
|
||||
if (fd < 0)
|
||||
return -EINVAL;
|
||||
if (chan->setup_is_valid) {
|
||||
if (!chan->open)
|
||||
return -EBADFD;
|
||||
if (chan->valid_setup) {
|
||||
memcpy(setup, &chan->setup, sizeof(*setup));
|
||||
return 0;
|
||||
}
|
||||
if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0)
|
||||
return -errno;
|
||||
if ((err = pcm->ops->channel_setup(pcm, setup)) < 0)
|
||||
return err;
|
||||
memcpy(&chan->setup, setup, sizeof(*setup));
|
||||
chan->setup_is_valid = 1;
|
||||
chan->valid_setup = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t * setup)
|
||||
const snd_pcm_channel_setup_t* snd_pcm_channel_cached_setup(snd_pcm_t *pcm, int channel)
|
||||
{
|
||||
int fd;
|
||||
struct snd_pcm_chan *chan;
|
||||
if (!pcm)
|
||||
return 0;
|
||||
if (channel < 0 || channel > 1)
|
||||
return 0;
|
||||
chan = &pcm->chan[channel];
|
||||
if (!chan->open || !chan->valid_setup)
|
||||
return 0;
|
||||
return &chan->setup;
|
||||
}
|
||||
|
||||
int snd_pcm_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup)
|
||||
{
|
||||
struct snd_pcm_chan *chan;
|
||||
if (!pcm || !setup)
|
||||
return -EINVAL;
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
fd = pcm->chan[channel].fd;
|
||||
if (fd < 0)
|
||||
chan = &pcm->chan[channel];
|
||||
if (!chan->open || !chan->valid_setup)
|
||||
return -EBADFD;
|
||||
if (chan->valid_voices_setup) {
|
||||
if (setup->voice >= chan->setup.format.voices)
|
||||
return -EINVAL;
|
||||
memcpy(setup, &chan->voices_setup[setup->voice], sizeof(*setup));
|
||||
return 0;
|
||||
}
|
||||
return pcm->ops->voice_setup(pcm, channel, setup);
|
||||
}
|
||||
|
||||
const snd_pcm_voice_setup_t* snd_pcm_channel_cached_voice_setup(snd_pcm_t *pcm, int channel, unsigned int voice)
|
||||
{
|
||||
struct snd_pcm_chan *chan;
|
||||
if (!pcm)
|
||||
return 0;
|
||||
if (channel < 0 || channel > 1)
|
||||
return 0;
|
||||
chan = &pcm->chan[channel];
|
||||
if (!chan->open || !chan->valid_setup)
|
||||
return 0;
|
||||
if (voice >= chan->setup.format.voices)
|
||||
return 0;
|
||||
return &chan->voices_setup[voice];
|
||||
}
|
||||
|
||||
int snd_pcm_all_voices_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup)
|
||||
{
|
||||
struct snd_pcm_chan *chan;
|
||||
snd_pcm_voice_setup_t *vs, *v;
|
||||
unsigned int voice;
|
||||
int err;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
if (ioctl(fd, SND_PCM_IOCTL_VOICE_SETUP, setup) < 0)
|
||||
return -errno;
|
||||
chan = &pcm->chan[channel];
|
||||
if (!chan->open || !chan->valid_setup)
|
||||
return -EBADFD;
|
||||
vs = calloc(chan->setup.format.voices, sizeof(*setup));
|
||||
for (voice = 0, v = vs; voice < chan->setup.format.voices; ++voice, ++v) {
|
||||
v->voice = voice;
|
||||
err = snd_pcm_voice_setup(pcm, channel, v);
|
||||
if (err < 0) {
|
||||
free(vs);
|
||||
return err;
|
||||
}
|
||||
if (setup) {
|
||||
memcpy(setup, v, sizeof(*setup));
|
||||
setup++;
|
||||
}
|
||||
}
|
||||
chan->voices_setup = vs;
|
||||
chan->valid_voices_setup = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t * status)
|
||||
int snd_pcm_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status)
|
||||
{
|
||||
int fd;
|
||||
if (!pcm || !status)
|
||||
return -EFAULT;
|
||||
if (status->channel < 0 || status->channel > 1)
|
||||
return -EINVAL;
|
||||
fd = pcm->chan[status->channel].fd;
|
||||
if (fd < 0)
|
||||
if (!pcm->chan[status->channel].open)
|
||||
return -EBADFD;
|
||||
return pcm->ops->channel_status(pcm, status);
|
||||
}
|
||||
|
||||
int snd_pcm_channel_prepare(snd_pcm_t *pcm, int channel)
|
||||
{
|
||||
int err;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_STATUS, status) < 0)
|
||||
return -errno;
|
||||
if (!pcm->chan[channel].open)
|
||||
return -EBADFD;
|
||||
err = pcm->ops->channel_prepare(pcm, channel);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_PREPARED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_playback_prepare(snd_pcm_t *pcm)
|
||||
{
|
||||
if (!pcm)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_PREPARE) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
return snd_pcm_channel_prepare(pcm, SND_PCM_CHANNEL_PLAYBACK);
|
||||
}
|
||||
|
||||
int snd_pcm_capture_prepare(snd_pcm_t *pcm)
|
||||
{
|
||||
if (!pcm)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_PREPARE) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
return snd_pcm_channel_prepare(pcm, SND_PCM_CHANNEL_CAPTURE);
|
||||
}
|
||||
|
||||
int snd_pcm_channel_prepare(snd_pcm_t *pcm, int channel)
|
||||
static int mmap_playback_go(snd_pcm_t *pcm, int channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case SND_PCM_CHANNEL_PLAYBACK:
|
||||
return snd_pcm_playback_prepare(pcm);
|
||||
case SND_PCM_CHANNEL_CAPTURE:
|
||||
return snd_pcm_capture_prepare(pcm);
|
||||
default:
|
||||
struct snd_pcm_chan *chan = &pcm->chan[channel];
|
||||
if (chan->mmap_control->status != SND_PCM_STATUS_PREPARED)
|
||||
return -EBADFD;
|
||||
if (chan->mmap_control->frag_data == 0)
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
int snd_pcm_playback_go(snd_pcm_t *pcm)
|
||||
{
|
||||
if (!pcm)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_GO) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_capture_go(snd_pcm_t *pcm)
|
||||
{
|
||||
if (!pcm)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_GO) < 0)
|
||||
return -errno;
|
||||
chan->mmap_control->status = SND_PCM_STATUS_RUNNING;
|
||||
pthread_mutex_lock(&chan->mutex);
|
||||
pthread_cond_signal(&chan->status_cond);
|
||||
pthread_cond_wait(&chan->ready_cond, &chan->mutex);
|
||||
pthread_mutex_unlock(&chan->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_channel_go(snd_pcm_t *pcm, int channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case SND_PCM_CHANNEL_PLAYBACK:
|
||||
return snd_pcm_playback_go(pcm);
|
||||
case SND_PCM_CHANNEL_CAPTURE:
|
||||
return snd_pcm_capture_go(pcm);
|
||||
default:
|
||||
return -EIO;
|
||||
int err;
|
||||
struct snd_pcm_chan *chan;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
chan = &pcm->chan[channel];
|
||||
if (!chan->open)
|
||||
return -EBADFD;
|
||||
if (channel == SND_PCM_CHANNEL_PLAYBACK &&
|
||||
chan->mmap_data_emulation) {
|
||||
err = mmap_playback_go(pcm, channel);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
err = pcm->ops->channel_go(pcm, channel);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (channel == SND_PCM_CHANNEL_CAPTURE)
|
||||
snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_RUNNING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_playback_go(snd_pcm_t *pcm)
|
||||
{
|
||||
return snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_PLAYBACK);
|
||||
}
|
||||
|
||||
int snd_pcm_capture_go(snd_pcm_t *pcm)
|
||||
{
|
||||
return snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_CAPTURE);
|
||||
}
|
||||
|
||||
int snd_pcm_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync)
|
||||
{
|
||||
int err;
|
||||
if (!pcm || !sync)
|
||||
return -EFAULT;
|
||||
if (!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open &&
|
||||
!pcm->chan[SND_PCM_CHANNEL_CAPTURE].open)
|
||||
return -EBADFD;
|
||||
err = pcm->ops->sync_go(pcm, sync);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* NYI: mmap emulation */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_channel_drain(snd_pcm_t *pcm, int channel)
|
||||
{
|
||||
int err;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_SYNC_GO, sync) < 0)
|
||||
return -errno;
|
||||
if (!pcm->chan[channel].open)
|
||||
return -EBADFD;
|
||||
if (channel != SND_PCM_CHANNEL_PLAYBACK)
|
||||
return -EBADFD;
|
||||
err = pcm->ops->channel_drain(pcm, channel);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_READY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_playback_drain(snd_pcm_t *pcm)
|
||||
{
|
||||
return snd_pcm_channel_drain(pcm, SND_PCM_CHANNEL_PLAYBACK);
|
||||
}
|
||||
|
||||
int snd_pcm_channel_flush(snd_pcm_t *pcm, int channel)
|
||||
{
|
||||
int err;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_DRAIN) < 0)
|
||||
return -errno;
|
||||
if (!pcm->chan[channel].open)
|
||||
return -EBADFD;
|
||||
err = pcm->ops->channel_flush(pcm, channel);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_READY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_playback_flush(snd_pcm_t *pcm)
|
||||
{
|
||||
if (!pcm)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_FLUSH) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
return snd_pcm_channel_flush(pcm, SND_PCM_CHANNEL_PLAYBACK);
|
||||
}
|
||||
|
||||
int snd_pcm_capture_flush(snd_pcm_t *pcm)
|
||||
{
|
||||
if (!pcm)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_FLUSH) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
return snd_pcm_channel_flush(pcm, SND_PCM_CHANNEL_CAPTURE);
|
||||
}
|
||||
|
||||
int snd_pcm_channel_flush(snd_pcm_t *pcm, int channel)
|
||||
int snd_pcm_channel_pause(snd_pcm_t *pcm, int channel, int enable)
|
||||
{
|
||||
switch (channel) {
|
||||
case SND_PCM_CHANNEL_PLAYBACK:
|
||||
return snd_pcm_playback_flush(pcm);
|
||||
case SND_PCM_CHANNEL_CAPTURE:
|
||||
return snd_pcm_capture_flush(pcm);
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
int err;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
if (!pcm->chan[channel].open)
|
||||
return -EBADFD;
|
||||
if (channel != SND_PCM_CHANNEL_PLAYBACK)
|
||||
return -EBADFD;
|
||||
err = pcm->ops->channel_pause(pcm, channel, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_PAUSED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_playback_pause(snd_pcm_t *pcm, int enable)
|
||||
{
|
||||
if (!pcm)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
|
||||
return -EINVAL;
|
||||
if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_PAUSE, &enable) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
return snd_pcm_channel_pause(pcm, SND_PCM_CHANNEL_PLAYBACK, enable);
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_transfer_size(snd_pcm_t *pcm, int channel)
|
||||
{
|
||||
struct snd_pcm_chan *chan;
|
||||
if (!pcm || channel < 0 || channel > 1)
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
chan = &pcm->chan[channel];
|
||||
if (!chan->setup_is_valid)
|
||||
if (!chan->open)
|
||||
return -EBADFD;
|
||||
if (!chan->valid_setup)
|
||||
return -EBADFD;
|
||||
if (chan->setup.mode != SND_PCM_MODE_BLOCK)
|
||||
return -EBADFD;
|
||||
return chan->setup.buf.block.frag_size;
|
||||
return chan->setup.frag_size;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_write(snd_pcm_t *pcm, const void *buffer, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
|
||||
if (!pcm || (!buffer && size > 0) || size < 0)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
|
||||
return -EINVAL;
|
||||
result = write(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, buffer, size);
|
||||
if (result < 0)
|
||||
return -errno;
|
||||
return result;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open ||
|
||||
!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].valid_setup)
|
||||
return -EBADFD;
|
||||
if (size > 0 && !buffer)
|
||||
return -EFAULT;
|
||||
return pcm->ops->write(pcm, buffer, size);
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, int count)
|
||||
ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count)
|
||||
{
|
||||
ssize_t result;
|
||||
|
||||
if (!pcm || (!vector && count > 0) || count < 0)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
|
||||
return -EINVAL;
|
||||
#if 0
|
||||
result = writev(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, vector, count);
|
||||
#else
|
||||
{
|
||||
snd_v_args_t args;
|
||||
args.vector = vector;
|
||||
args.count = count;
|
||||
result = ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_IOCTL_WRITEV, &args);
|
||||
}
|
||||
#endif
|
||||
if (result < 0)
|
||||
return -errno;
|
||||
return result;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open ||
|
||||
!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].valid_setup)
|
||||
return -EBADFD;
|
||||
if (count > 0 && !vector)
|
||||
return -EFAULT;
|
||||
return pcm->ops->writev(pcm, vector, count);
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_read(snd_pcm_t *pcm, void *buffer, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
|
||||
if (!pcm || (!buffer && size > 0) || size < 0)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
|
||||
return -EINVAL;
|
||||
result = read(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, buffer, size);
|
||||
if (result < 0)
|
||||
return -errno;
|
||||
return result;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (!pcm->chan[SND_PCM_CHANNEL_CAPTURE].open ||
|
||||
!pcm->chan[SND_PCM_CHANNEL_CAPTURE].valid_setup)
|
||||
return -EBADFD;
|
||||
if (size > 0 && !buffer)
|
||||
return -EFAULT;
|
||||
return pcm->ops->read(pcm, buffer, size);
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, int count)
|
||||
ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count)
|
||||
{
|
||||
ssize_t result;
|
||||
|
||||
if (!pcm || (!vector && count > 0) || count < 0)
|
||||
return -EINVAL;
|
||||
if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
|
||||
return -EINVAL;
|
||||
#if 0
|
||||
result = readv(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, vector, count);
|
||||
#else
|
||||
{
|
||||
snd_v_args_t args;
|
||||
args.vector = vector;
|
||||
args.count = count;
|
||||
result = ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_IOCTL_READV, &args);
|
||||
}
|
||||
#endif
|
||||
if (result < 0)
|
||||
return -errno;
|
||||
return result;
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (!pcm->chan[SND_PCM_CHANNEL_CAPTURE].open ||
|
||||
!pcm->chan[SND_PCM_CHANNEL_CAPTURE].valid_setup)
|
||||
return -EBADFD;
|
||||
if (count > 0 && !vector)
|
||||
return -EFAULT;
|
||||
return pcm->ops->readv(pcm, vector, count);
|
||||
}
|
||||
|
||||
int snd_pcm_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, void **buffer)
|
||||
int snd_pcm_file_descriptor(snd_pcm_t* pcm, int channel)
|
||||
{
|
||||
snd_pcm_channel_info_t info;
|
||||
int err, fd, prot;
|
||||
void *caddr, *daddr;
|
||||
struct snd_pcm_chan *chan;
|
||||
|
||||
if (control)
|
||||
*control = NULL;
|
||||
if (buffer)
|
||||
*buffer = NULL;
|
||||
if (!pcm || channel < 0 || channel > 1 || !control || !buffer)
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
chan = &pcm->chan[channel];
|
||||
fd = chan->fd;
|
||||
if (fd < 0)
|
||||
return -EINVAL;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.channel = channel;
|
||||
if ((err = snd_pcm_channel_info(pcm, &info))<0)
|
||||
return err;
|
||||
caddr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, SND_PCM_MMAP_OFFSET_CONTROL);
|
||||
if (caddr == (caddr_t)-1 || caddr == NULL)
|
||||
return -errno;
|
||||
prot = channel == SND_PCM_CHANNEL_PLAYBACK ? PROT_WRITE : PROT_READ;
|
||||
daddr = mmap(NULL, info.mmap_size, prot, MAP_FILE|MAP_SHARED, fd, SND_PCM_MMAP_OFFSET_DATA);
|
||||
if (daddr == (caddr_t)-1 || daddr == NULL) {
|
||||
err = -errno;
|
||||
munmap(caddr, sizeof(snd_pcm_mmap_control_t));
|
||||
return err;
|
||||
}
|
||||
*control = chan->mmap_control = caddr;
|
||||
*buffer = chan->mmap_data = daddr;
|
||||
chan->mmap_size = info.mmap_size;
|
||||
return 0;
|
||||
if (!pcm->chan[channel].open)
|
||||
return -EBADFD;
|
||||
return pcm->ops->file_descriptor(pcm, channel);
|
||||
}
|
||||
|
||||
int snd_pcm_munmap(snd_pcm_t *pcm, int channel)
|
||||
int snd_pcm_voices_mask(snd_pcm_t *pcm, int channel, bitset_t *client_vmask)
|
||||
{
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
if (!pcm->chan[channel].open)
|
||||
return -EBADFD;
|
||||
return pcm->ops->voices_mask(pcm, channel, client_vmask);
|
||||
}
|
||||
|
||||
int snd_pcm_bytes_per_second(snd_pcm_t *pcm, int channel)
|
||||
{
|
||||
struct snd_pcm_chan *chan;
|
||||
if (!pcm || channel < 0 || channel > 1)
|
||||
if (!pcm)
|
||||
return -EFAULT;
|
||||
if (channel < 0 || channel > 1)
|
||||
return -EINVAL;
|
||||
chan = &pcm->chan[channel];
|
||||
if (chan->mmap_control) {
|
||||
munmap(chan->mmap_control, sizeof(snd_pcm_mmap_control_t));
|
||||
chan->mmap_control = NULL;
|
||||
}
|
||||
if (chan->mmap_data) {
|
||||
munmap(chan->mmap_data, chan->mmap_size);
|
||||
chan->mmap_data = NULL;
|
||||
chan->mmap_size = 0;
|
||||
}
|
||||
return 0;
|
||||
if (!chan->open || !chan->valid_setup)
|
||||
return -EBADFD;
|
||||
return snd_pcm_format_bytes_per_second(&chan->setup.format);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue