1d5ac70f0Sopenharmony_ci/** 2d5ac70f0Sopenharmony_ci * \file pcm/pcm_multi.c 3d5ac70f0Sopenharmony_ci * \ingroup PCM_Plugins 4d5ac70f0Sopenharmony_ci * \brief PCM Multi Streams to One Conversion Plugin Interface 5d5ac70f0Sopenharmony_ci * \author Abramo Bagnara <abramo@alsa-project.org> 6d5ac70f0Sopenharmony_ci * \date 2000-2001 7d5ac70f0Sopenharmony_ci */ 8d5ac70f0Sopenharmony_ci/* 9d5ac70f0Sopenharmony_ci * PCM - Multi 10d5ac70f0Sopenharmony_ci * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> 11d5ac70f0Sopenharmony_ci * 12d5ac70f0Sopenharmony_ci * 13d5ac70f0Sopenharmony_ci * This library is free software; you can redistribute it and/or modify 14d5ac70f0Sopenharmony_ci * it under the terms of the GNU Lesser General Public License as 15d5ac70f0Sopenharmony_ci * published by the Free Software Foundation; either version 2.1 of 16d5ac70f0Sopenharmony_ci * the License, or (at your option) any later version. 17d5ac70f0Sopenharmony_ci * 18d5ac70f0Sopenharmony_ci * This program is distributed in the hope that it will be useful, 19d5ac70f0Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 20d5ac70f0Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21d5ac70f0Sopenharmony_ci * GNU Lesser General Public License for more details. 22d5ac70f0Sopenharmony_ci * 23d5ac70f0Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public 24d5ac70f0Sopenharmony_ci * License along with this library; if not, write to the Free Software 25d5ac70f0Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 26d5ac70f0Sopenharmony_ci * 27d5ac70f0Sopenharmony_ci */ 28d5ac70f0Sopenharmony_ci 29d5ac70f0Sopenharmony_ci#include "pcm_local.h" 30d5ac70f0Sopenharmony_ci#include "pcm_generic.h" 31d5ac70f0Sopenharmony_ci#include <stdio.h> 32d5ac70f0Sopenharmony_ci#include <stdlib.h> 33d5ac70f0Sopenharmony_ci#include <unistd.h> 34d5ac70f0Sopenharmony_ci#include <string.h> 35d5ac70f0Sopenharmony_ci#include <math.h> 36d5ac70f0Sopenharmony_ci 37d5ac70f0Sopenharmony_ci#ifndef PIC 38d5ac70f0Sopenharmony_ci/* entry for static linking */ 39d5ac70f0Sopenharmony_ciconst char *_snd_module_pcm_multi = ""; 40d5ac70f0Sopenharmony_ci#endif 41d5ac70f0Sopenharmony_ci 42d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN 43d5ac70f0Sopenharmony_ci 44d5ac70f0Sopenharmony_citypedef struct { 45d5ac70f0Sopenharmony_ci snd_pcm_t *pcm; 46d5ac70f0Sopenharmony_ci unsigned int channels_count; 47d5ac70f0Sopenharmony_ci int close_slave; 48d5ac70f0Sopenharmony_ci snd_pcm_t *linked; 49d5ac70f0Sopenharmony_ci} snd_pcm_multi_slave_t; 50d5ac70f0Sopenharmony_ci 51d5ac70f0Sopenharmony_citypedef struct { 52d5ac70f0Sopenharmony_ci int slave_idx; 53d5ac70f0Sopenharmony_ci unsigned int slave_channel; 54d5ac70f0Sopenharmony_ci} snd_pcm_multi_channel_t; 55d5ac70f0Sopenharmony_ci 56d5ac70f0Sopenharmony_citypedef struct { 57d5ac70f0Sopenharmony_ci snd_pcm_uframes_t appl_ptr, hw_ptr; 58d5ac70f0Sopenharmony_ci unsigned int slaves_count; 59d5ac70f0Sopenharmony_ci unsigned int master_slave; 60d5ac70f0Sopenharmony_ci snd_pcm_multi_slave_t *slaves; 61d5ac70f0Sopenharmony_ci unsigned int channels_count; 62d5ac70f0Sopenharmony_ci snd_pcm_multi_channel_t *channels; 63d5ac70f0Sopenharmony_ci} snd_pcm_multi_t; 64d5ac70f0Sopenharmony_ci 65d5ac70f0Sopenharmony_ci#endif 66d5ac70f0Sopenharmony_ci 67d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_close(snd_pcm_t *pcm) 68d5ac70f0Sopenharmony_ci{ 69d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 70d5ac70f0Sopenharmony_ci unsigned int i; 71d5ac70f0Sopenharmony_ci int ret = 0; 72d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 73d5ac70f0Sopenharmony_ci snd_pcm_multi_slave_t *slave = &multi->slaves[i]; 74d5ac70f0Sopenharmony_ci if (slave->close_slave) { 75d5ac70f0Sopenharmony_ci int err = snd_pcm_close(slave->pcm); 76d5ac70f0Sopenharmony_ci if (err < 0) 77d5ac70f0Sopenharmony_ci ret = err; 78d5ac70f0Sopenharmony_ci } 79d5ac70f0Sopenharmony_ci } 80d5ac70f0Sopenharmony_ci free(multi->slaves); 81d5ac70f0Sopenharmony_ci free(multi->channels); 82d5ac70f0Sopenharmony_ci free(multi); 83d5ac70f0Sopenharmony_ci return ret; 84d5ac70f0Sopenharmony_ci} 85d5ac70f0Sopenharmony_ci 86d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED) 87d5ac70f0Sopenharmony_ci{ 88d5ac70f0Sopenharmony_ci return 0; 89d5ac70f0Sopenharmony_ci} 90d5ac70f0Sopenharmony_ci 91d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid) 92d5ac70f0Sopenharmony_ci{ 93d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 94d5ac70f0Sopenharmony_ci snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm; 95d5ac70f0Sopenharmony_ci return snd_pcm_async(slave_0, sig, pid); 96d5ac70f0Sopenharmony_ci} 97d5ac70f0Sopenharmony_ci 98d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_poll_descriptors_count(snd_pcm_t *pcm) 99d5ac70f0Sopenharmony_ci{ 100d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 101d5ac70f0Sopenharmony_ci snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm; 102d5ac70f0Sopenharmony_ci return snd_pcm_poll_descriptors_count(slave_0); 103d5ac70f0Sopenharmony_ci} 104d5ac70f0Sopenharmony_ci 105d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space) 106d5ac70f0Sopenharmony_ci{ 107d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 108d5ac70f0Sopenharmony_ci snd_pcm_t *slave; 109d5ac70f0Sopenharmony_ci snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm; 110d5ac70f0Sopenharmony_ci int err; 111d5ac70f0Sopenharmony_ci unsigned int i; 112d5ac70f0Sopenharmony_ci 113d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 114d5ac70f0Sopenharmony_ci slave = multi->slaves[i].pcm; 115d5ac70f0Sopenharmony_ci if (slave == slave_0) 116d5ac70f0Sopenharmony_ci continue; 117d5ac70f0Sopenharmony_ci err = snd_pcm_poll_descriptors(slave, pfds, space); 118d5ac70f0Sopenharmony_ci if (err < 0) 119d5ac70f0Sopenharmony_ci return err; 120d5ac70f0Sopenharmony_ci } 121d5ac70f0Sopenharmony_ci /* finally overwrite with master's pfds */ 122d5ac70f0Sopenharmony_ci return snd_pcm_poll_descriptors(slave_0, pfds, space); 123d5ac70f0Sopenharmony_ci} 124d5ac70f0Sopenharmony_ci 125d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) 126d5ac70f0Sopenharmony_ci{ 127d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 128d5ac70f0Sopenharmony_ci snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm; 129d5ac70f0Sopenharmony_ci return snd_pcm_poll_descriptors_revents(slave_0, pfds, nfds, revents); 130d5ac70f0Sopenharmony_ci} 131d5ac70f0Sopenharmony_ci 132d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info) 133d5ac70f0Sopenharmony_ci{ 134d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 135d5ac70f0Sopenharmony_ci int err, n; 136d5ac70f0Sopenharmony_ci assert(info->subdevice < multi->slaves_count); 137d5ac70f0Sopenharmony_ci n = info->subdevice; 138d5ac70f0Sopenharmony_ci info->subdevice = 0; 139d5ac70f0Sopenharmony_ci err = snd_pcm_info(multi->slaves[n].pcm, info); 140d5ac70f0Sopenharmony_ci if (err < 0) 141d5ac70f0Sopenharmony_ci return err; 142d5ac70f0Sopenharmony_ci info->subdevices_count = multi->slaves_count; 143d5ac70f0Sopenharmony_ci return 0; 144d5ac70f0Sopenharmony_ci} 145d5ac70f0Sopenharmony_ci 146d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 147d5ac70f0Sopenharmony_ci{ 148d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 149d5ac70f0Sopenharmony_ci snd_pcm_access_mask_t access_mask; 150d5ac70f0Sopenharmony_ci int err; 151d5ac70f0Sopenharmony_ci snd_pcm_access_mask_any(&access_mask); 152d5ac70f0Sopenharmony_ci snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); 153d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS, 154d5ac70f0Sopenharmony_ci &access_mask); 155d5ac70f0Sopenharmony_ci if (err < 0) 156d5ac70f0Sopenharmony_ci return err; 157d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS, 158d5ac70f0Sopenharmony_ci multi->channels_count, 0); 159d5ac70f0Sopenharmony_ci if (err < 0) 160d5ac70f0Sopenharmony_ci return err; 161d5ac70f0Sopenharmony_ci params->info = ~0U; 162d5ac70f0Sopenharmony_ci return 0; 163d5ac70f0Sopenharmony_ci} 164d5ac70f0Sopenharmony_ci 165d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_hw_refine_sprepare(snd_pcm_t *pcm, unsigned int slave_idx, 166d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *sparams) 167d5ac70f0Sopenharmony_ci{ 168d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 169d5ac70f0Sopenharmony_ci snd_pcm_multi_slave_t *slave = &multi->slaves[slave_idx]; 170d5ac70f0Sopenharmony_ci snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP }; 171d5ac70f0Sopenharmony_ci _snd_pcm_hw_params_any(sparams); 172d5ac70f0Sopenharmony_ci _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, 173d5ac70f0Sopenharmony_ci &saccess_mask); 174d5ac70f0Sopenharmony_ci _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS, 175d5ac70f0Sopenharmony_ci slave->channels_count, 0); 176d5ac70f0Sopenharmony_ci return 0; 177d5ac70f0Sopenharmony_ci} 178d5ac70f0Sopenharmony_ci 179d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, 180d5ac70f0Sopenharmony_ci unsigned int slave_idx ATTRIBUTE_UNUSED, 181d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *params, 182d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *sparams) 183d5ac70f0Sopenharmony_ci{ 184d5ac70f0Sopenharmony_ci int err; 185d5ac70f0Sopenharmony_ci unsigned int links = (SND_PCM_HW_PARBIT_FORMAT | 186d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_SUBFORMAT | 187d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_RATE | 188d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_PERIOD_SIZE | 189d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_PERIOD_TIME | 190d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_PERIODS | 191d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_BUFFER_SIZE | 192d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_BUFFER_TIME | 193d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_TICK_TIME); 194d5ac70f0Sopenharmony_ci const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); 195d5ac70f0Sopenharmony_ci if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) && 196d5ac70f0Sopenharmony_ci !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) && 197d5ac70f0Sopenharmony_ci !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) { 198d5ac70f0Sopenharmony_ci snd_pcm_access_mask_t saccess_mask; 199d5ac70f0Sopenharmony_ci snd_pcm_access_mask_any(&saccess_mask); 200d5ac70f0Sopenharmony_ci snd_pcm_access_mask_reset(&saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); 201d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, 202d5ac70f0Sopenharmony_ci &saccess_mask); 203d5ac70f0Sopenharmony_ci if (err < 0) 204d5ac70f0Sopenharmony_ci return err; 205d5ac70f0Sopenharmony_ci } 206d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_params_refine(sparams, links, params); 207d5ac70f0Sopenharmony_ci if (err < 0) 208d5ac70f0Sopenharmony_ci return err; 209d5ac70f0Sopenharmony_ci return 0; 210d5ac70f0Sopenharmony_ci} 211d5ac70f0Sopenharmony_ci 212d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, 213d5ac70f0Sopenharmony_ci unsigned int slave_idx ATTRIBUTE_UNUSED, 214d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *params, 215d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *sparams) 216d5ac70f0Sopenharmony_ci{ 217d5ac70f0Sopenharmony_ci int err; 218d5ac70f0Sopenharmony_ci unsigned int links = (SND_PCM_HW_PARBIT_FORMAT | 219d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_SUBFORMAT | 220d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_RATE | 221d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_PERIOD_SIZE | 222d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_PERIOD_TIME | 223d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_PERIODS | 224d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_BUFFER_SIZE | 225d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_BUFFER_TIME | 226d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_TICK_TIME); 227d5ac70f0Sopenharmony_ci snd_pcm_access_mask_t access_mask; 228d5ac70f0Sopenharmony_ci const snd_pcm_access_mask_t *saccess_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS); 229d5ac70f0Sopenharmony_ci snd_pcm_access_mask_any(&access_mask); 230d5ac70f0Sopenharmony_ci snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); 231d5ac70f0Sopenharmony_ci if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) 232d5ac70f0Sopenharmony_ci snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); 233d5ac70f0Sopenharmony_ci if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX) && 234d5ac70f0Sopenharmony_ci !snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) 235d5ac70f0Sopenharmony_ci snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_COMPLEX); 236d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS, 237d5ac70f0Sopenharmony_ci &access_mask); 238d5ac70f0Sopenharmony_ci if (err < 0) 239d5ac70f0Sopenharmony_ci return err; 240d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_params_refine(params, links, sparams); 241d5ac70f0Sopenharmony_ci if (err < 0) 242d5ac70f0Sopenharmony_ci return err; 243d5ac70f0Sopenharmony_ci params->info &= sparams->info; 244d5ac70f0Sopenharmony_ci return 0; 245d5ac70f0Sopenharmony_ci} 246d5ac70f0Sopenharmony_ci 247d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_hw_refine_slave(snd_pcm_t *pcm, 248d5ac70f0Sopenharmony_ci unsigned int slave_idx, 249d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *sparams) 250d5ac70f0Sopenharmony_ci{ 251d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 252d5ac70f0Sopenharmony_ci snd_pcm_t *slave = multi->slaves[slave_idx].pcm; 253d5ac70f0Sopenharmony_ci return snd_pcm_hw_refine(slave, sparams); 254d5ac70f0Sopenharmony_ci} 255d5ac70f0Sopenharmony_ci 256d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 257d5ac70f0Sopenharmony_ci{ 258d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 259d5ac70f0Sopenharmony_ci unsigned int k; 260d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t sparams[multi->slaves_count]; 261d5ac70f0Sopenharmony_ci int err; 262d5ac70f0Sopenharmony_ci unsigned int cmask, changed; 263d5ac70f0Sopenharmony_ci err = snd_pcm_multi_hw_refine_cprepare(pcm, params); 264d5ac70f0Sopenharmony_ci if (err < 0) 265d5ac70f0Sopenharmony_ci return err; 266d5ac70f0Sopenharmony_ci for (k = 0; k < multi->slaves_count; ++k) { 267d5ac70f0Sopenharmony_ci err = snd_pcm_multi_hw_refine_sprepare(pcm, k, &sparams[k]); 268d5ac70f0Sopenharmony_ci if (err < 0) { 269d5ac70f0Sopenharmony_ci SNDERR("Slave PCM #%d not usable", k); 270d5ac70f0Sopenharmony_ci return err; 271d5ac70f0Sopenharmony_ci } 272d5ac70f0Sopenharmony_ci } 273d5ac70f0Sopenharmony_ci do { 274d5ac70f0Sopenharmony_ci cmask = params->cmask; 275d5ac70f0Sopenharmony_ci params->cmask = 0; 276d5ac70f0Sopenharmony_ci for (k = 0; k < multi->slaves_count; ++k) { 277d5ac70f0Sopenharmony_ci err = snd_pcm_multi_hw_refine_schange(pcm, k, params, &sparams[k]); 278d5ac70f0Sopenharmony_ci if (err >= 0) 279d5ac70f0Sopenharmony_ci err = snd_pcm_multi_hw_refine_slave(pcm, k, &sparams[k]); 280d5ac70f0Sopenharmony_ci if (err < 0) { 281d5ac70f0Sopenharmony_ci snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]); 282d5ac70f0Sopenharmony_ci return err; 283d5ac70f0Sopenharmony_ci } 284d5ac70f0Sopenharmony_ci err = snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]); 285d5ac70f0Sopenharmony_ci if (err < 0) 286d5ac70f0Sopenharmony_ci return err; 287d5ac70f0Sopenharmony_ci } 288d5ac70f0Sopenharmony_ci err = snd_pcm_hw_refine_soft(pcm, params); 289d5ac70f0Sopenharmony_ci changed = params->cmask; 290d5ac70f0Sopenharmony_ci params->cmask |= cmask; 291d5ac70f0Sopenharmony_ci if (err < 0) 292d5ac70f0Sopenharmony_ci return err; 293d5ac70f0Sopenharmony_ci } while (changed); 294d5ac70f0Sopenharmony_ci return 0; 295d5ac70f0Sopenharmony_ci} 296d5ac70f0Sopenharmony_ci 297d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_hw_params_slave(snd_pcm_t *pcm, 298d5ac70f0Sopenharmony_ci unsigned int slave_idx, 299d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *sparams) 300d5ac70f0Sopenharmony_ci{ 301d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 302d5ac70f0Sopenharmony_ci snd_pcm_t *slave = multi->slaves[slave_idx].pcm; 303d5ac70f0Sopenharmony_ci int err = snd_pcm_hw_params(slave, sparams); 304d5ac70f0Sopenharmony_ci if (err < 0) 305d5ac70f0Sopenharmony_ci return err; 306d5ac70f0Sopenharmony_ci err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format); 307d5ac70f0Sopenharmony_ci if (err < 0) 308d5ac70f0Sopenharmony_ci return err; 309d5ac70f0Sopenharmony_ci if (slave->stopped_areas) { 310d5ac70f0Sopenharmony_ci err = snd_pcm_areas_silence(slave->stopped_areas, 0, slave->channels, slave->buffer_size, slave->format); 311d5ac70f0Sopenharmony_ci if (err < 0) 312d5ac70f0Sopenharmony_ci return err; 313d5ac70f0Sopenharmony_ci } 314d5ac70f0Sopenharmony_ci return 0; 315d5ac70f0Sopenharmony_ci} 316d5ac70f0Sopenharmony_ci 317d5ac70f0Sopenharmony_ci/* reset links to the normal state 318d5ac70f0Sopenharmony_ci * slave #0 = trigger master 319d5ac70f0Sopenharmony_ci * slave #1-(N-1) = trigger slaves, linked is set to #0 320d5ac70f0Sopenharmony_ci */ 321d5ac70f0Sopenharmony_cistatic void reset_links(snd_pcm_multi_t *multi) 322d5ac70f0Sopenharmony_ci{ 323d5ac70f0Sopenharmony_ci unsigned int i; 324d5ac70f0Sopenharmony_ci 325d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 326d5ac70f0Sopenharmony_ci if (multi->slaves[i].linked) 327d5ac70f0Sopenharmony_ci snd_pcm_unlink(multi->slaves[i].linked); 328d5ac70f0Sopenharmony_ci multi->slaves[0].linked = NULL; 329d5ac70f0Sopenharmony_ci if (! i) 330d5ac70f0Sopenharmony_ci continue; 331d5ac70f0Sopenharmony_ci if (snd_pcm_link(multi->slaves[0].pcm, multi->slaves[i].pcm) >= 0) 332d5ac70f0Sopenharmony_ci multi->slaves[i].linked = multi->slaves[0].pcm; 333d5ac70f0Sopenharmony_ci } 334d5ac70f0Sopenharmony_ci} 335d5ac70f0Sopenharmony_ci 336d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 337d5ac70f0Sopenharmony_ci{ 338d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 339d5ac70f0Sopenharmony_ci unsigned int i; 340d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t sparams[multi->slaves_count]; 341d5ac70f0Sopenharmony_ci int err; 342d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 343d5ac70f0Sopenharmony_ci err = snd_pcm_multi_hw_refine_sprepare(pcm, i, &sparams[i]); 344d5ac70f0Sopenharmony_ci assert(err >= 0); 345d5ac70f0Sopenharmony_ci err = snd_pcm_multi_hw_refine_schange(pcm, i, params, &sparams[i]); 346d5ac70f0Sopenharmony_ci assert(err >= 0); 347d5ac70f0Sopenharmony_ci err = snd_pcm_multi_hw_params_slave(pcm, i, &sparams[i]); 348d5ac70f0Sopenharmony_ci if (err < 0) { 349d5ac70f0Sopenharmony_ci snd_pcm_multi_hw_refine_cchange(pcm, i, params, &sparams[i]); 350d5ac70f0Sopenharmony_ci return err; 351d5ac70f0Sopenharmony_ci } 352d5ac70f0Sopenharmony_ci } 353d5ac70f0Sopenharmony_ci reset_links(multi); 354d5ac70f0Sopenharmony_ci return 0; 355d5ac70f0Sopenharmony_ci} 356d5ac70f0Sopenharmony_ci 357d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_hw_free(snd_pcm_t *pcm) 358d5ac70f0Sopenharmony_ci{ 359d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 360d5ac70f0Sopenharmony_ci unsigned int i; 361d5ac70f0Sopenharmony_ci int err = 0; 362d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 363d5ac70f0Sopenharmony_ci snd_pcm_t *slave = multi->slaves[i].pcm; 364d5ac70f0Sopenharmony_ci int e = snd_pcm_hw_free(slave); 365d5ac70f0Sopenharmony_ci if (e < 0) 366d5ac70f0Sopenharmony_ci err = e; 367d5ac70f0Sopenharmony_ci if (!multi->slaves[i].linked) 368d5ac70f0Sopenharmony_ci continue; 369d5ac70f0Sopenharmony_ci e = snd_pcm_unlink(slave); 370d5ac70f0Sopenharmony_ci if (e < 0) 371d5ac70f0Sopenharmony_ci err = e; 372d5ac70f0Sopenharmony_ci multi->slaves[i].linked = NULL; 373d5ac70f0Sopenharmony_ci } 374d5ac70f0Sopenharmony_ci return err; 375d5ac70f0Sopenharmony_ci} 376d5ac70f0Sopenharmony_ci 377d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) 378d5ac70f0Sopenharmony_ci{ 379d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 380d5ac70f0Sopenharmony_ci unsigned int i; 381d5ac70f0Sopenharmony_ci int err; 382d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 383d5ac70f0Sopenharmony_ci snd_pcm_t *slave = multi->slaves[i].pcm; 384d5ac70f0Sopenharmony_ci err = snd_pcm_sw_params(slave, params); 385d5ac70f0Sopenharmony_ci if (err < 0) 386d5ac70f0Sopenharmony_ci return err; 387d5ac70f0Sopenharmony_ci } 388d5ac70f0Sopenharmony_ci return 0; 389d5ac70f0Sopenharmony_ci} 390d5ac70f0Sopenharmony_ci 391d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm); 392d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status) 393d5ac70f0Sopenharmony_ci{ 394d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 395d5ac70f0Sopenharmony_ci snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm; 396d5ac70f0Sopenharmony_ci 397d5ac70f0Sopenharmony_ci int err = snd_pcm_status(slave, status); 398d5ac70f0Sopenharmony_ci if (err < 0) 399d5ac70f0Sopenharmony_ci return err; 400d5ac70f0Sopenharmony_ci snd_pcm_sframes_t avail = snd_pcm_multi_avail_update(pcm); 401d5ac70f0Sopenharmony_ci if (avail < 0) 402d5ac70f0Sopenharmony_ci return avail; 403d5ac70f0Sopenharmony_ci status->hw_ptr = *pcm->hw.ptr; 404d5ac70f0Sopenharmony_ci status->avail = avail; 405d5ac70f0Sopenharmony_ci return 0; 406d5ac70f0Sopenharmony_ci} 407d5ac70f0Sopenharmony_ci 408d5ac70f0Sopenharmony_cistatic snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm) 409d5ac70f0Sopenharmony_ci{ 410d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 411d5ac70f0Sopenharmony_ci snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm; 412d5ac70f0Sopenharmony_ci return snd_pcm_state(slave); 413d5ac70f0Sopenharmony_ci} 414d5ac70f0Sopenharmony_ci 415d5ac70f0Sopenharmony_cistatic void snd_pcm_multi_hwptr_update(snd_pcm_t *pcm) 416d5ac70f0Sopenharmony_ci{ 417d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 418d5ac70f0Sopenharmony_ci snd_pcm_uframes_t hw_ptr = 0, slave_hw_ptr, avail, last_avail; 419d5ac70f0Sopenharmony_ci unsigned int i; 420d5ac70f0Sopenharmony_ci /* the logic is really simple, choose the lowest hw_ptr from slaves */ 421d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 422d5ac70f0Sopenharmony_ci last_avail = 0; 423d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 424d5ac70f0Sopenharmony_ci slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr; 425d5ac70f0Sopenharmony_ci avail = __snd_pcm_playback_avail(pcm, multi->hw_ptr, slave_hw_ptr); 426d5ac70f0Sopenharmony_ci if (avail > last_avail) { 427d5ac70f0Sopenharmony_ci hw_ptr = slave_hw_ptr; 428d5ac70f0Sopenharmony_ci last_avail = avail; 429d5ac70f0Sopenharmony_ci } 430d5ac70f0Sopenharmony_ci } 431d5ac70f0Sopenharmony_ci } else { 432d5ac70f0Sopenharmony_ci last_avail = LONG_MAX; 433d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 434d5ac70f0Sopenharmony_ci slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr; 435d5ac70f0Sopenharmony_ci avail = __snd_pcm_capture_avail(pcm, multi->hw_ptr, slave_hw_ptr); 436d5ac70f0Sopenharmony_ci if (avail < last_avail) { 437d5ac70f0Sopenharmony_ci hw_ptr = slave_hw_ptr; 438d5ac70f0Sopenharmony_ci last_avail = avail; 439d5ac70f0Sopenharmony_ci } 440d5ac70f0Sopenharmony_ci } 441d5ac70f0Sopenharmony_ci } 442d5ac70f0Sopenharmony_ci multi->hw_ptr = hw_ptr; 443d5ac70f0Sopenharmony_ci} 444d5ac70f0Sopenharmony_ci 445d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_hwsync(snd_pcm_t *pcm) 446d5ac70f0Sopenharmony_ci{ 447d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 448d5ac70f0Sopenharmony_ci unsigned int i; 449d5ac70f0Sopenharmony_ci int err; 450d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 451d5ac70f0Sopenharmony_ci err = snd_pcm_hwsync(multi->slaves[i].pcm); 452d5ac70f0Sopenharmony_ci if (err < 0) 453d5ac70f0Sopenharmony_ci return err; 454d5ac70f0Sopenharmony_ci } 455d5ac70f0Sopenharmony_ci snd_pcm_multi_hwptr_update(pcm); 456d5ac70f0Sopenharmony_ci return 0; 457d5ac70f0Sopenharmony_ci} 458d5ac70f0Sopenharmony_ci 459d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) 460d5ac70f0Sopenharmony_ci{ 461d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 462d5ac70f0Sopenharmony_ci snd_pcm_sframes_t d, dr = 0; 463d5ac70f0Sopenharmony_ci unsigned int i; 464d5ac70f0Sopenharmony_ci int err; 465d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 466d5ac70f0Sopenharmony_ci err = snd_pcm_delay(multi->slaves[i].pcm, &d); 467d5ac70f0Sopenharmony_ci if (err < 0) 468d5ac70f0Sopenharmony_ci return err; 469d5ac70f0Sopenharmony_ci if (dr < d) 470d5ac70f0Sopenharmony_ci dr = d; 471d5ac70f0Sopenharmony_ci } 472d5ac70f0Sopenharmony_ci *delayp = dr; 473d5ac70f0Sopenharmony_ci return 0; 474d5ac70f0Sopenharmony_ci} 475d5ac70f0Sopenharmony_ci 476d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm) 477d5ac70f0Sopenharmony_ci{ 478d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 479d5ac70f0Sopenharmony_ci snd_pcm_sframes_t ret = LONG_MAX; 480d5ac70f0Sopenharmony_ci unsigned int i; 481d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 482d5ac70f0Sopenharmony_ci snd_pcm_sframes_t avail; 483d5ac70f0Sopenharmony_ci avail = snd_pcm_avail_update(multi->slaves[i].pcm); 484d5ac70f0Sopenharmony_ci if (avail < 0) 485d5ac70f0Sopenharmony_ci return avail; 486d5ac70f0Sopenharmony_ci if (ret > avail) 487d5ac70f0Sopenharmony_ci ret = avail; 488d5ac70f0Sopenharmony_ci } 489d5ac70f0Sopenharmony_ci snd_pcm_multi_hwptr_update(pcm); 490d5ac70f0Sopenharmony_ci return ret; 491d5ac70f0Sopenharmony_ci} 492d5ac70f0Sopenharmony_ci 493d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail, 494d5ac70f0Sopenharmony_ci snd_htimestamp_t *tstamp) 495d5ac70f0Sopenharmony_ci{ 496d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 497d5ac70f0Sopenharmony_ci snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm; 498d5ac70f0Sopenharmony_ci return snd_pcm_htimestamp(slave, avail, tstamp); 499d5ac70f0Sopenharmony_ci} 500d5ac70f0Sopenharmony_ci 501d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_prepare(snd_pcm_t *pcm) 502d5ac70f0Sopenharmony_ci{ 503d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 504d5ac70f0Sopenharmony_ci int result = 0, err; 505d5ac70f0Sopenharmony_ci unsigned int i; 506d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 507d5ac70f0Sopenharmony_ci /* We call prepare to each slave even if it's linked. 508d5ac70f0Sopenharmony_ci * This is to make sure to sync non-mmaped control/status. 509d5ac70f0Sopenharmony_ci */ 510d5ac70f0Sopenharmony_ci err = snd_pcm_prepare(multi->slaves[i].pcm); 511d5ac70f0Sopenharmony_ci if (err < 0) 512d5ac70f0Sopenharmony_ci result = err; 513d5ac70f0Sopenharmony_ci } 514d5ac70f0Sopenharmony_ci multi->hw_ptr = multi->appl_ptr = 0; 515d5ac70f0Sopenharmony_ci return result; 516d5ac70f0Sopenharmony_ci} 517d5ac70f0Sopenharmony_ci 518d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_reset(snd_pcm_t *pcm) 519d5ac70f0Sopenharmony_ci{ 520d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 521d5ac70f0Sopenharmony_ci int result = 0, err; 522d5ac70f0Sopenharmony_ci unsigned int i; 523d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 524d5ac70f0Sopenharmony_ci /* Reset each slave, as well as in prepare */ 525d5ac70f0Sopenharmony_ci err = snd_pcm_reset(multi->slaves[i].pcm); 526d5ac70f0Sopenharmony_ci if (err < 0) 527d5ac70f0Sopenharmony_ci result = err; 528d5ac70f0Sopenharmony_ci } 529d5ac70f0Sopenharmony_ci multi->hw_ptr = multi->appl_ptr = 0; 530d5ac70f0Sopenharmony_ci return result; 531d5ac70f0Sopenharmony_ci} 532d5ac70f0Sopenharmony_ci 533d5ac70f0Sopenharmony_ci/* when the first slave PCM is linked, it means that the whole multi 534d5ac70f0Sopenharmony_ci * plugin instance is linked manually to another PCM. in this case, 535d5ac70f0Sopenharmony_ci * we need to trigger the master. 536d5ac70f0Sopenharmony_ci */ 537d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_start(snd_pcm_t *pcm) 538d5ac70f0Sopenharmony_ci{ 539d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 540d5ac70f0Sopenharmony_ci int err = 0; 541d5ac70f0Sopenharmony_ci unsigned int i; 542d5ac70f0Sopenharmony_ci if (multi->slaves[0].linked) 543d5ac70f0Sopenharmony_ci return snd_pcm_start(multi->slaves[0].linked); 544d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 545d5ac70f0Sopenharmony_ci if (multi->slaves[i].linked) 546d5ac70f0Sopenharmony_ci continue; 547d5ac70f0Sopenharmony_ci err = snd_pcm_start(multi->slaves[i].pcm); 548d5ac70f0Sopenharmony_ci if (err < 0) 549d5ac70f0Sopenharmony_ci return err; 550d5ac70f0Sopenharmony_ci } 551d5ac70f0Sopenharmony_ci return err; 552d5ac70f0Sopenharmony_ci} 553d5ac70f0Sopenharmony_ci 554d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_drop(snd_pcm_t *pcm) 555d5ac70f0Sopenharmony_ci{ 556d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 557d5ac70f0Sopenharmony_ci int err = 0; 558d5ac70f0Sopenharmony_ci unsigned int i; 559d5ac70f0Sopenharmony_ci if (multi->slaves[0].linked) 560d5ac70f0Sopenharmony_ci return snd_pcm_drop(multi->slaves[0].linked); 561d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 562d5ac70f0Sopenharmony_ci if (multi->slaves[i].linked) 563d5ac70f0Sopenharmony_ci continue; 564d5ac70f0Sopenharmony_ci err = snd_pcm_drop(multi->slaves[i].pcm); 565d5ac70f0Sopenharmony_ci if (err < 0) 566d5ac70f0Sopenharmony_ci return err; 567d5ac70f0Sopenharmony_ci } 568d5ac70f0Sopenharmony_ci return err; 569d5ac70f0Sopenharmony_ci} 570d5ac70f0Sopenharmony_ci 571d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_drain(snd_pcm_t *pcm) 572d5ac70f0Sopenharmony_ci{ 573d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 574d5ac70f0Sopenharmony_ci int err = 0; 575d5ac70f0Sopenharmony_ci unsigned int i; 576d5ac70f0Sopenharmony_ci if (multi->slaves[0].linked) 577d5ac70f0Sopenharmony_ci return snd_pcm_drain(multi->slaves[0].linked); 578d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 579d5ac70f0Sopenharmony_ci if (multi->slaves[i].linked) 580d5ac70f0Sopenharmony_ci continue; 581d5ac70f0Sopenharmony_ci err = snd_pcm_drain(multi->slaves[i].pcm); 582d5ac70f0Sopenharmony_ci if (err < 0) 583d5ac70f0Sopenharmony_ci return err; 584d5ac70f0Sopenharmony_ci } 585d5ac70f0Sopenharmony_ci return err; 586d5ac70f0Sopenharmony_ci} 587d5ac70f0Sopenharmony_ci 588d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable) 589d5ac70f0Sopenharmony_ci{ 590d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 591d5ac70f0Sopenharmony_ci int err = 0; 592d5ac70f0Sopenharmony_ci unsigned int i; 593d5ac70f0Sopenharmony_ci if (multi->slaves[0].linked) 594d5ac70f0Sopenharmony_ci return snd_pcm_pause(multi->slaves[0].linked, enable); 595d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 596d5ac70f0Sopenharmony_ci if (multi->slaves[i].linked) 597d5ac70f0Sopenharmony_ci continue; 598d5ac70f0Sopenharmony_ci err = snd_pcm_pause(multi->slaves[i].pcm, enable); 599d5ac70f0Sopenharmony_ci if (err < 0) 600d5ac70f0Sopenharmony_ci return err; 601d5ac70f0Sopenharmony_ci } 602d5ac70f0Sopenharmony_ci return err; 603d5ac70f0Sopenharmony_ci} 604d5ac70f0Sopenharmony_ci 605d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) 606d5ac70f0Sopenharmony_ci{ 607d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 608d5ac70f0Sopenharmony_ci unsigned int channel = info->channel; 609d5ac70f0Sopenharmony_ci snd_pcm_multi_channel_t *c = &multi->channels[channel]; 610d5ac70f0Sopenharmony_ci int err; 611d5ac70f0Sopenharmony_ci if (c->slave_idx < 0) 612d5ac70f0Sopenharmony_ci return -ENXIO; 613d5ac70f0Sopenharmony_ci info->channel = c->slave_channel; 614d5ac70f0Sopenharmony_ci err = snd_pcm_channel_info(multi->slaves[c->slave_idx].pcm, info); 615d5ac70f0Sopenharmony_ci info->channel = channel; 616d5ac70f0Sopenharmony_ci return err; 617d5ac70f0Sopenharmony_ci} 618d5ac70f0Sopenharmony_ci 619d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_multi_rewindable(snd_pcm_t *pcm) 620d5ac70f0Sopenharmony_ci{ 621d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 622d5ac70f0Sopenharmony_ci unsigned int i; 623d5ac70f0Sopenharmony_ci snd_pcm_sframes_t frames = LONG_MAX; 624d5ac70f0Sopenharmony_ci 625d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 626d5ac70f0Sopenharmony_ci snd_pcm_sframes_t f = snd_pcm_rewindable(multi->slaves[i].pcm); 627d5ac70f0Sopenharmony_ci if (f <= 0) 628d5ac70f0Sopenharmony_ci return f; 629d5ac70f0Sopenharmony_ci if (f < frames) 630d5ac70f0Sopenharmony_ci frames = f; 631d5ac70f0Sopenharmony_ci } 632d5ac70f0Sopenharmony_ci 633d5ac70f0Sopenharmony_ci return frames; 634d5ac70f0Sopenharmony_ci 635d5ac70f0Sopenharmony_ci} 636d5ac70f0Sopenharmony_ci 637d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_multi_forwardable(snd_pcm_t *pcm) 638d5ac70f0Sopenharmony_ci{ 639d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 640d5ac70f0Sopenharmony_ci unsigned int i; 641d5ac70f0Sopenharmony_ci snd_pcm_sframes_t frames = LONG_MAX; 642d5ac70f0Sopenharmony_ci 643d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 644d5ac70f0Sopenharmony_ci snd_pcm_sframes_t f = snd_pcm_forwardable(multi->slaves[i].pcm); 645d5ac70f0Sopenharmony_ci if (f <= 0) 646d5ac70f0Sopenharmony_ci return f; 647d5ac70f0Sopenharmony_ci if (f < frames) 648d5ac70f0Sopenharmony_ci frames = f; 649d5ac70f0Sopenharmony_ci } 650d5ac70f0Sopenharmony_ci 651d5ac70f0Sopenharmony_ci return frames; 652d5ac70f0Sopenharmony_ci 653d5ac70f0Sopenharmony_ci} 654d5ac70f0Sopenharmony_ci 655d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_multi_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 656d5ac70f0Sopenharmony_ci{ 657d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 658d5ac70f0Sopenharmony_ci unsigned int i; 659d5ac70f0Sopenharmony_ci snd_pcm_uframes_t pos[multi->slaves_count]; 660d5ac70f0Sopenharmony_ci memset(pos, 0, sizeof(pos)); 661d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 662d5ac70f0Sopenharmony_ci snd_pcm_t *slave_i = multi->slaves[i].pcm; 663d5ac70f0Sopenharmony_ci snd_pcm_sframes_t f = snd_pcm_rewind(slave_i, frames); 664d5ac70f0Sopenharmony_ci if (f < 0) 665d5ac70f0Sopenharmony_ci return f; 666d5ac70f0Sopenharmony_ci pos[i] = f; 667d5ac70f0Sopenharmony_ci frames = f; 668d5ac70f0Sopenharmony_ci } 669d5ac70f0Sopenharmony_ci /* Realign the pointers */ 670d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 671d5ac70f0Sopenharmony_ci snd_pcm_t *slave_i = multi->slaves[i].pcm; 672d5ac70f0Sopenharmony_ci snd_pcm_uframes_t f = pos[i] - frames; 673d5ac70f0Sopenharmony_ci snd_pcm_sframes_t result; 674d5ac70f0Sopenharmony_ci if (f > 0) { 675d5ac70f0Sopenharmony_ci result = INTERNAL(snd_pcm_forward)(slave_i, f); 676d5ac70f0Sopenharmony_ci if (result < 0) 677d5ac70f0Sopenharmony_ci return result; 678d5ac70f0Sopenharmony_ci if ((snd_pcm_uframes_t)result != f) 679d5ac70f0Sopenharmony_ci return -EIO; 680d5ac70f0Sopenharmony_ci } 681d5ac70f0Sopenharmony_ci } 682d5ac70f0Sopenharmony_ci snd_pcm_mmap_appl_backward(pcm, frames); 683d5ac70f0Sopenharmony_ci return frames; 684d5ac70f0Sopenharmony_ci} 685d5ac70f0Sopenharmony_ci 686d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_multi_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 687d5ac70f0Sopenharmony_ci{ 688d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 689d5ac70f0Sopenharmony_ci unsigned int i; 690d5ac70f0Sopenharmony_ci snd_pcm_uframes_t pos[multi->slaves_count]; 691d5ac70f0Sopenharmony_ci memset(pos, 0, sizeof(pos)); 692d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 693d5ac70f0Sopenharmony_ci snd_pcm_t *slave_i = multi->slaves[i].pcm; 694d5ac70f0Sopenharmony_ci snd_pcm_sframes_t f = INTERNAL(snd_pcm_forward)(slave_i, frames); 695d5ac70f0Sopenharmony_ci if (f < 0) 696d5ac70f0Sopenharmony_ci return f; 697d5ac70f0Sopenharmony_ci pos[i] = f; 698d5ac70f0Sopenharmony_ci frames = f; 699d5ac70f0Sopenharmony_ci } 700d5ac70f0Sopenharmony_ci /* Realign the pointers */ 701d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 702d5ac70f0Sopenharmony_ci snd_pcm_t *slave_i = multi->slaves[i].pcm; 703d5ac70f0Sopenharmony_ci snd_pcm_uframes_t f = pos[i] - frames; 704d5ac70f0Sopenharmony_ci snd_pcm_sframes_t result; 705d5ac70f0Sopenharmony_ci if (f > 0) { 706d5ac70f0Sopenharmony_ci result = snd_pcm_rewind(slave_i, f); 707d5ac70f0Sopenharmony_ci if (result < 0) 708d5ac70f0Sopenharmony_ci return result; 709d5ac70f0Sopenharmony_ci if ((snd_pcm_uframes_t)result != f) 710d5ac70f0Sopenharmony_ci return -EIO; 711d5ac70f0Sopenharmony_ci } 712d5ac70f0Sopenharmony_ci } 713d5ac70f0Sopenharmony_ci snd_pcm_mmap_appl_forward(pcm, frames); 714d5ac70f0Sopenharmony_ci return frames; 715d5ac70f0Sopenharmony_ci} 716d5ac70f0Sopenharmony_ci 717d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_resume(snd_pcm_t *pcm) 718d5ac70f0Sopenharmony_ci{ 719d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 720d5ac70f0Sopenharmony_ci int err = 0; 721d5ac70f0Sopenharmony_ci unsigned int i; 722d5ac70f0Sopenharmony_ci if (multi->slaves[0].linked) 723d5ac70f0Sopenharmony_ci return snd_pcm_resume(multi->slaves[0].linked); 724d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 725d5ac70f0Sopenharmony_ci if (multi->slaves[i].linked) 726d5ac70f0Sopenharmony_ci continue; 727d5ac70f0Sopenharmony_ci err = snd_pcm_resume(multi->slaves[i].pcm); 728d5ac70f0Sopenharmony_ci if (err < 0) 729d5ac70f0Sopenharmony_ci return err; 730d5ac70f0Sopenharmony_ci } 731d5ac70f0Sopenharmony_ci return err; 732d5ac70f0Sopenharmony_ci} 733d5ac70f0Sopenharmony_ci 734d5ac70f0Sopenharmony_ci/* if a multi plugin instance is linked as slaves, every slave PCMs 735d5ac70f0Sopenharmony_ci * including the first one has to be relinked to the given master. 736d5ac70f0Sopenharmony_ci */ 737d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master) 738d5ac70f0Sopenharmony_ci{ 739d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 740d5ac70f0Sopenharmony_ci unsigned int i; 741d5ac70f0Sopenharmony_ci int err; 742d5ac70f0Sopenharmony_ci 743d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 744d5ac70f0Sopenharmony_ci snd_pcm_unlink(multi->slaves[i].pcm); 745d5ac70f0Sopenharmony_ci multi->slaves[i].linked = NULL; 746d5ac70f0Sopenharmony_ci err = snd_pcm_link(master, multi->slaves[i].pcm); 747d5ac70f0Sopenharmony_ci if (err < 0) { 748d5ac70f0Sopenharmony_ci reset_links(multi); 749d5ac70f0Sopenharmony_ci return err; 750d5ac70f0Sopenharmony_ci } 751d5ac70f0Sopenharmony_ci multi->slaves[i].linked = master; 752d5ac70f0Sopenharmony_ci } 753d5ac70f0Sopenharmony_ci return 0; 754d5ac70f0Sopenharmony_ci} 755d5ac70f0Sopenharmony_ci 756d5ac70f0Sopenharmony_ci/* linking to a multi as a master is easy - simply link to the first 757d5ac70f0Sopenharmony_ci * slave element as its own slaves are already linked. 758d5ac70f0Sopenharmony_ci */ 759d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2) 760d5ac70f0Sopenharmony_ci{ 761d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm1->private_data; 762d5ac70f0Sopenharmony_ci snd_pcm_t *main_pcm = multi->slaves[0].pcm; 763d5ac70f0Sopenharmony_ci if (main_pcm->fast_ops->link) 764d5ac70f0Sopenharmony_ci return main_pcm->fast_ops->link(main_pcm->fast_op_arg, pcm2); 765d5ac70f0Sopenharmony_ci return -ENOSYS; 766d5ac70f0Sopenharmony_ci} 767d5ac70f0Sopenharmony_ci 768d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_unlink(snd_pcm_t *pcm) 769d5ac70f0Sopenharmony_ci{ 770d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 771d5ac70f0Sopenharmony_ci unsigned int i; 772d5ac70f0Sopenharmony_ci 773d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 774d5ac70f0Sopenharmony_ci if (multi->slaves[i].linked) 775d5ac70f0Sopenharmony_ci snd_pcm_unlink(multi->slaves[i].linked); 776d5ac70f0Sopenharmony_ci multi->slaves[0].linked = NULL; 777d5ac70f0Sopenharmony_ci } 778d5ac70f0Sopenharmony_ci return 0; 779d5ac70f0Sopenharmony_ci} 780d5ac70f0Sopenharmony_ci 781d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_multi_mmap_commit(snd_pcm_t *pcm, 782d5ac70f0Sopenharmony_ci snd_pcm_uframes_t offset, 783d5ac70f0Sopenharmony_ci snd_pcm_uframes_t size) 784d5ac70f0Sopenharmony_ci{ 785d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 786d5ac70f0Sopenharmony_ci snd_pcm_t *slave; 787d5ac70f0Sopenharmony_ci unsigned int i; 788d5ac70f0Sopenharmony_ci snd_pcm_sframes_t result; 789d5ac70f0Sopenharmony_ci 790d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 791d5ac70f0Sopenharmony_ci slave = multi->slaves[i].pcm; 792d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_commit(slave, offset, size); 793d5ac70f0Sopenharmony_ci if (result < 0) 794d5ac70f0Sopenharmony_ci return result; 795d5ac70f0Sopenharmony_ci if ((snd_pcm_uframes_t)result != size) 796d5ac70f0Sopenharmony_ci return -EIO; 797d5ac70f0Sopenharmony_ci } 798d5ac70f0Sopenharmony_ci snd_pcm_mmap_appl_forward(pcm, size); 799d5ac70f0Sopenharmony_ci return size; 800d5ac70f0Sopenharmony_ci} 801d5ac70f0Sopenharmony_ci 802d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_munmap(snd_pcm_t *pcm) 803d5ac70f0Sopenharmony_ci{ 804d5ac70f0Sopenharmony_ci free(pcm->mmap_channels); 805d5ac70f0Sopenharmony_ci free(pcm->running_areas); 806d5ac70f0Sopenharmony_ci pcm->mmap_channels = NULL; 807d5ac70f0Sopenharmony_ci pcm->running_areas = NULL; 808d5ac70f0Sopenharmony_ci return 0; 809d5ac70f0Sopenharmony_ci} 810d5ac70f0Sopenharmony_ci 811d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_mmap(snd_pcm_t *pcm) 812d5ac70f0Sopenharmony_ci{ 813d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 814d5ac70f0Sopenharmony_ci unsigned int c; 815d5ac70f0Sopenharmony_ci 816d5ac70f0Sopenharmony_ci pcm->mmap_channels = calloc(pcm->channels, 817d5ac70f0Sopenharmony_ci sizeof(pcm->mmap_channels[0])); 818d5ac70f0Sopenharmony_ci pcm->running_areas = calloc(pcm->channels, 819d5ac70f0Sopenharmony_ci sizeof(pcm->running_areas[0])); 820d5ac70f0Sopenharmony_ci if (!pcm->mmap_channels || !pcm->running_areas) { 821d5ac70f0Sopenharmony_ci snd_pcm_multi_munmap(pcm); 822d5ac70f0Sopenharmony_ci return -ENOMEM; 823d5ac70f0Sopenharmony_ci } 824d5ac70f0Sopenharmony_ci 825d5ac70f0Sopenharmony_ci /* Copy the slave mmapped buffer data */ 826d5ac70f0Sopenharmony_ci for (c = 0; c < pcm->channels; c++) { 827d5ac70f0Sopenharmony_ci snd_pcm_multi_channel_t *chan = &multi->channels[c]; 828d5ac70f0Sopenharmony_ci snd_pcm_t *slave; 829d5ac70f0Sopenharmony_ci if (chan->slave_idx < 0) { 830d5ac70f0Sopenharmony_ci snd_pcm_multi_munmap(pcm); 831d5ac70f0Sopenharmony_ci return -ENXIO; 832d5ac70f0Sopenharmony_ci } 833d5ac70f0Sopenharmony_ci slave = multi->slaves[chan->slave_idx].pcm; 834d5ac70f0Sopenharmony_ci pcm->mmap_channels[c] = 835d5ac70f0Sopenharmony_ci slave->mmap_channels[chan->slave_channel]; 836d5ac70f0Sopenharmony_ci pcm->mmap_channels[c].channel = c; 837d5ac70f0Sopenharmony_ci pcm->running_areas[c] = 838d5ac70f0Sopenharmony_ci slave->running_areas[chan->slave_channel]; 839d5ac70f0Sopenharmony_ci } 840d5ac70f0Sopenharmony_ci return 0; 841d5ac70f0Sopenharmony_ci} 842d5ac70f0Sopenharmony_ci 843d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail) 844d5ac70f0Sopenharmony_ci{ 845d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 846d5ac70f0Sopenharmony_ci unsigned int i; 847d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; ++i) { 848d5ac70f0Sopenharmony_ci if (snd_pcm_may_wait_for_avail_min(multi->slaves[i].pcm, avail)) 849d5ac70f0Sopenharmony_ci return 1; 850d5ac70f0Sopenharmony_ci } 851d5ac70f0Sopenharmony_ci return 0; 852d5ac70f0Sopenharmony_ci} 853d5ac70f0Sopenharmony_ci 854d5ac70f0Sopenharmony_cistatic snd_pcm_chmap_query_t **snd_pcm_multi_query_chmaps(snd_pcm_t *pcm) 855d5ac70f0Sopenharmony_ci{ 856d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 857d5ac70f0Sopenharmony_ci snd_pcm_chmap_query_t **slave_maps[multi->slaves_count]; 858d5ac70f0Sopenharmony_ci snd_pcm_chmap_query_t **maps; 859d5ac70f0Sopenharmony_ci unsigned int i; 860d5ac70f0Sopenharmony_ci int err = -ENOMEM; 861d5ac70f0Sopenharmony_ci 862d5ac70f0Sopenharmony_ci memset(slave_maps, 0, sizeof(slave_maps)); 863d5ac70f0Sopenharmony_ci maps = calloc(2, sizeof(*maps)); 864d5ac70f0Sopenharmony_ci if (!maps) 865d5ac70f0Sopenharmony_ci return NULL; 866d5ac70f0Sopenharmony_ci maps[0] = calloc(multi->channels_count + 2, sizeof(int *)); 867d5ac70f0Sopenharmony_ci if (!maps[0]) 868d5ac70f0Sopenharmony_ci goto error; 869d5ac70f0Sopenharmony_ci maps[0]->type = SND_CHMAP_TYPE_FIXED; 870d5ac70f0Sopenharmony_ci maps[0]->map.channels = multi->channels_count; 871d5ac70f0Sopenharmony_ci 872d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; i++) { 873d5ac70f0Sopenharmony_ci slave_maps[i] = snd_pcm_query_chmaps(multi->slaves[i].pcm); 874d5ac70f0Sopenharmony_ci if (!slave_maps[i]) 875d5ac70f0Sopenharmony_ci goto error; 876d5ac70f0Sopenharmony_ci } 877d5ac70f0Sopenharmony_ci 878d5ac70f0Sopenharmony_ci for (i = 0; i < multi->channels_count; i++) { 879d5ac70f0Sopenharmony_ci snd_pcm_multi_channel_t *bind = &multi->channels[i]; 880d5ac70f0Sopenharmony_ci unsigned int slave_channels = 881d5ac70f0Sopenharmony_ci multi->slaves[bind->slave_idx].channels_count; 882d5ac70f0Sopenharmony_ci snd_pcm_chmap_query_t **p; 883d5ac70f0Sopenharmony_ci 884d5ac70f0Sopenharmony_ci for (p = slave_maps[bind->slave_idx]; *p; p++) { 885d5ac70f0Sopenharmony_ci if ((*p)->map.channels == slave_channels) { 886d5ac70f0Sopenharmony_ci maps[0]->map.pos[i] = 887d5ac70f0Sopenharmony_ci (*p)->map.pos[bind->slave_channel]; 888d5ac70f0Sopenharmony_ci break; 889d5ac70f0Sopenharmony_ci } 890d5ac70f0Sopenharmony_ci } 891d5ac70f0Sopenharmony_ci } 892d5ac70f0Sopenharmony_ci err = 0; 893d5ac70f0Sopenharmony_ci 894d5ac70f0Sopenharmony_ci error: 895d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; i++) { 896d5ac70f0Sopenharmony_ci if (slave_maps[i]) 897d5ac70f0Sopenharmony_ci snd_pcm_free_chmaps(slave_maps[i]); 898d5ac70f0Sopenharmony_ci } 899d5ac70f0Sopenharmony_ci 900d5ac70f0Sopenharmony_ci if (err) { 901d5ac70f0Sopenharmony_ci snd_pcm_free_chmaps(maps); 902d5ac70f0Sopenharmony_ci return NULL; 903d5ac70f0Sopenharmony_ci } 904d5ac70f0Sopenharmony_ci 905d5ac70f0Sopenharmony_ci return maps; 906d5ac70f0Sopenharmony_ci} 907d5ac70f0Sopenharmony_ci 908d5ac70f0Sopenharmony_cistatic snd_pcm_chmap_t *snd_pcm_multi_get_chmap(snd_pcm_t *pcm) 909d5ac70f0Sopenharmony_ci{ 910d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 911d5ac70f0Sopenharmony_ci snd_pcm_chmap_t *map; 912d5ac70f0Sopenharmony_ci snd_pcm_chmap_t *slave_maps[multi->slaves_count]; 913d5ac70f0Sopenharmony_ci unsigned int i; 914d5ac70f0Sopenharmony_ci int err = -ENOMEM; 915d5ac70f0Sopenharmony_ci 916d5ac70f0Sopenharmony_ci memset(slave_maps, 0, sizeof(slave_maps)); 917d5ac70f0Sopenharmony_ci map = calloc(multi->channels_count + 1, sizeof(int)); 918d5ac70f0Sopenharmony_ci if (!map) 919d5ac70f0Sopenharmony_ci return NULL; 920d5ac70f0Sopenharmony_ci 921d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; i++) { 922d5ac70f0Sopenharmony_ci slave_maps[i] = snd_pcm_get_chmap(multi->slaves[i].pcm); 923d5ac70f0Sopenharmony_ci if (!slave_maps[i]) 924d5ac70f0Sopenharmony_ci goto error; 925d5ac70f0Sopenharmony_ci } 926d5ac70f0Sopenharmony_ci 927d5ac70f0Sopenharmony_ci map->channels = multi->channels_count; 928d5ac70f0Sopenharmony_ci for (i = 0; i < multi->channels_count; i++) { 929d5ac70f0Sopenharmony_ci snd_pcm_multi_channel_t *bind = &multi->channels[i]; 930d5ac70f0Sopenharmony_ci map->pos[i] = slave_maps[bind->slave_idx]->pos[bind->slave_channel]; 931d5ac70f0Sopenharmony_ci } 932d5ac70f0Sopenharmony_ci err = 0; 933d5ac70f0Sopenharmony_ci 934d5ac70f0Sopenharmony_ci error: 935d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; i++) 936d5ac70f0Sopenharmony_ci free(slave_maps[i]); 937d5ac70f0Sopenharmony_ci 938d5ac70f0Sopenharmony_ci if (err) { 939d5ac70f0Sopenharmony_ci free(map); 940d5ac70f0Sopenharmony_ci return NULL; 941d5ac70f0Sopenharmony_ci } 942d5ac70f0Sopenharmony_ci 943d5ac70f0Sopenharmony_ci return map; 944d5ac70f0Sopenharmony_ci} 945d5ac70f0Sopenharmony_ci 946d5ac70f0Sopenharmony_cistatic int snd_pcm_multi_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) 947d5ac70f0Sopenharmony_ci{ 948d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 949d5ac70f0Sopenharmony_ci snd_pcm_chmap_t *slave_maps[multi->slaves_count]; 950d5ac70f0Sopenharmony_ci unsigned int i; 951d5ac70f0Sopenharmony_ci int err = 0; 952d5ac70f0Sopenharmony_ci 953d5ac70f0Sopenharmony_ci if (map->channels != multi->channels_count) 954d5ac70f0Sopenharmony_ci return -EINVAL; 955d5ac70f0Sopenharmony_ci 956d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; i++) { 957d5ac70f0Sopenharmony_ci slave_maps[i] = calloc(multi->slaves[i].channels_count + 1, 958d5ac70f0Sopenharmony_ci sizeof(int)); 959d5ac70f0Sopenharmony_ci if (!slave_maps[i]) { 960d5ac70f0Sopenharmony_ci for (i++; i < multi->slaves_count; i++) 961d5ac70f0Sopenharmony_ci slave_maps[i] = NULL; 962d5ac70f0Sopenharmony_ci err = -ENOMEM; 963d5ac70f0Sopenharmony_ci goto error; 964d5ac70f0Sopenharmony_ci } 965d5ac70f0Sopenharmony_ci } 966d5ac70f0Sopenharmony_ci 967d5ac70f0Sopenharmony_ci for (i = 0; i < multi->channels_count; i++) { 968d5ac70f0Sopenharmony_ci snd_pcm_multi_channel_t *bind = &multi->channels[i]; 969d5ac70f0Sopenharmony_ci slave_maps[bind->slave_idx]->pos[bind->slave_channel] = 970d5ac70f0Sopenharmony_ci map->pos[i]; 971d5ac70f0Sopenharmony_ci } 972d5ac70f0Sopenharmony_ci 973d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; i++) { 974d5ac70f0Sopenharmony_ci err = snd_pcm_set_chmap(multi->slaves[i].pcm, slave_maps[i]); 975d5ac70f0Sopenharmony_ci if (err < 0) 976d5ac70f0Sopenharmony_ci goto error; 977d5ac70f0Sopenharmony_ci } 978d5ac70f0Sopenharmony_ci 979d5ac70f0Sopenharmony_ci error: 980d5ac70f0Sopenharmony_ci for (i = 0; i < multi->slaves_count; i++) 981d5ac70f0Sopenharmony_ci free(slave_maps[i]); 982d5ac70f0Sopenharmony_ci 983d5ac70f0Sopenharmony_ci return err; 984d5ac70f0Sopenharmony_ci} 985d5ac70f0Sopenharmony_ci 986d5ac70f0Sopenharmony_cistatic void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out) 987d5ac70f0Sopenharmony_ci{ 988d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi = pcm->private_data; 989d5ac70f0Sopenharmony_ci unsigned int k; 990d5ac70f0Sopenharmony_ci snd_output_printf(out, "Multi PCM\n"); 991d5ac70f0Sopenharmony_ci snd_output_printf(out, " Channel bindings:\n"); 992d5ac70f0Sopenharmony_ci for (k = 0; k < multi->channels_count; ++k) { 993d5ac70f0Sopenharmony_ci snd_pcm_multi_channel_t *c = &multi->channels[k]; 994d5ac70f0Sopenharmony_ci if (c->slave_idx < 0) 995d5ac70f0Sopenharmony_ci continue; 996d5ac70f0Sopenharmony_ci snd_output_printf(out, " %d: slave %d, channel %d\n", 997d5ac70f0Sopenharmony_ci k, c->slave_idx, c->slave_channel); 998d5ac70f0Sopenharmony_ci } 999d5ac70f0Sopenharmony_ci if (pcm->setup) { 1000d5ac70f0Sopenharmony_ci snd_output_printf(out, "Its setup is:\n"); 1001d5ac70f0Sopenharmony_ci snd_pcm_dump_setup(pcm, out); 1002d5ac70f0Sopenharmony_ci } 1003d5ac70f0Sopenharmony_ci for (k = 0; k < multi->slaves_count; ++k) { 1004d5ac70f0Sopenharmony_ci snd_output_printf(out, "Slave #%d: ", k); 1005d5ac70f0Sopenharmony_ci snd_pcm_dump(multi->slaves[k].pcm, out); 1006d5ac70f0Sopenharmony_ci } 1007d5ac70f0Sopenharmony_ci} 1008d5ac70f0Sopenharmony_ci 1009d5ac70f0Sopenharmony_cistatic const snd_pcm_ops_t snd_pcm_multi_ops = { 1010d5ac70f0Sopenharmony_ci .close = snd_pcm_multi_close, 1011d5ac70f0Sopenharmony_ci .info = snd_pcm_multi_info, 1012d5ac70f0Sopenharmony_ci .hw_refine = snd_pcm_multi_hw_refine, 1013d5ac70f0Sopenharmony_ci .hw_params = snd_pcm_multi_hw_params, 1014d5ac70f0Sopenharmony_ci .hw_free = snd_pcm_multi_hw_free, 1015d5ac70f0Sopenharmony_ci .sw_params = snd_pcm_multi_sw_params, 1016d5ac70f0Sopenharmony_ci .channel_info = snd_pcm_multi_channel_info, 1017d5ac70f0Sopenharmony_ci .dump = snd_pcm_multi_dump, 1018d5ac70f0Sopenharmony_ci .nonblock = snd_pcm_multi_nonblock, 1019d5ac70f0Sopenharmony_ci .async = snd_pcm_multi_async, 1020d5ac70f0Sopenharmony_ci .mmap = snd_pcm_multi_mmap, 1021d5ac70f0Sopenharmony_ci .munmap = snd_pcm_multi_munmap, 1022d5ac70f0Sopenharmony_ci .query_chmaps = snd_pcm_multi_query_chmaps, 1023d5ac70f0Sopenharmony_ci .get_chmap = snd_pcm_multi_get_chmap, 1024d5ac70f0Sopenharmony_ci .set_chmap = snd_pcm_multi_set_chmap, 1025d5ac70f0Sopenharmony_ci}; 1026d5ac70f0Sopenharmony_ci 1027d5ac70f0Sopenharmony_cistatic const snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = { 1028d5ac70f0Sopenharmony_ci .status = snd_pcm_multi_status, 1029d5ac70f0Sopenharmony_ci .state = snd_pcm_multi_state, 1030d5ac70f0Sopenharmony_ci .hwsync = snd_pcm_multi_hwsync, 1031d5ac70f0Sopenharmony_ci .delay = snd_pcm_multi_delay, 1032d5ac70f0Sopenharmony_ci .prepare = snd_pcm_multi_prepare, 1033d5ac70f0Sopenharmony_ci .reset = snd_pcm_multi_reset, 1034d5ac70f0Sopenharmony_ci .start = snd_pcm_multi_start, 1035d5ac70f0Sopenharmony_ci .drop = snd_pcm_multi_drop, 1036d5ac70f0Sopenharmony_ci .drain = snd_pcm_multi_drain, 1037d5ac70f0Sopenharmony_ci .pause = snd_pcm_multi_pause, 1038d5ac70f0Sopenharmony_ci .writei = snd_pcm_mmap_writei, 1039d5ac70f0Sopenharmony_ci .writen = snd_pcm_mmap_writen, 1040d5ac70f0Sopenharmony_ci .readi = snd_pcm_mmap_readi, 1041d5ac70f0Sopenharmony_ci .readn = snd_pcm_mmap_readn, 1042d5ac70f0Sopenharmony_ci .rewindable = snd_pcm_multi_rewindable, 1043d5ac70f0Sopenharmony_ci .rewind = snd_pcm_multi_rewind, 1044d5ac70f0Sopenharmony_ci .forwardable = snd_pcm_multi_forwardable, 1045d5ac70f0Sopenharmony_ci .forward = snd_pcm_multi_forward, 1046d5ac70f0Sopenharmony_ci .resume = snd_pcm_multi_resume, 1047d5ac70f0Sopenharmony_ci .link = snd_pcm_multi_link, 1048d5ac70f0Sopenharmony_ci .link_slaves = snd_pcm_multi_link_slaves, 1049d5ac70f0Sopenharmony_ci .unlink = snd_pcm_multi_unlink, 1050d5ac70f0Sopenharmony_ci .avail_update = snd_pcm_multi_avail_update, 1051d5ac70f0Sopenharmony_ci .mmap_commit = snd_pcm_multi_mmap_commit, 1052d5ac70f0Sopenharmony_ci .htimestamp = snd_pcm_multi_htimestamp, 1053d5ac70f0Sopenharmony_ci .poll_descriptors_count = snd_pcm_multi_poll_descriptors_count, 1054d5ac70f0Sopenharmony_ci .poll_descriptors = snd_pcm_multi_poll_descriptors, 1055d5ac70f0Sopenharmony_ci .poll_revents = snd_pcm_multi_poll_revents, 1056d5ac70f0Sopenharmony_ci .may_wait_for_avail_min = snd_pcm_multi_may_wait_for_avail_min, 1057d5ac70f0Sopenharmony_ci}; 1058d5ac70f0Sopenharmony_ci 1059d5ac70f0Sopenharmony_ci/** 1060d5ac70f0Sopenharmony_ci * \brief Creates a new Multi PCM 1061d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle 1062d5ac70f0Sopenharmony_ci * \param name Name of PCM 1063d5ac70f0Sopenharmony_ci * \param slaves_count Count of slaves 1064d5ac70f0Sopenharmony_ci * \param master_slave Master slave number 1065d5ac70f0Sopenharmony_ci * \param slaves_pcm Array with slave PCMs 1066d5ac70f0Sopenharmony_ci * \param schannels_count Array with slave channel counts 1067d5ac70f0Sopenharmony_ci * \param channels_count Count of channels 1068d5ac70f0Sopenharmony_ci * \param sidxs Array with channels indexes to slaves 1069d5ac70f0Sopenharmony_ci * \param schannels Array with slave channels 1070d5ac70f0Sopenharmony_ci * \param close_slaves When set, the slave PCM handle is closed 1071d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code 1072d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense 1073d5ac70f0Sopenharmony_ci * of compatibility reasons. The prototype might be freely 1074d5ac70f0Sopenharmony_ci * changed in future. 1075d5ac70f0Sopenharmony_ci */ 1076d5ac70f0Sopenharmony_ciint snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, 1077d5ac70f0Sopenharmony_ci unsigned int slaves_count, unsigned int master_slave, 1078d5ac70f0Sopenharmony_ci snd_pcm_t **slaves_pcm, unsigned int *schannels_count, 1079d5ac70f0Sopenharmony_ci unsigned int channels_count, 1080d5ac70f0Sopenharmony_ci int *sidxs, unsigned int *schannels, 1081d5ac70f0Sopenharmony_ci int close_slaves) 1082d5ac70f0Sopenharmony_ci{ 1083d5ac70f0Sopenharmony_ci snd_pcm_t *pcm; 1084d5ac70f0Sopenharmony_ci snd_pcm_multi_t *multi; 1085d5ac70f0Sopenharmony_ci unsigned int i; 1086d5ac70f0Sopenharmony_ci snd_pcm_stream_t stream; 1087d5ac70f0Sopenharmony_ci int err; 1088d5ac70f0Sopenharmony_ci 1089d5ac70f0Sopenharmony_ci assert(pcmp); 1090d5ac70f0Sopenharmony_ci assert(slaves_count > 0 && slaves_pcm && schannels_count); 1091d5ac70f0Sopenharmony_ci assert(channels_count > 0 && sidxs && schannels); 1092d5ac70f0Sopenharmony_ci assert(master_slave < slaves_count); 1093d5ac70f0Sopenharmony_ci 1094d5ac70f0Sopenharmony_ci multi = calloc(1, sizeof(snd_pcm_multi_t)); 1095d5ac70f0Sopenharmony_ci if (!multi) { 1096d5ac70f0Sopenharmony_ci return -ENOMEM; 1097d5ac70f0Sopenharmony_ci } 1098d5ac70f0Sopenharmony_ci 1099d5ac70f0Sopenharmony_ci stream = slaves_pcm[0]->stream; 1100d5ac70f0Sopenharmony_ci 1101d5ac70f0Sopenharmony_ci multi->slaves_count = slaves_count; 1102d5ac70f0Sopenharmony_ci multi->master_slave = master_slave; 1103d5ac70f0Sopenharmony_ci multi->slaves = calloc(slaves_count, sizeof(*multi->slaves)); 1104d5ac70f0Sopenharmony_ci if (!multi->slaves) { 1105d5ac70f0Sopenharmony_ci free(multi); 1106d5ac70f0Sopenharmony_ci return -ENOMEM; 1107d5ac70f0Sopenharmony_ci } 1108d5ac70f0Sopenharmony_ci multi->channels_count = channels_count; 1109d5ac70f0Sopenharmony_ci multi->channels = calloc(channels_count, sizeof(*multi->channels)); 1110d5ac70f0Sopenharmony_ci if (!multi->channels) { 1111d5ac70f0Sopenharmony_ci free(multi->slaves); 1112d5ac70f0Sopenharmony_ci free(multi); 1113d5ac70f0Sopenharmony_ci return -ENOMEM; 1114d5ac70f0Sopenharmony_ci } 1115d5ac70f0Sopenharmony_ci for (i = 0; i < slaves_count; ++i) { 1116d5ac70f0Sopenharmony_ci snd_pcm_multi_slave_t *slave = &multi->slaves[i]; 1117d5ac70f0Sopenharmony_ci assert(slaves_pcm[i]->stream == stream); 1118d5ac70f0Sopenharmony_ci slave->pcm = slaves_pcm[i]; 1119d5ac70f0Sopenharmony_ci slave->channels_count = schannels_count[i]; 1120d5ac70f0Sopenharmony_ci slave->close_slave = close_slaves; 1121d5ac70f0Sopenharmony_ci } 1122d5ac70f0Sopenharmony_ci for (i = 0; i < channels_count; ++i) { 1123d5ac70f0Sopenharmony_ci snd_pcm_multi_channel_t *bind = &multi->channels[i]; 1124d5ac70f0Sopenharmony_ci assert(sidxs[i] < (int)slaves_count); 1125d5ac70f0Sopenharmony_ci assert(schannels[i] < schannels_count[sidxs[i]]); 1126d5ac70f0Sopenharmony_ci bind->slave_idx = sidxs[i]; 1127d5ac70f0Sopenharmony_ci bind->slave_channel = schannels[i]; 1128d5ac70f0Sopenharmony_ci if (sidxs[i] < 0) 1129d5ac70f0Sopenharmony_ci continue; 1130d5ac70f0Sopenharmony_ci } 1131d5ac70f0Sopenharmony_ci multi->channels_count = channels_count; 1132d5ac70f0Sopenharmony_ci 1133d5ac70f0Sopenharmony_ci err = snd_pcm_new(&pcm, SND_PCM_TYPE_MULTI, name, stream, 1134d5ac70f0Sopenharmony_ci multi->slaves[0].pcm->mode); 1135d5ac70f0Sopenharmony_ci if (err < 0) { 1136d5ac70f0Sopenharmony_ci free(multi->slaves); 1137d5ac70f0Sopenharmony_ci free(multi->channels); 1138d5ac70f0Sopenharmony_ci free(multi); 1139d5ac70f0Sopenharmony_ci return err; 1140d5ac70f0Sopenharmony_ci } 1141d5ac70f0Sopenharmony_ci pcm->mmap_rw = 1; 1142d5ac70f0Sopenharmony_ci pcm->mmap_shadow = 1; /* has own mmap method */ 1143d5ac70f0Sopenharmony_ci pcm->ops = &snd_pcm_multi_ops; 1144d5ac70f0Sopenharmony_ci pcm->fast_ops = &snd_pcm_multi_fast_ops; 1145d5ac70f0Sopenharmony_ci pcm->private_data = multi; 1146d5ac70f0Sopenharmony_ci pcm->poll_fd = multi->slaves[master_slave].pcm->poll_fd; 1147d5ac70f0Sopenharmony_ci pcm->poll_events = multi->slaves[master_slave].pcm->poll_events; 1148d5ac70f0Sopenharmony_ci pcm->tstamp_type = multi->slaves[master_slave].pcm->tstamp_type; 1149d5ac70f0Sopenharmony_ci snd_pcm_set_hw_ptr(pcm, &multi->hw_ptr, -1, 0); 1150d5ac70f0Sopenharmony_ci snd_pcm_set_appl_ptr(pcm, &multi->appl_ptr, -1, 0); 1151d5ac70f0Sopenharmony_ci *pcmp = pcm; 1152d5ac70f0Sopenharmony_ci return 0; 1153d5ac70f0Sopenharmony_ci} 1154d5ac70f0Sopenharmony_ci 1155d5ac70f0Sopenharmony_ci/*! \page pcm_plugins 1156d5ac70f0Sopenharmony_ci 1157d5ac70f0Sopenharmony_ci\section pcm_plugins_multi Plugin: Multiple streams to One 1158d5ac70f0Sopenharmony_ci 1159d5ac70f0Sopenharmony_ciThis plugin converts multiple streams to one. 1160d5ac70f0Sopenharmony_ci 1161d5ac70f0Sopenharmony_ci\code 1162d5ac70f0Sopenharmony_cipcm.name { 1163d5ac70f0Sopenharmony_ci type multi # Multiple streams conversion PCM 1164d5ac70f0Sopenharmony_ci slaves { # Slaves definition 1165d5ac70f0Sopenharmony_ci ID STR # Slave PCM name 1166d5ac70f0Sopenharmony_ci # or 1167d5ac70f0Sopenharmony_ci ID { 1168d5ac70f0Sopenharmony_ci pcm STR # Slave PCM name 1169d5ac70f0Sopenharmony_ci # or 1170d5ac70f0Sopenharmony_ci pcm { } # Slave PCM definition 1171d5ac70f0Sopenharmony_ci channels INT # Slave channels 1172d5ac70f0Sopenharmony_ci } 1173d5ac70f0Sopenharmony_ci } 1174d5ac70f0Sopenharmony_ci bindings { # Bindings table 1175d5ac70f0Sopenharmony_ci N { 1176d5ac70f0Sopenharmony_ci slave STR # Slave key 1177d5ac70f0Sopenharmony_ci channel INT # Slave channel 1178d5ac70f0Sopenharmony_ci } 1179d5ac70f0Sopenharmony_ci } 1180d5ac70f0Sopenharmony_ci [master INT] # Define the master slave 1181d5ac70f0Sopenharmony_ci} 1182d5ac70f0Sopenharmony_ci\endcode 1183d5ac70f0Sopenharmony_ci 1184d5ac70f0Sopenharmony_ciFor example, to bind two PCM streams with two-channel stereo (hw:0,0 and 1185d5ac70f0Sopenharmony_cihw:0,1) as one 4-channel stereo PCM stream, define like this: 1186d5ac70f0Sopenharmony_ci\code 1187d5ac70f0Sopenharmony_cipcm.quad { 1188d5ac70f0Sopenharmony_ci type multi 1189d5ac70f0Sopenharmony_ci 1190d5ac70f0Sopenharmony_ci slaves.a.pcm "hw:0,0" 1191d5ac70f0Sopenharmony_ci slaves.a.channels 2 1192d5ac70f0Sopenharmony_ci slaves.b.pcm "hw:0,1" 1193d5ac70f0Sopenharmony_ci slaves.b.channels 2 1194d5ac70f0Sopenharmony_ci 1195d5ac70f0Sopenharmony_ci bindings.0.slave a 1196d5ac70f0Sopenharmony_ci bindings.0.channel 0 1197d5ac70f0Sopenharmony_ci bindings.1.slave a 1198d5ac70f0Sopenharmony_ci bindings.1.channel 1 1199d5ac70f0Sopenharmony_ci bindings.2.slave b 1200d5ac70f0Sopenharmony_ci bindings.2.channel 0 1201d5ac70f0Sopenharmony_ci bindings.3.slave b 1202d5ac70f0Sopenharmony_ci bindings.3.channel 1 1203d5ac70f0Sopenharmony_ci} 1204d5ac70f0Sopenharmony_ci\endcode 1205d5ac70f0Sopenharmony_ciNote that the resultant pcm "quad" is not in the interleaved format 1206d5ac70f0Sopenharmony_cibut in the "complex" format. Hence, it's not accessible by applications 1207d5ac70f0Sopenharmony_ciwhich can handle only the interleaved (or the non-interleaved) format. 1208d5ac70f0Sopenharmony_ciIn such a case, wrap this PCM with \ref pcm_plugins_route "route" or 1209d5ac70f0Sopenharmony_ci\ref pcm_plugins_plug "plug" plugin. 1210d5ac70f0Sopenharmony_ci\code 1211d5ac70f0Sopenharmony_cipcm.quad2 { 1212d5ac70f0Sopenharmony_ci type route 1213d5ac70f0Sopenharmony_ci slave.pcm "quad" 1214d5ac70f0Sopenharmony_ci ttable.0.0 1 1215d5ac70f0Sopenharmony_ci ttable.1.1 1 1216d5ac70f0Sopenharmony_ci ttable.2.2 1 1217d5ac70f0Sopenharmony_ci ttable.3.3 1 1218d5ac70f0Sopenharmony_ci} 1219d5ac70f0Sopenharmony_ci\endcode 1220d5ac70f0Sopenharmony_ci 1221d5ac70f0Sopenharmony_ci\subsection pcm_plugins_multi_funcref Function reference 1222d5ac70f0Sopenharmony_ci 1223d5ac70f0Sopenharmony_ci<UL> 1224d5ac70f0Sopenharmony_ci <LI>snd_pcm_multi_open() 1225d5ac70f0Sopenharmony_ci <LI>_snd_pcm_multi_open() 1226d5ac70f0Sopenharmony_ci</UL> 1227d5ac70f0Sopenharmony_ci 1228d5ac70f0Sopenharmony_ci*/ 1229d5ac70f0Sopenharmony_ci 1230d5ac70f0Sopenharmony_ci/** 1231d5ac70f0Sopenharmony_ci * \brief Creates a new Multi PCM 1232d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle 1233d5ac70f0Sopenharmony_ci * \param name Name of PCM 1234d5ac70f0Sopenharmony_ci * \param root Root configuration node 1235d5ac70f0Sopenharmony_ci * \param conf Configuration node with Multi PCM description 1236d5ac70f0Sopenharmony_ci * \param stream Stream type 1237d5ac70f0Sopenharmony_ci * \param mode Stream mode 1238d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code 1239d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense 1240d5ac70f0Sopenharmony_ci * of compatibility reasons. The prototype might be freely 1241d5ac70f0Sopenharmony_ci * changed in future. 1242d5ac70f0Sopenharmony_ci */ 1243d5ac70f0Sopenharmony_ciint _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, 1244d5ac70f0Sopenharmony_ci snd_config_t *root, snd_config_t *conf, 1245d5ac70f0Sopenharmony_ci snd_pcm_stream_t stream, int mode) 1246d5ac70f0Sopenharmony_ci{ 1247d5ac70f0Sopenharmony_ci snd_config_iterator_t i, inext, j, jnext; 1248d5ac70f0Sopenharmony_ci snd_config_t *slaves = NULL; 1249d5ac70f0Sopenharmony_ci snd_config_t *bindings = NULL; 1250d5ac70f0Sopenharmony_ci int err; 1251d5ac70f0Sopenharmony_ci unsigned int idx; 1252d5ac70f0Sopenharmony_ci const char **slaves_id = NULL; 1253d5ac70f0Sopenharmony_ci snd_config_t **slaves_conf = NULL; 1254d5ac70f0Sopenharmony_ci snd_pcm_t **slaves_pcm = NULL; 1255d5ac70f0Sopenharmony_ci unsigned int *slaves_channels = NULL; 1256d5ac70f0Sopenharmony_ci int *channels_sidx = NULL; 1257d5ac70f0Sopenharmony_ci unsigned int *channels_schannel = NULL; 1258d5ac70f0Sopenharmony_ci unsigned int slaves_count = 0; 1259d5ac70f0Sopenharmony_ci long master_slave = 0; 1260d5ac70f0Sopenharmony_ci unsigned int channels_count = 0; 1261d5ac70f0Sopenharmony_ci snd_config_for_each(i, inext, conf) { 1262d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 1263d5ac70f0Sopenharmony_ci const char *id; 1264d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 1265d5ac70f0Sopenharmony_ci continue; 1266d5ac70f0Sopenharmony_ci if (snd_pcm_conf_generic_id(id)) 1267d5ac70f0Sopenharmony_ci continue; 1268d5ac70f0Sopenharmony_ci if (strcmp(id, "slaves") == 0) { 1269d5ac70f0Sopenharmony_ci if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { 1270d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 1271d5ac70f0Sopenharmony_ci return -EINVAL; 1272d5ac70f0Sopenharmony_ci } 1273d5ac70f0Sopenharmony_ci slaves = n; 1274d5ac70f0Sopenharmony_ci continue; 1275d5ac70f0Sopenharmony_ci } 1276d5ac70f0Sopenharmony_ci if (strcmp(id, "bindings") == 0) { 1277d5ac70f0Sopenharmony_ci if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { 1278d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 1279d5ac70f0Sopenharmony_ci return -EINVAL; 1280d5ac70f0Sopenharmony_ci } 1281d5ac70f0Sopenharmony_ci bindings = n; 1282d5ac70f0Sopenharmony_ci continue; 1283d5ac70f0Sopenharmony_ci } 1284d5ac70f0Sopenharmony_ci if (strcmp(id, "master") == 0) { 1285d5ac70f0Sopenharmony_ci if (snd_config_get_integer(n, &master_slave) < 0) { 1286d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 1287d5ac70f0Sopenharmony_ci return -EINVAL; 1288d5ac70f0Sopenharmony_ci } 1289d5ac70f0Sopenharmony_ci continue; 1290d5ac70f0Sopenharmony_ci } 1291d5ac70f0Sopenharmony_ci SNDERR("Unknown field %s", id); 1292d5ac70f0Sopenharmony_ci return -EINVAL; 1293d5ac70f0Sopenharmony_ci } 1294d5ac70f0Sopenharmony_ci if (!slaves) { 1295d5ac70f0Sopenharmony_ci SNDERR("slaves is not defined"); 1296d5ac70f0Sopenharmony_ci return -EINVAL; 1297d5ac70f0Sopenharmony_ci } 1298d5ac70f0Sopenharmony_ci if (!bindings) { 1299d5ac70f0Sopenharmony_ci SNDERR("bindings is not defined"); 1300d5ac70f0Sopenharmony_ci return -EINVAL; 1301d5ac70f0Sopenharmony_ci } 1302d5ac70f0Sopenharmony_ci snd_config_for_each(i, inext, slaves) { 1303d5ac70f0Sopenharmony_ci ++slaves_count; 1304d5ac70f0Sopenharmony_ci } 1305d5ac70f0Sopenharmony_ci if (master_slave < 0 || master_slave >= (long)slaves_count) { 1306d5ac70f0Sopenharmony_ci SNDERR("Master slave is out of range (0-%u)", slaves_count-1); 1307d5ac70f0Sopenharmony_ci return -EINVAL; 1308d5ac70f0Sopenharmony_ci } 1309d5ac70f0Sopenharmony_ci snd_config_for_each(i, inext, bindings) { 1310d5ac70f0Sopenharmony_ci long cchannel; 1311d5ac70f0Sopenharmony_ci snd_config_t *m = snd_config_iterator_entry(i); 1312d5ac70f0Sopenharmony_ci const char *id; 1313d5ac70f0Sopenharmony_ci if (snd_config_get_id(m, &id) < 0) 1314d5ac70f0Sopenharmony_ci continue; 1315d5ac70f0Sopenharmony_ci err = safe_strtol(id, &cchannel); 1316d5ac70f0Sopenharmony_ci if (err < 0 || cchannel < 0) { 1317d5ac70f0Sopenharmony_ci SNDERR("Invalid channel number: %s", id); 1318d5ac70f0Sopenharmony_ci return -EINVAL; 1319d5ac70f0Sopenharmony_ci } 1320d5ac70f0Sopenharmony_ci if ((unsigned long)cchannel >= channels_count) 1321d5ac70f0Sopenharmony_ci channels_count = cchannel + 1; 1322d5ac70f0Sopenharmony_ci } 1323d5ac70f0Sopenharmony_ci if (channels_count == 0) { 1324d5ac70f0Sopenharmony_ci SNDERR("No channels defined"); 1325d5ac70f0Sopenharmony_ci return -EINVAL; 1326d5ac70f0Sopenharmony_ci } 1327d5ac70f0Sopenharmony_ci slaves_id = calloc(slaves_count, sizeof(*slaves_id)); 1328d5ac70f0Sopenharmony_ci slaves_conf = calloc(slaves_count, sizeof(*slaves_conf)); 1329d5ac70f0Sopenharmony_ci slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm)); 1330d5ac70f0Sopenharmony_ci slaves_channels = calloc(slaves_count, sizeof(*slaves_channels)); 1331d5ac70f0Sopenharmony_ci channels_sidx = calloc(channels_count, sizeof(*channels_sidx)); 1332d5ac70f0Sopenharmony_ci channels_schannel = calloc(channels_count, sizeof(*channels_schannel)); 1333d5ac70f0Sopenharmony_ci if (!slaves_id || !slaves_conf || !slaves_pcm || !slaves_channels || 1334d5ac70f0Sopenharmony_ci !channels_sidx || !channels_schannel) { 1335d5ac70f0Sopenharmony_ci err = -ENOMEM; 1336d5ac70f0Sopenharmony_ci goto _free; 1337d5ac70f0Sopenharmony_ci } 1338d5ac70f0Sopenharmony_ci for (idx = 0; idx < channels_count; ++idx) 1339d5ac70f0Sopenharmony_ci channels_sidx[idx] = -1; 1340d5ac70f0Sopenharmony_ci idx = 0; 1341d5ac70f0Sopenharmony_ci snd_config_for_each(i, inext, slaves) { 1342d5ac70f0Sopenharmony_ci snd_config_t *m = snd_config_iterator_entry(i); 1343d5ac70f0Sopenharmony_ci const char *id; 1344d5ac70f0Sopenharmony_ci int channels; 1345d5ac70f0Sopenharmony_ci if (snd_config_get_id(m, &id) < 0) 1346d5ac70f0Sopenharmony_ci continue; 1347d5ac70f0Sopenharmony_ci slaves_id[idx] = id; 1348d5ac70f0Sopenharmony_ci err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1, 1349d5ac70f0Sopenharmony_ci SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels); 1350d5ac70f0Sopenharmony_ci if (err < 0) 1351d5ac70f0Sopenharmony_ci goto _free; 1352d5ac70f0Sopenharmony_ci slaves_channels[idx] = channels; 1353d5ac70f0Sopenharmony_ci ++idx; 1354d5ac70f0Sopenharmony_ci } 1355d5ac70f0Sopenharmony_ci 1356d5ac70f0Sopenharmony_ci snd_config_for_each(i, inext, bindings) { 1357d5ac70f0Sopenharmony_ci snd_config_t *m = snd_config_iterator_entry(i); 1358d5ac70f0Sopenharmony_ci long cchannel = -1; 1359d5ac70f0Sopenharmony_ci long schannel = -1; 1360d5ac70f0Sopenharmony_ci int slave = -1; 1361d5ac70f0Sopenharmony_ci long val; 1362d5ac70f0Sopenharmony_ci const char *str; 1363d5ac70f0Sopenharmony_ci const char *id; 1364d5ac70f0Sopenharmony_ci if (snd_config_get_id(m, &id) < 0) 1365d5ac70f0Sopenharmony_ci continue; 1366d5ac70f0Sopenharmony_ci err = safe_strtol(id, &cchannel); 1367d5ac70f0Sopenharmony_ci if (err < 0 || cchannel < 0) { 1368d5ac70f0Sopenharmony_ci SNDERR("Invalid channel number: %s", id); 1369d5ac70f0Sopenharmony_ci err = -EINVAL; 1370d5ac70f0Sopenharmony_ci goto _free; 1371d5ac70f0Sopenharmony_ci } 1372d5ac70f0Sopenharmony_ci snd_config_for_each(j, jnext, m) { 1373d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(j); 1374d5ac70f0Sopenharmony_ci const char *id; 1375d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 1376d5ac70f0Sopenharmony_ci continue; 1377d5ac70f0Sopenharmony_ci if (strcmp(id, "comment") == 0) 1378d5ac70f0Sopenharmony_ci continue; 1379d5ac70f0Sopenharmony_ci if (strcmp(id, "slave") == 0) { 1380d5ac70f0Sopenharmony_ci char buf[32]; 1381d5ac70f0Sopenharmony_ci unsigned int k; 1382d5ac70f0Sopenharmony_ci err = snd_config_get_string(n, &str); 1383d5ac70f0Sopenharmony_ci if (err < 0) { 1384d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &val); 1385d5ac70f0Sopenharmony_ci if (err < 0) { 1386d5ac70f0Sopenharmony_ci SNDERR("Invalid value for %s", id); 1387d5ac70f0Sopenharmony_ci goto _free; 1388d5ac70f0Sopenharmony_ci } 1389d5ac70f0Sopenharmony_ci sprintf(buf, "%ld", val); 1390d5ac70f0Sopenharmony_ci str = buf; 1391d5ac70f0Sopenharmony_ci } 1392d5ac70f0Sopenharmony_ci for (k = 0; k < slaves_count; ++k) { 1393d5ac70f0Sopenharmony_ci if (strcmp(slaves_id[k], str) == 0) 1394d5ac70f0Sopenharmony_ci slave = k; 1395d5ac70f0Sopenharmony_ci } 1396d5ac70f0Sopenharmony_ci continue; 1397d5ac70f0Sopenharmony_ci } 1398d5ac70f0Sopenharmony_ci if (strcmp(id, "channel") == 0) { 1399d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &schannel); 1400d5ac70f0Sopenharmony_ci if (err < 0) { 1401d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 1402d5ac70f0Sopenharmony_ci goto _free; 1403d5ac70f0Sopenharmony_ci } 1404d5ac70f0Sopenharmony_ci continue; 1405d5ac70f0Sopenharmony_ci } 1406d5ac70f0Sopenharmony_ci SNDERR("Unknown field %s", id); 1407d5ac70f0Sopenharmony_ci err = -EINVAL; 1408d5ac70f0Sopenharmony_ci goto _free; 1409d5ac70f0Sopenharmony_ci } 1410d5ac70f0Sopenharmony_ci if (slave < 0 || (unsigned int)slave >= slaves_count) { 1411d5ac70f0Sopenharmony_ci SNDERR("Invalid or missing sidx for channel %s", id); 1412d5ac70f0Sopenharmony_ci err = -EINVAL; 1413d5ac70f0Sopenharmony_ci goto _free; 1414d5ac70f0Sopenharmony_ci } 1415d5ac70f0Sopenharmony_ci if (schannel < 0 || 1416d5ac70f0Sopenharmony_ci (unsigned int) schannel >= slaves_channels[slave]) { 1417d5ac70f0Sopenharmony_ci SNDERR("Invalid or missing schannel for channel %s", id); 1418d5ac70f0Sopenharmony_ci err = -EINVAL; 1419d5ac70f0Sopenharmony_ci goto _free; 1420d5ac70f0Sopenharmony_ci } 1421d5ac70f0Sopenharmony_ci channels_sidx[cchannel] = slave; 1422d5ac70f0Sopenharmony_ci channels_schannel[cchannel] = schannel; 1423d5ac70f0Sopenharmony_ci } 1424d5ac70f0Sopenharmony_ci 1425d5ac70f0Sopenharmony_ci for (idx = 0; idx < slaves_count; ++idx) { 1426d5ac70f0Sopenharmony_ci err = snd_pcm_open_slave(&slaves_pcm[idx], root, 1427d5ac70f0Sopenharmony_ci slaves_conf[idx], stream, mode, 1428d5ac70f0Sopenharmony_ci conf); 1429d5ac70f0Sopenharmony_ci if (err < 0) 1430d5ac70f0Sopenharmony_ci goto _free; 1431d5ac70f0Sopenharmony_ci snd_config_delete(slaves_conf[idx]); 1432d5ac70f0Sopenharmony_ci slaves_conf[idx] = NULL; 1433d5ac70f0Sopenharmony_ci } 1434d5ac70f0Sopenharmony_ci err = snd_pcm_multi_open(pcmp, name, slaves_count, master_slave, 1435d5ac70f0Sopenharmony_ci slaves_pcm, slaves_channels, 1436d5ac70f0Sopenharmony_ci channels_count, 1437d5ac70f0Sopenharmony_ci channels_sidx, channels_schannel, 1438d5ac70f0Sopenharmony_ci 1); 1439d5ac70f0Sopenharmony_ci_free: 1440d5ac70f0Sopenharmony_ci if (err < 0) { 1441d5ac70f0Sopenharmony_ci for (idx = 0; idx < slaves_count; ++idx) { 1442d5ac70f0Sopenharmony_ci if (slaves_pcm[idx]) 1443d5ac70f0Sopenharmony_ci snd_pcm_close(slaves_pcm[idx]); 1444d5ac70f0Sopenharmony_ci } 1445d5ac70f0Sopenharmony_ci } 1446d5ac70f0Sopenharmony_ci if (slaves_conf) { 1447d5ac70f0Sopenharmony_ci for (idx = 0; idx < slaves_count; ++idx) { 1448d5ac70f0Sopenharmony_ci if (slaves_conf[idx]) 1449d5ac70f0Sopenharmony_ci snd_config_delete(slaves_conf[idx]); 1450d5ac70f0Sopenharmony_ci } 1451d5ac70f0Sopenharmony_ci free(slaves_conf); 1452d5ac70f0Sopenharmony_ci } 1453d5ac70f0Sopenharmony_ci free(slaves_pcm); 1454d5ac70f0Sopenharmony_ci free(slaves_channels); 1455d5ac70f0Sopenharmony_ci free(channels_sidx); 1456d5ac70f0Sopenharmony_ci free(channels_schannel); 1457d5ac70f0Sopenharmony_ci free(slaves_id); 1458d5ac70f0Sopenharmony_ci return err; 1459d5ac70f0Sopenharmony_ci} 1460d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN 1461d5ac70f0Sopenharmony_ciSND_DLSYM_BUILD_VERSION(_snd_pcm_multi_open, SND_PCM_DLSYM_VERSION); 1462d5ac70f0Sopenharmony_ci#endif 1463