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 "pcm_local.h" 30#include "pcm_generic.h" 31#include <stdio.h> 32#include <stdlib.h> 33#include <unistd.h> 34#include <string.h> 35#include <math.h> 36 37#ifndef PIC 38/* entry for static linking */ 39const char *_snd_module_pcm_multi = ""; 40#endif 41 42#ifndef DOC_HIDDEN 43 44typedef 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 51typedef struct { 52 int slave_idx; 53 unsigned int slave_channel; 54} snd_pcm_multi_channel_t; 55 56typedef 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 67static 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 86static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED) 87{ 88 return 0; 89} 90 91static 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 98static 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 105static 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 125static 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 132static 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 146static 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 165static 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 179static 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 212static 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 247static 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 256static 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 297static 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 */ 321static 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 336static 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 357static 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 377static 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 391static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm); 392static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status) 393{ 394 snd_pcm_multi_t *multi = pcm->private_data; 395 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm; 396 397 int err = snd_pcm_status(slave, status); 398 if (err < 0) 399 return err; 400 snd_pcm_sframes_t avail = snd_pcm_multi_avail_update(pcm); 401 if (avail < 0) 402 return avail; 403 status->hw_ptr = *pcm->hw.ptr; 404 status->avail = avail; 405 return 0; 406} 407 408static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm) 409{ 410 snd_pcm_multi_t *multi = pcm->private_data; 411 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm; 412 return snd_pcm_state(slave); 413} 414 415static void snd_pcm_multi_hwptr_update(snd_pcm_t *pcm) 416{ 417 snd_pcm_multi_t *multi = pcm->private_data; 418 snd_pcm_uframes_t hw_ptr = 0, slave_hw_ptr, avail, last_avail; 419 unsigned int i; 420 /* the logic is really simple, choose the lowest hw_ptr from slaves */ 421 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 422 last_avail = 0; 423 for (i = 0; i < multi->slaves_count; ++i) { 424 slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr; 425 avail = __snd_pcm_playback_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 } else { 432 last_avail = LONG_MAX; 433 for (i = 0; i < multi->slaves_count; ++i) { 434 slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr; 435 avail = __snd_pcm_capture_avail(pcm, multi->hw_ptr, slave_hw_ptr); 436 if (avail < last_avail) { 437 hw_ptr = slave_hw_ptr; 438 last_avail = avail; 439 } 440 } 441 } 442 multi->hw_ptr = hw_ptr; 443} 444 445static int snd_pcm_multi_hwsync(snd_pcm_t *pcm) 446{ 447 snd_pcm_multi_t *multi = pcm->private_data; 448 unsigned int i; 449 int err; 450 for (i = 0; i < multi->slaves_count; ++i) { 451 err = snd_pcm_hwsync(multi->slaves[i].pcm); 452 if (err < 0) 453 return err; 454 } 455 snd_pcm_multi_hwptr_update(pcm); 456 return 0; 457} 458 459static int snd_pcm_multi_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) 460{ 461 snd_pcm_multi_t *multi = pcm->private_data; 462 snd_pcm_sframes_t d, dr = 0; 463 unsigned int i; 464 int err; 465 for (i = 0; i < multi->slaves_count; ++i) { 466 err = snd_pcm_delay(multi->slaves[i].pcm, &d); 467 if (err < 0) 468 return err; 469 if (dr < d) 470 dr = d; 471 } 472 *delayp = dr; 473 return 0; 474} 475 476static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm) 477{ 478 snd_pcm_multi_t *multi = pcm->private_data; 479 snd_pcm_sframes_t ret = LONG_MAX; 480 unsigned int i; 481 for (i = 0; i < multi->slaves_count; ++i) { 482 snd_pcm_sframes_t avail; 483 avail = snd_pcm_avail_update(multi->slaves[i].pcm); 484 if (avail < 0) 485 return avail; 486 if (ret > avail) 487 ret = avail; 488 } 489 snd_pcm_multi_hwptr_update(pcm); 490 return ret; 491} 492 493static int snd_pcm_multi_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail, 494 snd_htimestamp_t *tstamp) 495{ 496 snd_pcm_multi_t *multi = pcm->private_data; 497 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm; 498 return snd_pcm_htimestamp(slave, avail, tstamp); 499} 500 501static int snd_pcm_multi_prepare(snd_pcm_t *pcm) 502{ 503 snd_pcm_multi_t *multi = pcm->private_data; 504 int result = 0, err; 505 unsigned int i; 506 for (i = 0; i < multi->slaves_count; ++i) { 507 /* We call prepare to each slave even if it's linked. 508 * This is to make sure to sync non-mmaped control/status. 509 */ 510 err = snd_pcm_prepare(multi->slaves[i].pcm); 511 if (err < 0) 512 result = err; 513 } 514 multi->hw_ptr = multi->appl_ptr = 0; 515 return result; 516} 517 518static int snd_pcm_multi_reset(snd_pcm_t *pcm) 519{ 520 snd_pcm_multi_t *multi = pcm->private_data; 521 int result = 0, err; 522 unsigned int i; 523 for (i = 0; i < multi->slaves_count; ++i) { 524 /* Reset each slave, as well as in prepare */ 525 err = snd_pcm_reset(multi->slaves[i].pcm); 526 if (err < 0) 527 result = err; 528 } 529 multi->hw_ptr = multi->appl_ptr = 0; 530 return result; 531} 532 533/* when the first slave PCM is linked, it means that the whole multi 534 * plugin instance is linked manually to another PCM. in this case, 535 * we need to trigger the master. 536 */ 537static int snd_pcm_multi_start(snd_pcm_t *pcm) 538{ 539 snd_pcm_multi_t *multi = pcm->private_data; 540 int err = 0; 541 unsigned int i; 542 if (multi->slaves[0].linked) 543 return snd_pcm_start(multi->slaves[0].linked); 544 for (i = 0; i < multi->slaves_count; ++i) { 545 if (multi->slaves[i].linked) 546 continue; 547 err = snd_pcm_start(multi->slaves[i].pcm); 548 if (err < 0) 549 return err; 550 } 551 return err; 552} 553 554static int snd_pcm_multi_drop(snd_pcm_t *pcm) 555{ 556 snd_pcm_multi_t *multi = pcm->private_data; 557 int err = 0; 558 unsigned int i; 559 if (multi->slaves[0].linked) 560 return snd_pcm_drop(multi->slaves[0].linked); 561 for (i = 0; i < multi->slaves_count; ++i) { 562 if (multi->slaves[i].linked) 563 continue; 564 err = snd_pcm_drop(multi->slaves[i].pcm); 565 if (err < 0) 566 return err; 567 } 568 return err; 569} 570 571static int snd_pcm_multi_drain(snd_pcm_t *pcm) 572{ 573 snd_pcm_multi_t *multi = pcm->private_data; 574 int err = 0; 575 unsigned int i; 576 if (multi->slaves[0].linked) 577 return snd_pcm_drain(multi->slaves[0].linked); 578 for (i = 0; i < multi->slaves_count; ++i) { 579 if (multi->slaves[i].linked) 580 continue; 581 err = snd_pcm_drain(multi->slaves[i].pcm); 582 if (err < 0) 583 return err; 584 } 585 return err; 586} 587 588static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable) 589{ 590 snd_pcm_multi_t *multi = pcm->private_data; 591 int err = 0; 592 unsigned int i; 593 if (multi->slaves[0].linked) 594 return snd_pcm_pause(multi->slaves[0].linked, enable); 595 for (i = 0; i < multi->slaves_count; ++i) { 596 if (multi->slaves[i].linked) 597 continue; 598 err = snd_pcm_pause(multi->slaves[i].pcm, enable); 599 if (err < 0) 600 return err; 601 } 602 return err; 603} 604 605static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) 606{ 607 snd_pcm_multi_t *multi = pcm->private_data; 608 unsigned int channel = info->channel; 609 snd_pcm_multi_channel_t *c = &multi->channels[channel]; 610 int err; 611 if (c->slave_idx < 0) 612 return -ENXIO; 613 info->channel = c->slave_channel; 614 err = snd_pcm_channel_info(multi->slaves[c->slave_idx].pcm, info); 615 info->channel = channel; 616 return err; 617} 618 619static snd_pcm_sframes_t snd_pcm_multi_rewindable(snd_pcm_t *pcm) 620{ 621 snd_pcm_multi_t *multi = pcm->private_data; 622 unsigned int i; 623 snd_pcm_sframes_t frames = LONG_MAX; 624 625 for (i = 0; i < multi->slaves_count; ++i) { 626 snd_pcm_sframes_t f = snd_pcm_rewindable(multi->slaves[i].pcm); 627 if (f <= 0) 628 return f; 629 if (f < frames) 630 frames = f; 631 } 632 633 return frames; 634 635} 636 637static snd_pcm_sframes_t snd_pcm_multi_forwardable(snd_pcm_t *pcm) 638{ 639 snd_pcm_multi_t *multi = pcm->private_data; 640 unsigned int i; 641 snd_pcm_sframes_t frames = LONG_MAX; 642 643 for (i = 0; i < multi->slaves_count; ++i) { 644 snd_pcm_sframes_t f = snd_pcm_forwardable(multi->slaves[i].pcm); 645 if (f <= 0) 646 return f; 647 if (f < frames) 648 frames = f; 649 } 650 651 return frames; 652 653} 654 655static snd_pcm_sframes_t snd_pcm_multi_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 656{ 657 snd_pcm_multi_t *multi = pcm->private_data; 658 unsigned int i; 659 snd_pcm_uframes_t pos[multi->slaves_count]; 660 memset(pos, 0, sizeof(pos)); 661 for (i = 0; i < multi->slaves_count; ++i) { 662 snd_pcm_t *slave_i = multi->slaves[i].pcm; 663 snd_pcm_sframes_t f = snd_pcm_rewind(slave_i, frames); 664 if (f < 0) 665 return f; 666 pos[i] = f; 667 frames = f; 668 } 669 /* Realign the pointers */ 670 for (i = 0; i < multi->slaves_count; ++i) { 671 snd_pcm_t *slave_i = multi->slaves[i].pcm; 672 snd_pcm_uframes_t f = pos[i] - frames; 673 snd_pcm_sframes_t result; 674 if (f > 0) { 675 result = INTERNAL(snd_pcm_forward)(slave_i, f); 676 if (result < 0) 677 return result; 678 if ((snd_pcm_uframes_t)result != f) 679 return -EIO; 680 } 681 } 682 snd_pcm_mmap_appl_backward(pcm, frames); 683 return frames; 684} 685 686static snd_pcm_sframes_t snd_pcm_multi_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 687{ 688 snd_pcm_multi_t *multi = pcm->private_data; 689 unsigned int i; 690 snd_pcm_uframes_t pos[multi->slaves_count]; 691 memset(pos, 0, sizeof(pos)); 692 for (i = 0; i < multi->slaves_count; ++i) { 693 snd_pcm_t *slave_i = multi->slaves[i].pcm; 694 snd_pcm_sframes_t f = INTERNAL(snd_pcm_forward)(slave_i, frames); 695 if (f < 0) 696 return f; 697 pos[i] = f; 698 frames = f; 699 } 700 /* Realign the pointers */ 701 for (i = 0; i < multi->slaves_count; ++i) { 702 snd_pcm_t *slave_i = multi->slaves[i].pcm; 703 snd_pcm_uframes_t f = pos[i] - frames; 704 snd_pcm_sframes_t result; 705 if (f > 0) { 706 result = snd_pcm_rewind(slave_i, f); 707 if (result < 0) 708 return result; 709 if ((snd_pcm_uframes_t)result != f) 710 return -EIO; 711 } 712 } 713 snd_pcm_mmap_appl_forward(pcm, frames); 714 return frames; 715} 716 717static int snd_pcm_multi_resume(snd_pcm_t *pcm) 718{ 719 snd_pcm_multi_t *multi = pcm->private_data; 720 int err = 0; 721 unsigned int i; 722 if (multi->slaves[0].linked) 723 return snd_pcm_resume(multi->slaves[0].linked); 724 for (i = 0; i < multi->slaves_count; ++i) { 725 if (multi->slaves[i].linked) 726 continue; 727 err = snd_pcm_resume(multi->slaves[i].pcm); 728 if (err < 0) 729 return err; 730 } 731 return err; 732} 733 734/* if a multi plugin instance is linked as slaves, every slave PCMs 735 * including the first one has to be relinked to the given master. 736 */ 737static int snd_pcm_multi_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master) 738{ 739 snd_pcm_multi_t *multi = pcm->private_data; 740 unsigned int i; 741 int err; 742 743 for (i = 0; i < multi->slaves_count; ++i) { 744 snd_pcm_unlink(multi->slaves[i].pcm); 745 multi->slaves[i].linked = NULL; 746 err = snd_pcm_link(master, multi->slaves[i].pcm); 747 if (err < 0) { 748 reset_links(multi); 749 return err; 750 } 751 multi->slaves[i].linked = master; 752 } 753 return 0; 754} 755 756/* linking to a multi as a master is easy - simply link to the first 757 * slave element as its own slaves are already linked. 758 */ 759static int snd_pcm_multi_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2) 760{ 761 snd_pcm_multi_t *multi = pcm1->private_data; 762 snd_pcm_t *main_pcm = multi->slaves[0].pcm; 763 if (main_pcm->fast_ops->link) 764 return main_pcm->fast_ops->link(main_pcm->fast_op_arg, pcm2); 765 return -ENOSYS; 766} 767 768static int snd_pcm_multi_unlink(snd_pcm_t *pcm) 769{ 770 snd_pcm_multi_t *multi = pcm->private_data; 771 unsigned int i; 772 773 for (i = 0; i < multi->slaves_count; ++i) { 774 if (multi->slaves[i].linked) 775 snd_pcm_unlink(multi->slaves[i].linked); 776 multi->slaves[0].linked = NULL; 777 } 778 return 0; 779} 780 781static snd_pcm_sframes_t snd_pcm_multi_mmap_commit(snd_pcm_t *pcm, 782 snd_pcm_uframes_t offset, 783 snd_pcm_uframes_t size) 784{ 785 snd_pcm_multi_t *multi = pcm->private_data; 786 snd_pcm_t *slave; 787 unsigned int i; 788 snd_pcm_sframes_t result; 789 790 for (i = 0; i < multi->slaves_count; ++i) { 791 slave = multi->slaves[i].pcm; 792 result = snd_pcm_mmap_commit(slave, offset, size); 793 if (result < 0) 794 return result; 795 if ((snd_pcm_uframes_t)result != size) 796 return -EIO; 797 } 798 snd_pcm_mmap_appl_forward(pcm, size); 799 return size; 800} 801 802static int snd_pcm_multi_munmap(snd_pcm_t *pcm) 803{ 804 free(pcm->mmap_channels); 805 free(pcm->running_areas); 806 pcm->mmap_channels = NULL; 807 pcm->running_areas = NULL; 808 return 0; 809} 810 811static int snd_pcm_multi_mmap(snd_pcm_t *pcm) 812{ 813 snd_pcm_multi_t *multi = pcm->private_data; 814 unsigned int c; 815 816 pcm->mmap_channels = calloc(pcm->channels, 817 sizeof(pcm->mmap_channels[0])); 818 pcm->running_areas = calloc(pcm->channels, 819 sizeof(pcm->running_areas[0])); 820 if (!pcm->mmap_channels || !pcm->running_areas) { 821 snd_pcm_multi_munmap(pcm); 822 return -ENOMEM; 823 } 824 825 /* Copy the slave mmapped buffer data */ 826 for (c = 0; c < pcm->channels; c++) { 827 snd_pcm_multi_channel_t *chan = &multi->channels[c]; 828 snd_pcm_t *slave; 829 if (chan->slave_idx < 0) { 830 snd_pcm_multi_munmap(pcm); 831 return -ENXIO; 832 } 833 slave = multi->slaves[chan->slave_idx].pcm; 834 pcm->mmap_channels[c] = 835 slave->mmap_channels[chan->slave_channel]; 836 pcm->mmap_channels[c].channel = c; 837 pcm->running_areas[c] = 838 slave->running_areas[chan->slave_channel]; 839 } 840 return 0; 841} 842 843static int snd_pcm_multi_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail) 844{ 845 snd_pcm_multi_t *multi = pcm->private_data; 846 unsigned int i; 847 for (i = 0; i < multi->slaves_count; ++i) { 848 if (snd_pcm_may_wait_for_avail_min(multi->slaves[i].pcm, avail)) 849 return 1; 850 } 851 return 0; 852} 853 854static snd_pcm_chmap_query_t **snd_pcm_multi_query_chmaps(snd_pcm_t *pcm) 855{ 856 snd_pcm_multi_t *multi = pcm->private_data; 857 snd_pcm_chmap_query_t **slave_maps[multi->slaves_count]; 858 snd_pcm_chmap_query_t **maps; 859 unsigned int i; 860 int err = -ENOMEM; 861 862 memset(slave_maps, 0, sizeof(slave_maps)); 863 maps = calloc(2, sizeof(*maps)); 864 if (!maps) 865 return NULL; 866 maps[0] = calloc(multi->channels_count + 2, sizeof(int *)); 867 if (!maps[0]) 868 goto error; 869 maps[0]->type = SND_CHMAP_TYPE_FIXED; 870 maps[0]->map.channels = multi->channels_count; 871 872 for (i = 0; i < multi->slaves_count; i++) { 873 slave_maps[i] = snd_pcm_query_chmaps(multi->slaves[i].pcm); 874 if (!slave_maps[i]) 875 goto error; 876 } 877 878 for (i = 0; i < multi->channels_count; i++) { 879 snd_pcm_multi_channel_t *bind = &multi->channels[i]; 880 unsigned int slave_channels = 881 multi->slaves[bind->slave_idx].channels_count; 882 snd_pcm_chmap_query_t **p; 883 884 for (p = slave_maps[bind->slave_idx]; *p; p++) { 885 if ((*p)->map.channels == slave_channels) { 886 maps[0]->map.pos[i] = 887 (*p)->map.pos[bind->slave_channel]; 888 break; 889 } 890 } 891 } 892 err = 0; 893 894 error: 895 for (i = 0; i < multi->slaves_count; i++) { 896 if (slave_maps[i]) 897 snd_pcm_free_chmaps(slave_maps[i]); 898 } 899 900 if (err) { 901 snd_pcm_free_chmaps(maps); 902 return NULL; 903 } 904 905 return maps; 906} 907 908static snd_pcm_chmap_t *snd_pcm_multi_get_chmap(snd_pcm_t *pcm) 909{ 910 snd_pcm_multi_t *multi = pcm->private_data; 911 snd_pcm_chmap_t *map; 912 snd_pcm_chmap_t *slave_maps[multi->slaves_count]; 913 unsigned int i; 914 int err = -ENOMEM; 915 916 memset(slave_maps, 0, sizeof(slave_maps)); 917 map = calloc(multi->channels_count + 1, sizeof(int)); 918 if (!map) 919 return NULL; 920 921 for (i = 0; i < multi->slaves_count; i++) { 922 slave_maps[i] = snd_pcm_get_chmap(multi->slaves[i].pcm); 923 if (!slave_maps[i]) 924 goto error; 925 } 926 927 map->channels = multi->channels_count; 928 for (i = 0; i < multi->channels_count; i++) { 929 snd_pcm_multi_channel_t *bind = &multi->channels[i]; 930 map->pos[i] = slave_maps[bind->slave_idx]->pos[bind->slave_channel]; 931 } 932 err = 0; 933 934 error: 935 for (i = 0; i < multi->slaves_count; i++) 936 free(slave_maps[i]); 937 938 if (err) { 939 free(map); 940 return NULL; 941 } 942 943 return map; 944} 945 946static int snd_pcm_multi_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) 947{ 948 snd_pcm_multi_t *multi = pcm->private_data; 949 snd_pcm_chmap_t *slave_maps[multi->slaves_count]; 950 unsigned int i; 951 int err = 0; 952 953 if (map->channels != multi->channels_count) 954 return -EINVAL; 955 956 for (i = 0; i < multi->slaves_count; i++) { 957 slave_maps[i] = calloc(multi->slaves[i].channels_count + 1, 958 sizeof(int)); 959 if (!slave_maps[i]) { 960 for (i++; i < multi->slaves_count; i++) 961 slave_maps[i] = NULL; 962 err = -ENOMEM; 963 goto error; 964 } 965 } 966 967 for (i = 0; i < multi->channels_count; i++) { 968 snd_pcm_multi_channel_t *bind = &multi->channels[i]; 969 slave_maps[bind->slave_idx]->pos[bind->slave_channel] = 970 map->pos[i]; 971 } 972 973 for (i = 0; i < multi->slaves_count; i++) { 974 err = snd_pcm_set_chmap(multi->slaves[i].pcm, slave_maps[i]); 975 if (err < 0) 976 goto error; 977 } 978 979 error: 980 for (i = 0; i < multi->slaves_count; i++) 981 free(slave_maps[i]); 982 983 return err; 984} 985 986static void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out) 987{ 988 snd_pcm_multi_t *multi = pcm->private_data; 989 unsigned int k; 990 snd_output_printf(out, "Multi PCM\n"); 991 snd_output_printf(out, " Channel bindings:\n"); 992 for (k = 0; k < multi->channels_count; ++k) { 993 snd_pcm_multi_channel_t *c = &multi->channels[k]; 994 if (c->slave_idx < 0) 995 continue; 996 snd_output_printf(out, " %d: slave %d, channel %d\n", 997 k, c->slave_idx, c->slave_channel); 998 } 999 if (pcm->setup) { 1000 snd_output_printf(out, "Its setup is:\n"); 1001 snd_pcm_dump_setup(pcm, out); 1002 } 1003 for (k = 0; k < multi->slaves_count; ++k) { 1004 snd_output_printf(out, "Slave #%d: ", k); 1005 snd_pcm_dump(multi->slaves[k].pcm, out); 1006 } 1007} 1008 1009static const snd_pcm_ops_t snd_pcm_multi_ops = { 1010 .close = snd_pcm_multi_close, 1011 .info = snd_pcm_multi_info, 1012 .hw_refine = snd_pcm_multi_hw_refine, 1013 .hw_params = snd_pcm_multi_hw_params, 1014 .hw_free = snd_pcm_multi_hw_free, 1015 .sw_params = snd_pcm_multi_sw_params, 1016 .channel_info = snd_pcm_multi_channel_info, 1017 .dump = snd_pcm_multi_dump, 1018 .nonblock = snd_pcm_multi_nonblock, 1019 .async = snd_pcm_multi_async, 1020 .mmap = snd_pcm_multi_mmap, 1021 .munmap = snd_pcm_multi_munmap, 1022 .query_chmaps = snd_pcm_multi_query_chmaps, 1023 .get_chmap = snd_pcm_multi_get_chmap, 1024 .set_chmap = snd_pcm_multi_set_chmap, 1025}; 1026 1027static const snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = { 1028 .status = snd_pcm_multi_status, 1029 .state = snd_pcm_multi_state, 1030 .hwsync = snd_pcm_multi_hwsync, 1031 .delay = snd_pcm_multi_delay, 1032 .prepare = snd_pcm_multi_prepare, 1033 .reset = snd_pcm_multi_reset, 1034 .start = snd_pcm_multi_start, 1035 .drop = snd_pcm_multi_drop, 1036 .drain = snd_pcm_multi_drain, 1037 .pause = snd_pcm_multi_pause, 1038 .writei = snd_pcm_mmap_writei, 1039 .writen = snd_pcm_mmap_writen, 1040 .readi = snd_pcm_mmap_readi, 1041 .readn = snd_pcm_mmap_readn, 1042 .rewindable = snd_pcm_multi_rewindable, 1043 .rewind = snd_pcm_multi_rewind, 1044 .forwardable = snd_pcm_multi_forwardable, 1045 .forward = snd_pcm_multi_forward, 1046 .resume = snd_pcm_multi_resume, 1047 .link = snd_pcm_multi_link, 1048 .link_slaves = snd_pcm_multi_link_slaves, 1049 .unlink = snd_pcm_multi_unlink, 1050 .avail_update = snd_pcm_multi_avail_update, 1051 .mmap_commit = snd_pcm_multi_mmap_commit, 1052 .htimestamp = snd_pcm_multi_htimestamp, 1053 .poll_descriptors_count = snd_pcm_multi_poll_descriptors_count, 1054 .poll_descriptors = snd_pcm_multi_poll_descriptors, 1055 .poll_revents = snd_pcm_multi_poll_revents, 1056 .may_wait_for_avail_min = snd_pcm_multi_may_wait_for_avail_min, 1057}; 1058 1059/** 1060 * \brief Creates a new Multi PCM 1061 * \param pcmp Returns created PCM handle 1062 * \param name Name of PCM 1063 * \param slaves_count Count of slaves 1064 * \param master_slave Master slave number 1065 * \param slaves_pcm Array with slave PCMs 1066 * \param schannels_count Array with slave channel counts 1067 * \param channels_count Count of channels 1068 * \param sidxs Array with channels indexes to slaves 1069 * \param schannels Array with slave channels 1070 * \param close_slaves When set, the slave PCM handle is closed 1071 * \retval zero on success otherwise a negative error code 1072 * \warning Using of this function might be dangerous in the sense 1073 * of compatibility reasons. The prototype might be freely 1074 * changed in future. 1075 */ 1076int snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, 1077 unsigned int slaves_count, unsigned int master_slave, 1078 snd_pcm_t **slaves_pcm, unsigned int *schannels_count, 1079 unsigned int channels_count, 1080 int *sidxs, unsigned int *schannels, 1081 int close_slaves) 1082{ 1083 snd_pcm_t *pcm; 1084 snd_pcm_multi_t *multi; 1085 unsigned int i; 1086 snd_pcm_stream_t stream; 1087 int err; 1088 1089 assert(pcmp); 1090 assert(slaves_count > 0 && slaves_pcm && schannels_count); 1091 assert(channels_count > 0 && sidxs && schannels); 1092 assert(master_slave < slaves_count); 1093 1094 multi = calloc(1, sizeof(snd_pcm_multi_t)); 1095 if (!multi) { 1096 return -ENOMEM; 1097 } 1098 1099 stream = slaves_pcm[0]->stream; 1100 1101 multi->slaves_count = slaves_count; 1102 multi->master_slave = master_slave; 1103 multi->slaves = calloc(slaves_count, sizeof(*multi->slaves)); 1104 if (!multi->slaves) { 1105 free(multi); 1106 return -ENOMEM; 1107 } 1108 multi->channels_count = channels_count; 1109 multi->channels = calloc(channels_count, sizeof(*multi->channels)); 1110 if (!multi->channels) { 1111 free(multi->slaves); 1112 free(multi); 1113 return -ENOMEM; 1114 } 1115 for (i = 0; i < slaves_count; ++i) { 1116 snd_pcm_multi_slave_t *slave = &multi->slaves[i]; 1117 assert(slaves_pcm[i]->stream == stream); 1118 slave->pcm = slaves_pcm[i]; 1119 slave->channels_count = schannels_count[i]; 1120 slave->close_slave = close_slaves; 1121 } 1122 for (i = 0; i < channels_count; ++i) { 1123 snd_pcm_multi_channel_t *bind = &multi->channels[i]; 1124 assert(sidxs[i] < (int)slaves_count); 1125 assert(schannels[i] < schannels_count[sidxs[i]]); 1126 bind->slave_idx = sidxs[i]; 1127 bind->slave_channel = schannels[i]; 1128 if (sidxs[i] < 0) 1129 continue; 1130 } 1131 multi->channels_count = channels_count; 1132 1133 err = snd_pcm_new(&pcm, SND_PCM_TYPE_MULTI, name, stream, 1134 multi->slaves[0].pcm->mode); 1135 if (err < 0) { 1136 free(multi->slaves); 1137 free(multi->channels); 1138 free(multi); 1139 return err; 1140 } 1141 pcm->mmap_rw = 1; 1142 pcm->mmap_shadow = 1; /* has own mmap method */ 1143 pcm->ops = &snd_pcm_multi_ops; 1144 pcm->fast_ops = &snd_pcm_multi_fast_ops; 1145 pcm->private_data = multi; 1146 pcm->poll_fd = multi->slaves[master_slave].pcm->poll_fd; 1147 pcm->poll_events = multi->slaves[master_slave].pcm->poll_events; 1148 pcm->tstamp_type = multi->slaves[master_slave].pcm->tstamp_type; 1149 snd_pcm_set_hw_ptr(pcm, &multi->hw_ptr, -1, 0); 1150 snd_pcm_set_appl_ptr(pcm, &multi->appl_ptr, -1, 0); 1151 *pcmp = pcm; 1152 return 0; 1153} 1154 1155/*! \page pcm_plugins 1156 1157\section pcm_plugins_multi Plugin: Multiple streams to One 1158 1159This plugin converts multiple streams to one. 1160 1161\code 1162pcm.name { 1163 type multi # Multiple streams conversion PCM 1164 slaves { # Slaves definition 1165 ID STR # Slave PCM name 1166 # or 1167 ID { 1168 pcm STR # Slave PCM name 1169 # or 1170 pcm { } # Slave PCM definition 1171 channels INT # Slave channels 1172 } 1173 } 1174 bindings { # Bindings table 1175 N { 1176 slave STR # Slave key 1177 channel INT # Slave channel 1178 } 1179 } 1180 [master INT] # Define the master slave 1181} 1182\endcode 1183 1184For example, to bind two PCM streams with two-channel stereo (hw:0,0 and 1185hw:0,1) as one 4-channel stereo PCM stream, define like this: 1186\code 1187pcm.quad { 1188 type multi 1189 1190 slaves.a.pcm "hw:0,0" 1191 slaves.a.channels 2 1192 slaves.b.pcm "hw:0,1" 1193 slaves.b.channels 2 1194 1195 bindings.0.slave a 1196 bindings.0.channel 0 1197 bindings.1.slave a 1198 bindings.1.channel 1 1199 bindings.2.slave b 1200 bindings.2.channel 0 1201 bindings.3.slave b 1202 bindings.3.channel 1 1203} 1204\endcode 1205Note that the resultant pcm "quad" is not in the interleaved format 1206but in the "complex" format. Hence, it's not accessible by applications 1207which can handle only the interleaved (or the non-interleaved) format. 1208In such a case, wrap this PCM with \ref pcm_plugins_route "route" or 1209\ref pcm_plugins_plug "plug" plugin. 1210\code 1211pcm.quad2 { 1212 type route 1213 slave.pcm "quad" 1214 ttable.0.0 1 1215 ttable.1.1 1 1216 ttable.2.2 1 1217 ttable.3.3 1 1218} 1219\endcode 1220 1221\subsection pcm_plugins_multi_funcref Function reference 1222 1223<UL> 1224 <LI>snd_pcm_multi_open() 1225 <LI>_snd_pcm_multi_open() 1226</UL> 1227 1228*/ 1229 1230/** 1231 * \brief Creates a new Multi PCM 1232 * \param pcmp Returns created PCM handle 1233 * \param name Name of PCM 1234 * \param root Root configuration node 1235 * \param conf Configuration node with Multi PCM description 1236 * \param stream Stream type 1237 * \param mode Stream mode 1238 * \retval zero on success otherwise a negative error code 1239 * \warning Using of this function might be dangerous in the sense 1240 * of compatibility reasons. The prototype might be freely 1241 * changed in future. 1242 */ 1243int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, 1244 snd_config_t *root, snd_config_t *conf, 1245 snd_pcm_stream_t stream, int mode) 1246{ 1247 snd_config_iterator_t i, inext, j, jnext; 1248 snd_config_t *slaves = NULL; 1249 snd_config_t *bindings = NULL; 1250 int err; 1251 unsigned int idx; 1252 const char **slaves_id = NULL; 1253 snd_config_t **slaves_conf = NULL; 1254 snd_pcm_t **slaves_pcm = NULL; 1255 unsigned int *slaves_channels = NULL; 1256 int *channels_sidx = NULL; 1257 unsigned int *channels_schannel = NULL; 1258 unsigned int slaves_count = 0; 1259 long master_slave = 0; 1260 unsigned int channels_count = 0; 1261 snd_config_for_each(i, inext, conf) { 1262 snd_config_t *n = snd_config_iterator_entry(i); 1263 const char *id; 1264 if (snd_config_get_id(n, &id) < 0) 1265 continue; 1266 if (snd_pcm_conf_generic_id(id)) 1267 continue; 1268 if (strcmp(id, "slaves") == 0) { 1269 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { 1270 SNDERR("Invalid type for %s", id); 1271 return -EINVAL; 1272 } 1273 slaves = n; 1274 continue; 1275 } 1276 if (strcmp(id, "bindings") == 0) { 1277 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { 1278 SNDERR("Invalid type for %s", id); 1279 return -EINVAL; 1280 } 1281 bindings = n; 1282 continue; 1283 } 1284 if (strcmp(id, "master") == 0) { 1285 if (snd_config_get_integer(n, &master_slave) < 0) { 1286 SNDERR("Invalid type for %s", id); 1287 return -EINVAL; 1288 } 1289 continue; 1290 } 1291 SNDERR("Unknown field %s", id); 1292 return -EINVAL; 1293 } 1294 if (!slaves) { 1295 SNDERR("slaves is not defined"); 1296 return -EINVAL; 1297 } 1298 if (!bindings) { 1299 SNDERR("bindings is not defined"); 1300 return -EINVAL; 1301 } 1302 snd_config_for_each(i, inext, slaves) { 1303 ++slaves_count; 1304 } 1305 if (master_slave < 0 || master_slave >= (long)slaves_count) { 1306 SNDERR("Master slave is out of range (0-%u)", slaves_count-1); 1307 return -EINVAL; 1308 } 1309 snd_config_for_each(i, inext, bindings) { 1310 long cchannel; 1311 snd_config_t *m = snd_config_iterator_entry(i); 1312 const char *id; 1313 if (snd_config_get_id(m, &id) < 0) 1314 continue; 1315 err = safe_strtol(id, &cchannel); 1316 if (err < 0 || cchannel < 0) { 1317 SNDERR("Invalid channel number: %s", id); 1318 return -EINVAL; 1319 } 1320 if ((unsigned long)cchannel >= channels_count) 1321 channels_count = cchannel + 1; 1322 } 1323 if (channels_count == 0) { 1324 SNDERR("No channels defined"); 1325 return -EINVAL; 1326 } 1327 slaves_id = calloc(slaves_count, sizeof(*slaves_id)); 1328 slaves_conf = calloc(slaves_count, sizeof(*slaves_conf)); 1329 slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm)); 1330 slaves_channels = calloc(slaves_count, sizeof(*slaves_channels)); 1331 channels_sidx = calloc(channels_count, sizeof(*channels_sidx)); 1332 channels_schannel = calloc(channels_count, sizeof(*channels_schannel)); 1333 if (!slaves_id || !slaves_conf || !slaves_pcm || !slaves_channels || 1334 !channels_sidx || !channels_schannel) { 1335 err = -ENOMEM; 1336 goto _free; 1337 } 1338 for (idx = 0; idx < channels_count; ++idx) 1339 channels_sidx[idx] = -1; 1340 idx = 0; 1341 snd_config_for_each(i, inext, slaves) { 1342 snd_config_t *m = snd_config_iterator_entry(i); 1343 const char *id; 1344 int channels; 1345 if (snd_config_get_id(m, &id) < 0) 1346 continue; 1347 slaves_id[idx] = id; 1348 err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1, 1349 SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels); 1350 if (err < 0) 1351 goto _free; 1352 slaves_channels[idx] = channels; 1353 ++idx; 1354 } 1355 1356 snd_config_for_each(i, inext, bindings) { 1357 snd_config_t *m = snd_config_iterator_entry(i); 1358 long cchannel = -1; 1359 long schannel = -1; 1360 int slave = -1; 1361 long val; 1362 const char *str; 1363 const char *id; 1364 if (snd_config_get_id(m, &id) < 0) 1365 continue; 1366 err = safe_strtol(id, &cchannel); 1367 if (err < 0 || cchannel < 0) { 1368 SNDERR("Invalid channel number: %s", id); 1369 err = -EINVAL; 1370 goto _free; 1371 } 1372 snd_config_for_each(j, jnext, m) { 1373 snd_config_t *n = snd_config_iterator_entry(j); 1374 const char *id; 1375 if (snd_config_get_id(n, &id) < 0) 1376 continue; 1377 if (strcmp(id, "comment") == 0) 1378 continue; 1379 if (strcmp(id, "slave") == 0) { 1380 char buf[32]; 1381 unsigned int k; 1382 err = snd_config_get_string(n, &str); 1383 if (err < 0) { 1384 err = snd_config_get_integer(n, &val); 1385 if (err < 0) { 1386 SNDERR("Invalid value for %s", id); 1387 goto _free; 1388 } 1389 sprintf(buf, "%ld", val); 1390 str = buf; 1391 } 1392 for (k = 0; k < slaves_count; ++k) { 1393 if (strcmp(slaves_id[k], str) == 0) 1394 slave = k; 1395 } 1396 continue; 1397 } 1398 if (strcmp(id, "channel") == 0) { 1399 err = snd_config_get_integer(n, &schannel); 1400 if (err < 0) { 1401 SNDERR("Invalid type for %s", id); 1402 goto _free; 1403 } 1404 continue; 1405 } 1406 SNDERR("Unknown field %s", id); 1407 err = -EINVAL; 1408 goto _free; 1409 } 1410 if (slave < 0 || (unsigned int)slave >= slaves_count) { 1411 SNDERR("Invalid or missing sidx for channel %s", id); 1412 err = -EINVAL; 1413 goto _free; 1414 } 1415 if (schannel < 0 || 1416 (unsigned int) schannel >= slaves_channels[slave]) { 1417 SNDERR("Invalid or missing schannel for channel %s", id); 1418 err = -EINVAL; 1419 goto _free; 1420 } 1421 channels_sidx[cchannel] = slave; 1422 channels_schannel[cchannel] = schannel; 1423 } 1424 1425 for (idx = 0; idx < slaves_count; ++idx) { 1426 err = snd_pcm_open_slave(&slaves_pcm[idx], root, 1427 slaves_conf[idx], stream, mode, 1428 conf); 1429 if (err < 0) 1430 goto _free; 1431 snd_config_delete(slaves_conf[idx]); 1432 slaves_conf[idx] = NULL; 1433 } 1434 err = snd_pcm_multi_open(pcmp, name, slaves_count, master_slave, 1435 slaves_pcm, slaves_channels, 1436 channels_count, 1437 channels_sidx, channels_schannel, 1438 1); 1439_free: 1440 if (err < 0) { 1441 for (idx = 0; idx < slaves_count; ++idx) { 1442 if (slaves_pcm[idx]) 1443 snd_pcm_close(slaves_pcm[idx]); 1444 } 1445 } 1446 if (slaves_conf) { 1447 for (idx = 0; idx < slaves_count; ++idx) { 1448 if (slaves_conf[idx]) 1449 snd_config_delete(slaves_conf[idx]); 1450 } 1451 free(slaves_conf); 1452 } 1453 free(slaves_pcm); 1454 free(slaves_channels); 1455 free(channels_sidx); 1456 free(channels_schannel); 1457 free(slaves_id); 1458 return err; 1459} 1460#ifndef DOC_HIDDEN 1461SND_DLSYM_BUILD_VERSION(_snd_pcm_multi_open, SND_PCM_DLSYM_VERSION); 1462#endif 1463