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