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