alsa-lib/src/pcm/pcm_multi.c

718 lines
19 KiB
C
Raw Normal View History

2000-06-21 14:57:19 +00:00
/*
* PCM - Multi
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
*
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "pcm_local.h"
2000-12-21 20:44:10 +00:00
#include "interval.h"
2000-06-21 14:57:19 +00:00
typedef struct {
2000-10-02 06:58:38 +00:00
snd_pcm_t *pcm;
unsigned int channels_count;
2000-06-21 14:57:19 +00:00
int close_slave;
} snd_pcm_multi_slave_t;
typedef struct {
2000-10-02 06:58:38 +00:00
int slave_idx;
2000-06-21 14:57:19 +00:00
unsigned int slave_channel;
2000-10-02 06:58:38 +00:00
} snd_pcm_multi_channel_t;
2000-06-21 14:57:19 +00:00
typedef struct {
size_t slaves_count;
snd_pcm_multi_slave_t *slaves;
size_t channels_count;
2000-10-02 06:58:38 +00:00
snd_pcm_multi_channel_t *channels;
2000-06-21 14:57:19 +00:00
} snd_pcm_multi_t;
static int snd_pcm_multi_close(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-10 09:11:07 +00:00
size_t i;
2000-06-21 14:57:19 +00:00
int ret = 0;
for (i = 0; i < multi->slaves_count; ++i) {
int err;
snd_pcm_multi_slave_t *slave = &multi->slaves[i];
2000-10-10 09:11:07 +00:00
if (slave->close_slave)
2000-10-02 06:58:38 +00:00
err = snd_pcm_close(slave->pcm);
2000-10-10 09:11:07 +00:00
else
err = snd_pcm_unlink(slave->pcm);
if (err < 0)
ret = err;
2000-06-21 14:57:19 +00:00
}
free(multi->slaves);
2000-10-02 06:58:38 +00:00
free(multi->channels);
free(multi);
2000-06-21 14:57:19 +00:00
return ret;
}
2000-10-02 06:58:38 +00:00
static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
2000-06-21 14:57:19 +00:00
{
2000-10-02 06:58:38 +00:00
return 0;
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid)
{
snd_pcm_multi_t *multi = pcm->private;
snd_pcm_t *slave_0 = multi->slaves[0].pcm;
return snd_pcm_async(slave_0, sig, pid);
}
static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-06-21 14:57:19 +00:00
int err;
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave_0 = multi->slaves[0].pcm;
2000-09-24 09:57:26 +00:00
/* FIXME */
2000-10-02 06:58:38 +00:00
err = snd_pcm_info(slave_0, info);
2000-06-21 14:57:19 +00:00
if (err < 0)
return err;
return 0;
}
2000-12-21 20:44:10 +00:00
static int snd_pcm_multi_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private;
2000-11-20 20:10:46 +00:00
unsigned int k;
2000-12-21 20:44:10 +00:00
snd_pcm_hw_params_t sparams;
int changed = 0;
2000-12-21 20:44:10 +00:00
int err;
const mask_t *access_mask = snd_pcm_hw_params_value_mask(params, SND_PCM_HW_PARAM_ACCESS);
mask_t *saccess_mask = alloca(mask_sizeof());
if (mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP);
else {
mask_none(saccess_mask);
if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED) &&
multi->slaves_count == 1)
mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_COMPLEX)) {
mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
if (multi->slaves_count > 1)
mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
}
}
err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_CHANNELS,
multi->channels_count);
if (err < 0)
return err;
changed = 0;
do {
for (k = 0; k < multi->slaves_count; ++k) {
snd_pcm_t *slave = multi->slaves[k].pcm;
2000-12-21 20:44:10 +00:00
_snd_pcm_hw_params_any(&sparams);
_snd_pcm_hw_params_mask(&sparams, 0,
SND_PCM_HW_PARAM_ACCESS,
saccess_mask);
_snd_pcm_hw_params_set(&sparams, 0,
SND_PCM_HW_PARAM_CHANNELS,
multi->slaves[k].channels_count);
err = snd_pcm_hw_refine2(params, &sparams,
snd_pcm_hw_refine, slave,
SND_PCM_HW_PARBIT_FORMAT |
SND_PCM_HW_PARBIT_SUBFORMAT |
SND_PCM_HW_PARBIT_RATE |
SND_PCM_HW_PARBIT_FRAGMENT_SIZE |
SND_PCM_HW_PARBIT_FRAGMENT_LENGTH |
SND_PCM_HW_PARBIT_BUFFER_SIZE |
SND_PCM_HW_PARBIT_BUFFER_LENGTH |
SND_PCM_HW_PARBIT_FRAGMENTS);
if (err < 0)
return err;
2000-12-21 20:44:10 +00:00
if (params->hw_cmask)
changed++;
}
} while (changed && multi->slaves_count > 1);
return 0;
2000-06-21 14:57:19 +00:00
}
2000-11-20 20:10:46 +00:00
static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private;
2000-12-21 20:44:10 +00:00
unsigned int k;
2000-11-20 20:10:46 +00:00
int err;
2000-12-21 20:44:10 +00:00
const mask_t *access_mask = snd_pcm_hw_params_value_mask(params, SND_PCM_HW_PARAM_ACCESS);
mask_t *saccess_mask = alloca(mask_sizeof());
if (mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP);
else {
mask_none(saccess_mask);
if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED) &&
multi->slaves_count == 1)
mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_COMPLEX)) {
mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
if (multi->slaves_count > 1)
mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
}
}
for (k = 0; k < multi->slaves_count; ++k) {
snd_pcm_t *slave = multi->slaves[k].pcm;
snd_pcm_hw_params_t sparams;
2000-12-21 20:44:10 +00:00
_snd_pcm_hw_params_any(&sparams);
_snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS,
saccess_mask);
_snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_CHANNELS,
multi->slaves[k].channels_count);
err = snd_pcm_hw_params2(params, &sparams,
snd_pcm_hw_params, slave,
SND_PCM_HW_PARBIT_FORMAT |
SND_PCM_HW_PARBIT_SUBFORMAT |
SND_PCM_HW_PARBIT_RATE |
SND_PCM_HW_PARBIT_FRAGMENT_SIZE |
SND_PCM_HW_PARBIT_FRAGMENT_LENGTH |
SND_PCM_HW_PARBIT_BUFFER_SIZE |
SND_PCM_HW_PARBIT_BUFFER_LENGTH |
SND_PCM_HW_PARBIT_FRAGMENTS);
if (err < 0)
2000-11-20 20:10:46 +00:00
return err;
err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);
if (err < 0)
return err;
2000-11-20 20:10:46 +00:00
if (slave->stopped_areas) {
err = snd_pcm_areas_silence(slave->stopped_areas, 0, slave->channels, slave->buffer_size, slave->format);
if (err < 0)
return err;
}
}
return 0;
}
2000-11-20 20:10:46 +00:00
static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int i;
2000-11-20 20:10:46 +00:00
int err;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
2000-11-20 20:10:46 +00:00
err = snd_pcm_sw_params(slave, params);
if (err < 0)
return err;
}
return 0;
}
2000-11-20 20:10:46 +00:00
static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_status(slave, status);
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_state(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_state(slave);
2000-06-21 14:57:19 +00:00
}
2000-09-24 09:57:26 +00:00
static int snd_pcm_multi_delay(snd_pcm_t *pcm, ssize_t *delayp)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_delay(slave, delayp);
2000-09-24 09:57:26 +00:00
}
static ssize_t snd_pcm_multi_avail_update(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_avail_update(slave);
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-02 06:58:38 +00:00
return snd_pcm_prepare(multi->slaves[0].pcm);
2000-06-21 14:57:19 +00:00
}
2000-11-24 17:08:03 +00:00
static int snd_pcm_multi_reset(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private;
return snd_pcm_reset(multi->slaves[0].pcm);
}
2000-09-24 09:57:26 +00:00
static int snd_pcm_multi_start(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-02 06:58:38 +00:00
return snd_pcm_start(multi->slaves[0].pcm);
2000-06-21 14:57:19 +00:00
}
2000-10-02 06:58:38 +00:00
static int snd_pcm_multi_drop(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-02 06:58:38 +00:00
return snd_pcm_drop(multi->slaves[0].pcm);
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_drain(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-02 06:58:38 +00:00
return snd_pcm_drain(multi->slaves[0].pcm);
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-02 06:58:38 +00:00
return snd_pcm_pause(multi->slaves[0].pcm, enable);
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int channel = info->channel;
2000-10-02 06:58:38 +00:00
snd_pcm_multi_channel_t *c = &multi->channels[channel];
int err;
if (c->slave_idx < 0)
return -ENXIO;
info->channel = c->slave_channel;
err = snd_pcm_channel_info(multi->slaves[c->slave_idx].pcm, info);
info->channel = channel;
2000-10-02 06:58:38 +00:00
return err;
}
static ssize_t snd_pcm_multi_rewind(snd_pcm_t *pcm, size_t frames)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-06-21 14:57:19 +00:00
unsigned int i;
size_t pos[multi->slaves_count];
memset(pos, 0, sizeof(pos));
for (i = 0; i < multi->slaves_count; ++i) {
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave_i = multi->slaves[i].pcm;
ssize_t f = snd_pcm_rewind(slave_i, frames);
if (f < 0)
return f;
pos[i] = f;
frames = f;
}
/* Realign the pointers */
for (i = 0; i < multi->slaves_count; ++i) {
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave_i = multi->slaves[i].pcm;
size_t f = pos[i] - frames;
if (f > 0)
2000-10-02 06:58:38 +00:00
snd_pcm_mmap_appl_forward(slave_i, f);
2000-06-21 14:57:19 +00:00
}
return frames;
2000-06-21 14:57:19 +00:00
}
2000-09-24 09:57:26 +00:00
static ssize_t snd_pcm_multi_mmap_forward(snd_pcm_t *pcm, size_t size)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-06-21 14:57:19 +00:00
unsigned int i;
2000-09-24 09:57:26 +00:00
2000-06-21 14:57:19 +00:00
for (i = 0; i < multi->slaves_count; ++i) {
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave = multi->slaves[i].pcm;
ssize_t frames = snd_pcm_mmap_forward(slave, size);
2000-09-24 09:57:26 +00:00
if (frames < 0)
return frames;
if (i == 0) {
size = frames;
continue;
}
if ((size_t) frames != size)
return -EBADFD;
2000-06-21 14:57:19 +00:00
}
2000-09-24 09:57:26 +00:00
return size;
2000-06-21 14:57:19 +00:00
}
2000-09-24 09:57:26 +00:00
static int snd_pcm_multi_set_avail_min(snd_pcm_t *pcm, size_t frames)
2000-10-16 11:34:11 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
return snd_pcm_set_avail_min(multi->slaves[0].pcm, frames);
}
2000-11-20 20:10:46 +00:00
static int snd_pcm_multi_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_multi_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
2000-09-24 09:57:26 +00:00
int snd_pcm_multi_poll_descriptor(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private;
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_poll_descriptor(slave);
2000-06-21 14:57:19 +00:00
}
static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int k;
fprintf(fp, "Multi PCM\n");
2000-10-10 15:39:09 +00:00
fprintf(fp, "\nChannel bindings:\n");
for (k = 0; k < multi->channels_count; ++k) {
snd_pcm_multi_channel_t *c = &multi->channels[k];
if (c->slave_idx < 0)
continue;
fprintf(fp, "%d: slave %d, channel %d\n",
k, c->slave_idx, c->slave_channel);
}
2000-11-20 20:10:46 +00:00
if (pcm->setup) {
fprintf(fp, "\nIts setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
for (k = 0; k < multi->slaves_count; ++k) {
fprintf(fp, "\nSlave #%d: ", k);
2000-10-02 06:58:38 +00:00
snd_pcm_dump(multi->slaves[k].pcm, fp);
}
}
snd_pcm_ops_t snd_pcm_multi_ops = {
2000-06-21 14:57:19 +00:00
close: snd_pcm_multi_close,
info: snd_pcm_multi_info,
2000-12-21 20:44:10 +00:00
hw_refine: snd_pcm_multi_hw_refine,
2000-11-20 20:10:46 +00:00
hw_params: snd_pcm_multi_hw_params,
sw_params: snd_pcm_multi_sw_params,
2000-09-24 09:57:26 +00:00
channel_info: snd_pcm_multi_channel_info,
dump: snd_pcm_multi_dump,
2000-09-24 09:57:26 +00:00
nonblock: snd_pcm_multi_nonblock,
async: snd_pcm_multi_async,
mmap: snd_pcm_multi_mmap,
munmap: snd_pcm_multi_munmap,
};
snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = {
2000-06-21 14:57:19 +00:00
status: snd_pcm_multi_status,
state: snd_pcm_multi_state,
2000-09-24 09:57:26 +00:00
delay: snd_pcm_multi_delay,
2000-06-21 14:57:19 +00:00
prepare: snd_pcm_multi_prepare,
2000-11-24 17:08:03 +00:00
reset: snd_pcm_multi_reset,
2000-09-24 09:57:26 +00:00
start: snd_pcm_multi_start,
2000-10-02 06:58:38 +00:00
drop: snd_pcm_multi_drop,
drain: snd_pcm_multi_drain,
2000-06-21 14:57:19 +00:00
pause: snd_pcm_multi_pause,
2000-09-24 09:57:26 +00:00
writei: snd_pcm_mmap_writei,
writen: snd_pcm_mmap_writen,
readi: snd_pcm_mmap_readi,
readn: snd_pcm_mmap_readn,
rewind: snd_pcm_multi_rewind,
2000-09-24 09:57:26 +00:00
avail_update: snd_pcm_multi_avail_update,
mmap_forward: snd_pcm_multi_mmap_forward,
2000-10-16 11:34:11 +00:00
set_avail_min: snd_pcm_multi_set_avail_min,
2000-06-21 14:57:19 +00:00
};
int snd_pcm_multi_open(snd_pcm_t **pcmp, char *name,
2000-10-10 09:11:07 +00:00
size_t slaves_count,
snd_pcm_t **slaves_pcm, size_t *schannels_count,
2000-10-10 09:11:07 +00:00
size_t channels_count,
int *sidxs, unsigned int *schannels,
int close_slaves)
2000-06-21 14:57:19 +00:00
{
snd_pcm_t *pcm;
2000-06-21 14:57:19 +00:00
snd_pcm_multi_t *multi;
unsigned int i;
int stream;
char slave_map[32][32] = { { 0 } };
assert(pcmp);
assert(slaves_count > 0 && slaves_pcm && schannels_count);
2000-10-02 06:58:38 +00:00
assert(channels_count > 0 && sidxs && schannels);
2000-06-21 14:57:19 +00:00
multi = calloc(1, sizeof(snd_pcm_multi_t));
if (!multi) {
return -ENOMEM;
}
stream = slaves_pcm[0]->stream;
2000-06-21 14:57:19 +00:00
multi->slaves_count = slaves_count;
multi->slaves = calloc(slaves_count, sizeof(*multi->slaves));
2000-10-02 06:58:38 +00:00
multi->channels_count = channels_count;
multi->channels = calloc(channels_count, sizeof(*multi->channels));
2000-06-21 14:57:19 +00:00
for (i = 0; i < slaves_count; ++i) {
snd_pcm_multi_slave_t *slave = &multi->slaves[i];
assert(slaves_pcm[i]->stream == stream);
slave->pcm = slaves_pcm[i];
2000-10-02 06:58:38 +00:00
slave->channels_count = schannels_count[i];
2000-06-21 14:57:19 +00:00
slave->close_slave = close_slaves;
if (i != 0)
snd_pcm_link(slaves_pcm[i-1], slaves_pcm[i]);
2000-06-21 14:57:19 +00:00
}
2000-10-02 06:58:38 +00:00
for (i = 0; i < channels_count; ++i) {
snd_pcm_multi_channel_t *bind = &multi->channels[i];
assert(sidxs[i] < (int)slaves_count);
assert(schannels[i] < schannels_count[sidxs[i]]);
bind->slave_idx = sidxs[i];
bind->slave_channel = schannels[i];
if (sidxs[i] < 0)
continue;
assert(!slave_map[sidxs[i]][schannels[i]]);
slave_map[sidxs[i]][schannels[i]] = 1;
2000-06-21 14:57:19 +00:00
}
2000-10-02 06:58:38 +00:00
multi->channels_count = channels_count;
2000-06-21 14:57:19 +00:00
pcm = calloc(1, sizeof(snd_pcm_t));
if (!pcm) {
2000-09-24 09:57:26 +00:00
free(multi);
return -ENOMEM;
}
2000-10-10 09:11:07 +00:00
if (name)
pcm->name = strdup(name);
pcm->type = SND_PCM_TYPE_MULTI;
pcm->stream = stream;
pcm->mode = multi->slaves[0].pcm->mode;
2000-11-20 20:10:46 +00:00
pcm->mmap_rw = 1;
pcm->ops = &snd_pcm_multi_ops;
pcm->op_arg = pcm;
pcm->fast_ops = &snd_pcm_multi_fast_ops;
pcm->fast_op_arg = pcm;
pcm->private = multi;
pcm->poll_fd = multi->slaves[0].pcm->poll_fd;
pcm->hw_ptr = multi->slaves[0].pcm->hw_ptr;
pcm->appl_ptr = multi->slaves[0].pcm->appl_ptr;
*pcmp = pcm;
2000-06-21 14:57:19 +00:00
return 0;
}
2000-09-24 09:57:26 +00:00
int _snd_pcm_multi_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
int stream, int mode)
{
snd_config_iterator_t i, j;
snd_config_t *slave = NULL;
snd_config_t *binding = NULL;
int err;
unsigned int idx;
char **slaves_id = NULL;
char **slaves_name = NULL;
snd_pcm_t **slaves_pcm = NULL;
size_t *slaves_channels = NULL;
2000-10-02 06:58:38 +00:00
unsigned int *channels_sidx = NULL;
unsigned int *channels_schannel = NULL;
2000-09-24 09:57:26 +00:00
size_t slaves_count = 0;
2000-10-02 06:58:38 +00:00
size_t channels_count = 0;
2000-09-24 09:57:26 +00:00
snd_config_foreach(i, conf) {
snd_config_t *n = snd_config_entry(i);
if (strcmp(n->id, "comment") == 0)
continue;
if (strcmp(n->id, "type") == 0)
continue;
if (strcmp(n->id, "stream") == 0)
continue;
if (strcmp(n->id, "slave") == 0) {
2000-11-30 09:40:50 +00:00
if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) {
ERR("Invalid type for %s", n->id);
2000-09-24 09:57:26 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
slave = n;
continue;
}
if (strcmp(n->id, "binding") == 0) {
2000-11-30 09:40:50 +00:00
if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) {
ERR("Invalid type for %s", n->id);
2000-09-24 09:57:26 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
binding = n;
continue;
}
2000-11-30 09:40:50 +00:00
ERR("Unknown field %s", n->id);
return -EINVAL;
}
if (!slave) {
ERR("slave is not defined");
2000-09-24 09:57:26 +00:00
return -EINVAL;
}
2000-11-30 09:40:50 +00:00
if (!binding) {
ERR("binding is not defined");
2000-09-24 09:57:26 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
snd_config_foreach(i, slave) {
++slaves_count;
}
snd_config_foreach(i, binding) {
2000-10-02 06:58:38 +00:00
int cchannel = -1;
char *p;
snd_config_t *m = snd_config_entry(i);
errno = 0;
cchannel = strtol(m->id, &p, 10);
2000-11-30 09:40:50 +00:00
if (errno || *p || cchannel < 0) {
ERR("Invalid channel number: %s", m->id);
2000-10-02 06:58:38 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
if ((unsigned)cchannel >= channels_count)
2000-10-02 06:58:38 +00:00
channels_count = cchannel + 1;
2000-09-24 09:57:26 +00:00
}
2000-11-30 09:40:50 +00:00
if (channels_count == 0) {
ERR("No cannels defined");
2000-10-02 06:58:38 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
slaves_id = calloc(slaves_count, sizeof(*slaves_id));
slaves_name = calloc(slaves_count, sizeof(*slaves_name));
slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm));
slaves_channels = calloc(slaves_count, sizeof(*slaves_channels));
2000-10-02 06:58:38 +00:00
channels_sidx = calloc(channels_count, sizeof(*channels_sidx));
channels_schannel = calloc(channels_count, sizeof(*channels_schannel));
2000-09-24 09:57:26 +00:00
idx = 0;
2000-10-02 06:58:38 +00:00
for (idx = 0; idx < channels_count; ++idx)
channels_sidx[idx] = -1;
2000-11-30 09:40:50 +00:00
idx = 0;
2000-09-24 09:57:26 +00:00
snd_config_foreach(i, slave) {
snd_config_t *m = snd_config_entry(i);
2000-10-02 06:58:38 +00:00
char *name = NULL;
2000-09-24 09:57:26 +00:00
long channels = -1;
2000-11-30 09:40:50 +00:00
slaves_id[idx] = m->id;
2000-09-24 09:57:26 +00:00
snd_config_foreach(j, m) {
snd_config_t *n = snd_config_entry(j);
if (strcmp(n->id, "comment") == 0)
continue;
2000-10-02 06:58:38 +00:00
if (strcmp(n->id, "name") == 0) {
err = snd_config_string_get(n, &name);
2000-11-30 09:40:50 +00:00
if (err < 0) {
ERR("Invalid type for %s", n->id);
2000-09-24 09:57:26 +00:00
goto _free;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
continue;
}
if (strcmp(n->id, "channels") == 0) {
err = snd_config_integer_get(n, &channels);
2000-11-30 09:40:50 +00:00
if (err < 0) {
ERR("Invalid type for %s", n->id);
2000-09-24 09:57:26 +00:00
goto _free;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
continue;
}
2000-11-30 09:40:50 +00:00
ERR("Unknown field %s", n->id);
2000-09-24 09:57:26 +00:00
err = -EINVAL;
goto _free;
}
2000-11-30 09:40:50 +00:00
if (!name) {
ERR("name is not defined");
err = -EINVAL;
goto _free;
}
if (channels < 0) {
ERR("channels is not defined");
2000-09-24 09:57:26 +00:00
err = -EINVAL;
goto _free;
}
2000-10-02 06:58:38 +00:00
slaves_name[idx] = strdup(name);
2000-09-24 09:57:26 +00:00
slaves_channels[idx] = channels;
++idx;
}
snd_config_foreach(i, binding) {
snd_config_t *m = snd_config_entry(i);
2000-10-02 06:58:38 +00:00
long cchannel = -1;
long schannel = -1;
2000-09-24 09:57:26 +00:00
int slave = -1;
long val;
char *str;
2000-10-02 06:58:38 +00:00
cchannel = strtol(m->id, 0, 10);
2000-11-30 09:40:50 +00:00
if (cchannel < 0) {
ERR("Invalid channel number: %s", m->id);
err = -EINVAL;
goto _free;
}
2000-09-24 09:57:26 +00:00
snd_config_foreach(j, m) {
snd_config_t *n = snd_config_entry(j);
if (strcmp(n->id, "comment") == 0)
continue;
2000-10-02 06:58:38 +00:00
if (strcmp(n->id, "sidx") == 0) {
2000-09-24 09:57:26 +00:00
char buf[32];
unsigned int k;
err = snd_config_string_get(n, &str);
if (err < 0) {
err = snd_config_integer_get(n, &val);
2000-11-30 09:40:50 +00:00
if (err < 0) {
ERR("Invalid value for %s", n->id);
2000-09-24 09:57:26 +00:00
goto _free;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
sprintf(buf, "%ld", val);
str = buf;
}
for (k = 0; k < slaves_count; ++k) {
if (strcmp(slaves_id[k], str) == 0)
slave = k;
}
continue;
}
2000-10-02 06:58:38 +00:00
if (strcmp(n->id, "schannel") == 0) {
2000-09-24 09:57:26 +00:00
err = snd_config_integer_get(n, &schannel);
2000-11-30 09:40:50 +00:00
if (err < 0) {
ERR("Invalid type for %s", n->id);
2000-09-24 09:57:26 +00:00
goto _free;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
continue;
}
2000-11-30 09:40:50 +00:00
ERR("Unknown field %s", n->id);
2000-09-24 09:57:26 +00:00
err = -EINVAL;
goto _free;
}
2000-11-30 09:40:50 +00:00
if (slave < 0 || (size_t)slave >= slaves_count) {
ERR("Invalid or missing sidx");
2000-09-24 09:57:26 +00:00
err = -EINVAL;
goto _free;
}
2000-11-30 09:40:50 +00:00
if (schannel < 0 ||
(unsigned int) schannel >= slaves_channels[slave]) {
ERR("Invalid or missing schannel");
2000-09-24 09:57:26 +00:00
err = -EINVAL;
goto _free;
}
2000-10-02 06:58:38 +00:00
channels_sidx[cchannel] = slave;
channels_schannel[cchannel] = schannel;
2000-09-24 09:57:26 +00:00
}
for (idx = 0; idx < slaves_count; ++idx) {
err = snd_pcm_open(&slaves_pcm[idx], slaves_name[idx], stream, mode);
if (err < 0)
goto _free;
}
2000-10-10 09:11:07 +00:00
err = snd_pcm_multi_open(pcmp, name, slaves_count, slaves_pcm,
slaves_channels,
channels_count,
channels_sidx, channels_schannel,
1);
2000-09-24 09:57:26 +00:00
_free:
if (err < 0) {
for (idx = 0; idx < slaves_count; ++idx) {
if (slaves_pcm[idx])
snd_pcm_close(slaves_pcm[idx]);
if (slaves_name[idx])
free(slaves_name[idx]);
}
}
if (slaves_name)
free(slaves_name);
if (slaves_pcm)
free(slaves_pcm);
if (slaves_channels)
free(slaves_channels);
2000-10-02 06:58:38 +00:00
if (channels_sidx)
free(channels_sidx);
if (channels_schannel)
free(channels_schannel);
2000-09-24 09:57:26 +00:00
return err;
}