alsa-lib/src/pcm/pcm_multi.c
2001-02-12 23:51:49 +00:00

805 lines
22 KiB
C

/*
* 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 <math.h>
#include "pcm_local.h"
typedef struct {
snd_pcm_t *pcm;
unsigned int channels_count;
int close_slave;
} snd_pcm_multi_slave_t;
typedef struct {
int slave_idx;
unsigned int slave_channel;
} snd_pcm_multi_channel_t;
typedef struct {
unsigned int slaves_count;
snd_pcm_multi_slave_t *slaves;
unsigned int channels_count;
snd_pcm_multi_channel_t *channels;
} snd_pcm_multi_t;
static int snd_pcm_multi_close(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int i;
int ret = 0;
for (i = 0; i < multi->slaves_count; ++i) {
int err;
snd_pcm_multi_slave_t *slave = &multi->slaves[i];
if (slave->close_slave)
err = snd_pcm_close(slave->pcm);
else
err = snd_pcm_unlink(slave->pcm);
if (err < 0)
ret = err;
}
free(multi->slaves);
free(multi->channels);
free(multi);
return ret;
}
static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid)
{
snd_pcm_multi_t *multi = pcm->private_data;
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)
{
snd_pcm_multi_t *multi = pcm->private_data;
if (multi->slaves_count == 1)
return snd_pcm_info(multi->slaves[0].pcm, info);
memset(info, 0, sizeof(*info));
info->stream = snd_enum_to_int(pcm->stream);
info->card = -1;
strcpy(info->id, "multi");
strcpy(info->name, "multi");
strcpy(info->subname, "multi");
info->subdevices_count = 1;
return 0;
}
static int snd_pcm_multi_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_access_mask_t access_mask;
int err;
snd_pcm_access_mask_any(&access_mask);
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
&access_mask);
if (err < 0)
return err;
err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS,
multi->channels_count, 0);
if (err < 0)
return err;
params->info = ~0;
return 0;
}
static int snd_pcm_multi_hw_refine_sprepare(snd_pcm_t *pcm, int slave_idx,
snd_pcm_hw_params_t *sparams)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_multi_slave_t *slave = &multi->slaves[slave_idx];
snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
_snd_pcm_hw_params_any(sparams);
_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
&saccess_mask);
_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
slave->channels_count, 0);
return 0;
}
static int snd_pcm_multi_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
int slave_idx ATTRIBUTE_UNUSED,
snd_pcm_hw_params_t *params,
snd_pcm_hw_params_t *sparams)
{
int err;
unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
SND_PCM_HW_PARBIT_SUBFORMAT |
SND_PCM_HW_PARBIT_RATE |
SND_PCM_HW_PARBIT_PERIOD_SIZE |
SND_PCM_HW_PARBIT_PERIOD_TIME |
SND_PCM_HW_PARBIT_PERIODS |
SND_PCM_HW_PARBIT_BUFFER_SIZE |
SND_PCM_HW_PARBIT_BUFFER_TIME |
SND_PCM_HW_PARBIT_TICK_TIME);
const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) &&
!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
snd_pcm_access_mask_t saccess_mask;
snd_pcm_access_mask_any(&saccess_mask);
snd_pcm_access_mask_reset(&saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
&saccess_mask);
if (err < 0)
return err;
}
err = _snd_pcm_hw_params_refine(sparams, links, params);
if (err < 0)
return err;
return 0;
}
static int snd_pcm_multi_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
int slave_idx ATTRIBUTE_UNUSED,
snd_pcm_hw_params_t *params,
snd_pcm_hw_params_t *sparams)
{
int err;
unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
SND_PCM_HW_PARBIT_SUBFORMAT |
SND_PCM_HW_PARBIT_RATE |
SND_PCM_HW_PARBIT_PERIOD_SIZE |
SND_PCM_HW_PARBIT_PERIOD_TIME |
SND_PCM_HW_PARBIT_PERIODS |
SND_PCM_HW_PARBIT_BUFFER_SIZE |
SND_PCM_HW_PARBIT_BUFFER_TIME |
SND_PCM_HW_PARBIT_TICK_TIME);
snd_pcm_access_mask_t access_mask;
const snd_pcm_access_mask_t *saccess_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS);
snd_pcm_access_mask_any(&access_mask);
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX) &&
!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
&access_mask);
if (err < 0)
return err;
err = _snd_pcm_hw_params_refine(params, links, sparams);
if (err < 0)
return err;
params->info &= sparams->info;
return 0;
}
static int snd_pcm_multi_hw_refine_slave(snd_pcm_t *pcm,
int slave_idx,
snd_pcm_hw_params_t *sparams)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
return snd_pcm_hw_refine(slave, sparams);
}
static int snd_pcm_multi_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int k;
snd_pcm_hw_params_t sparams[multi->slaves_count];
int err;
unsigned int cmask, changed;
err = snd_pcm_multi_hw_refine_cprepare(pcm, params);
if (err < 0)
return err;
for (k = 0; k < multi->slaves_count; ++k) {
err = snd_pcm_multi_hw_refine_sprepare(pcm, k, &sparams[k]);
if (err < 0) {
ERR("Slave PCM #%d not useable", k);
return err;
}
}
do {
cmask = params->cmask;
params->cmask = 0;
for (k = 0; k < multi->slaves_count; ++k) {
err = snd_pcm_multi_hw_refine_schange(pcm, k, params, &sparams[k]);
if (err >= 0)
err = snd_pcm_multi_hw_refine_slave(pcm, k, &sparams[k]);
if (err < 0) {
snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
return err;
}
err = snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
if (err < 0)
return err;
}
err = snd_pcm_hw_refine_soft(pcm, params);
changed = params->cmask;
params->cmask |= cmask;
if (err < 0)
return err;
} while (changed);
return 0;
}
static int snd_pcm_multi_hw_params_slave(snd_pcm_t *pcm,
int slave_idx,
snd_pcm_hw_params_t *sparams)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
int err = snd_pcm_hw_refine(slave, sparams);
if (err < 0)
return err;
err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);
if (err < 0)
return err;
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;
}
static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int k;
snd_pcm_hw_params_t sparams[multi->slaves_count];
int err;
for (k = 0; k < multi->slaves_count; ++k) {
err = snd_pcm_multi_hw_refine_sprepare(pcm, k, &sparams[k]);
assert(err >= 0);
err = snd_pcm_multi_hw_refine_schange(pcm, k, params, &sparams[k]);
assert(err >= 0);
err = snd_pcm_multi_hw_params_slave(pcm, k, &sparams[k]);
if (err < 0) {
snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
return err;
}
}
return 0;
}
static int snd_pcm_multi_hw_free(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int i;
int err = 0;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
int e = snd_pcm_hw_free(slave);
if (e < 0)
err = e;
}
return err;
}
static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int i;
int err;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
err = snd_pcm_sw_params(slave, params);
if (err < 0)
return err;
}
return 0;
}
static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_status(slave, status);
}
static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_state(slave);
}
static int snd_pcm_multi_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_delay(slave, delayp);
}
static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_avail_update(slave);
}
static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
return snd_pcm_prepare(multi->slaves[0].pcm);
}
static int snd_pcm_multi_reset(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
return snd_pcm_reset(multi->slaves[0].pcm);
}
static int snd_pcm_multi_start(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
return snd_pcm_start(multi->slaves[0].pcm);
}
static int snd_pcm_multi_drop(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
return snd_pcm_drop(multi->slaves[0].pcm);
}
static int snd_pcm_multi_drain(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
return snd_pcm_drain(multi->slaves[0].pcm);
}
static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable)
{
snd_pcm_multi_t *multi = pcm->private_data;
return snd_pcm_pause(multi->slaves[0].pcm, enable);
}
static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int channel = info->channel;
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;
return err;
}
static snd_pcm_sframes_t snd_pcm_multi_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int i;
snd_pcm_uframes_t pos[multi->slaves_count];
memset(pos, 0, sizeof(pos));
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave_i = multi->slaves[i].pcm;
snd_pcm_sframes_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) {
snd_pcm_t *slave_i = multi->slaves[i].pcm;
snd_pcm_uframes_t f = pos[i] - frames;
if (f > 0)
snd_pcm_mmap_appl_forward(slave_i, f);
}
return frames;
}
static snd_pcm_sframes_t snd_pcm_multi_mmap_forward(snd_pcm_t *pcm, snd_pcm_uframes_t size)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
snd_pcm_sframes_t frames = snd_pcm_mmap_forward(slave, size);
if (frames < 0)
return frames;
if (i == 0) {
size = frames;
continue;
}
if ((snd_pcm_uframes_t) frames != size)
return -EBADFD;
}
return size;
}
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;
}
int snd_pcm_multi_poll_descriptor(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[0].pcm;
return _snd_pcm_poll_descriptor(slave);
}
static void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int k;
snd_output_printf(out, "Multi PCM\n");
snd_output_printf(out, "\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;
snd_output_printf(out, "%d: slave %d, channel %d\n",
k, c->slave_idx, c->slave_channel);
}
if (pcm->setup) {
snd_output_printf(out, "\nIts setup is:\n");
snd_pcm_dump_setup(pcm, out);
}
for (k = 0; k < multi->slaves_count; ++k) {
snd_output_printf(out, "\nSlave #%d: ", k);
snd_pcm_dump(multi->slaves[k].pcm, out);
}
}
snd_pcm_ops_t snd_pcm_multi_ops = {
close: snd_pcm_multi_close,
info: snd_pcm_multi_info,
hw_refine: snd_pcm_multi_hw_refine,
hw_params: snd_pcm_multi_hw_params,
hw_free: snd_pcm_multi_hw_free,
sw_params: snd_pcm_multi_sw_params,
channel_info: snd_pcm_multi_channel_info,
dump: snd_pcm_multi_dump,
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 = {
status: snd_pcm_multi_status,
state: snd_pcm_multi_state,
delay: snd_pcm_multi_delay,
prepare: snd_pcm_multi_prepare,
reset: snd_pcm_multi_reset,
start: snd_pcm_multi_start,
drop: snd_pcm_multi_drop,
drain: snd_pcm_multi_drain,
pause: snd_pcm_multi_pause,
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,
avail_update: snd_pcm_multi_avail_update,
mmap_forward: snd_pcm_multi_mmap_forward,
};
int snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
unsigned int slaves_count,
snd_pcm_t **slaves_pcm, unsigned int *schannels_count,
unsigned int channels_count,
int *sidxs, unsigned int *schannels,
int close_slaves)
{
snd_pcm_t *pcm;
snd_pcm_multi_t *multi;
unsigned int i;
snd_pcm_stream_t stream;
char slave_map[32][32] = { { 0 } };
assert(pcmp);
assert(slaves_count > 0 && slaves_pcm && schannels_count);
assert(channels_count > 0 && sidxs && schannels);
multi = calloc(1, sizeof(snd_pcm_multi_t));
if (!multi) {
return -ENOMEM;
}
stream = slaves_pcm[0]->stream;
multi->slaves_count = slaves_count;
multi->slaves = calloc(slaves_count, sizeof(*multi->slaves));
multi->channels_count = channels_count;
multi->channels = calloc(channels_count, sizeof(*multi->channels));
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];
slave->channels_count = schannels_count[i];
slave->close_slave = close_slaves;
if (i != 0)
snd_pcm_link(slaves_pcm[i-1], slaves_pcm[i]);
}
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;
}
multi->channels_count = channels_count;
pcm = calloc(1, sizeof(snd_pcm_t));
if (!pcm) {
free(multi);
return -ENOMEM;
}
if (name)
pcm->name = strdup(name);
pcm->type = SND_PCM_TYPE_MULTI;
pcm->stream = stream;
pcm->mode = multi->slaves[0].pcm->mode;
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_data = 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;
return 0;
}
int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, snd_config_t *conf,
snd_pcm_stream_t stream, int mode)
{
snd_config_iterator_t i, inext, j, jnext;
snd_config_t *slave = NULL;
snd_config_t *binding = NULL;
int err;
unsigned int idx;
const char **slaves_id = NULL;
char **slaves_name = NULL;
snd_pcm_t **slaves_pcm = NULL;
unsigned int *slaves_channels = NULL;
unsigned int *channels_sidx = NULL;
unsigned int *channels_schannel = NULL;
unsigned int slaves_count = 0;
unsigned int channels_count = 0;
snd_config_for_each(i, inext, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "type") == 0)
continue;
if (strcmp(id, "slave") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
ERR("Invalid type for %s", id);
return -EINVAL;
}
slave = n;
continue;
}
if (strcmp(id, "binding") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
ERR("Invalid type for %s", id);
return -EINVAL;
}
binding = n;
continue;
}
ERR("Unknown field %s", id);
return -EINVAL;
}
if (!slave) {
ERR("slave is not defined");
return -EINVAL;
}
if (!binding) {
ERR("binding is not defined");
return -EINVAL;
}
snd_config_for_each(i, inext, slave) {
++slaves_count;
}
snd_config_for_each(i, inext, binding) {
int cchannel = -1;
char *p;
snd_config_t *m = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(m);
errno = 0;
cchannel = strtol(id, &p, 10);
if (errno || *p || cchannel < 0) {
ERR("Invalid channel number: %s", id);
return -EINVAL;
}
if ((unsigned)cchannel >= channels_count)
channels_count = cchannel + 1;
}
if (channels_count == 0) {
ERR("No cannels defined");
return -EINVAL;
}
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));
channels_sidx = calloc(channels_count, sizeof(*channels_sidx));
channels_schannel = calloc(channels_count, sizeof(*channels_schannel));
idx = 0;
for (idx = 0; idx < channels_count; ++idx)
channels_sidx[idx] = -1;
idx = 0;
snd_config_for_each(i, inext, slave) {
snd_config_t *m = snd_config_iterator_entry(i);
const char *name = NULL;
long channels = -1;
slaves_id[idx] = snd_config_get_id(m);
snd_config_for_each(j, jnext, m) {
snd_config_t *n = snd_config_iterator_entry(j);
const char *id = snd_config_get_id(n);
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "name") == 0) {
err = snd_config_get_string(n, &name);
if (err < 0) {
ERR("Invalid type for %s", id);
goto _free;
}
continue;
}
if (strcmp(id, "channels") == 0) {
err = snd_config_get_integer(n, &channels);
if (err < 0) {
ERR("Invalid type for %s", id);
goto _free;
}
continue;
}
ERR("Unknown field %s", id);
err = -EINVAL;
goto _free;
}
if (!name) {
ERR("name is not defined");
err = -EINVAL;
goto _free;
}
if (channels < 0) {
ERR("channels is not defined");
err = -EINVAL;
goto _free;
}
slaves_name[idx] = strdup(name);
slaves_channels[idx] = channels;
++idx;
}
snd_config_for_each(i, inext, binding) {
snd_config_t *m = snd_config_iterator_entry(i);
long cchannel = -1;
long schannel = -1;
int slave = -1;
long val;
const char *str;
const char *id = snd_config_get_id(m);
cchannel = strtol(id, 0, 10);
if (cchannel < 0) {
ERR("Invalid channel number: %s", id);
err = -EINVAL;
goto _free;
}
snd_config_for_each(j, jnext, m) {
snd_config_t *n = snd_config_iterator_entry(j);
const char *id = snd_config_get_id(n);
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "sidx") == 0) {
char buf[32];
unsigned int k;
err = snd_config_get_string(n, &str);
if (err < 0) {
err = snd_config_get_integer(n, &val);
if (err < 0) {
ERR("Invalid value for %s", id);
goto _free;
}
sprintf(buf, "%ld", val);
str = buf;
}
for (k = 0; k < slaves_count; ++k) {
if (strcmp(slaves_id[k], str) == 0)
slave = k;
}
continue;
}
if (strcmp(id, "schannel") == 0) {
err = snd_config_get_integer(n, &schannel);
if (err < 0) {
ERR("Invalid type for %s", id);
goto _free;
}
continue;
}
ERR("Unknown field %s", id);
err = -EINVAL;
goto _free;
}
if (slave < 0 || (unsigned int)slave >= slaves_count) {
ERR("Invalid or missing sidx");
err = -EINVAL;
goto _free;
}
if (schannel < 0 ||
(unsigned int) schannel >= slaves_channels[slave]) {
ERR("Invalid or missing schannel");
err = -EINVAL;
goto _free;
}
channels_sidx[cchannel] = slave;
channels_schannel[cchannel] = schannel;
}
for (idx = 0; idx < slaves_count; ++idx) {
err = snd_pcm_open(&slaves_pcm[idx], slaves_name[idx], stream, mode);
if (err < 0)
goto _free;
}
err = snd_pcm_multi_open(pcmp, name, slaves_count, slaves_pcm,
slaves_channels,
channels_count,
channels_sidx, channels_schannel,
1);
_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);
if (channels_sidx)
free(channels_sidx);
if (channels_schannel)
free(channels_schannel);
return err;
}