1/** 2 * \file pcm/pcm_plugin.c 3 * \ingroup PCM 4 * \brief PCM Interface 5 * \author Jaroslav Kysela <perex@perex.cz> 6 * \author Abramo Bagnara <abramo@alsa-project.org> 7 * \date 2000-2001 8 */ 9/* 10 * PCM - Common plugin code 11 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> 12 * 13 * 14 * This library is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU Lesser General Public License as 16 * published by the Free Software Foundation; either version 2.1 of 17 * the License, or (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU Lesser General Public License for more details. 23 * 24 * You should have received a copy of the GNU Lesser General Public 25 * License along with this library; if not, write to the Free Software 26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 27 * 28 */ 29 30/*! 31 32\page pcm_plugins PCM (digital audio) plugins 33 34PCM plugins extends functionality and features of PCM devices. 35The plugins take care about various sample conversions, sample 36copying among channels and so on. 37 38\section pcm_plugins_slave Slave definition 39 40The slave plugin can be specified directly with a string or the definition 41can be entered inside a compound configuration node. Some restrictions can 42be also specified (like static rate or count of channels). 43 44\code 45pcm_slave.NAME { 46 pcm STR # PCM name 47 # or 48 pcm { } # PCM definition 49 format STR # Format or "unchanged" 50 channels INT # Count of channels or "unchanged" string 51 rate INT # Rate in Hz or "unchanged" string 52 period_time INT # Period time in us or "unchanged" string 53 buffer_time INT # Buffer time in us or "unchanged" string 54} 55\endcode 56 57Example: 58 59\code 60pcm_slave.slave_rate44100Hz { 61 pcm "hw:0,0" 62 rate 44100 63} 64 65pcm.rate44100Hz { 66 type plug 67 slave slave_rate44100Hz 68} 69\endcode 70 71The equivalent configuration (in one compound): 72 73\code 74pcm.rate44100Hz { 75 type plug 76 slave { 77 pcm "hw:0,0" 78 rate 44100 79 } 80} 81\endcode 82 83*/ 84 85#include "pcm_local.h" 86#include "pcm_plugin.h" 87#include <limits.h> 88 89#ifndef DOC_HIDDEN 90 91static snd_pcm_sframes_t 92snd_pcm_plugin_undo_read(snd_pcm_t *pcm ATTRIBUTE_UNUSED, 93 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED, 94 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED, 95 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED, 96 snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED) 97{ 98 return -EIO; 99} 100 101static snd_pcm_sframes_t 102snd_pcm_plugin_undo_write(snd_pcm_t *pcm ATTRIBUTE_UNUSED, 103 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED, 104 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED, 105 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED, 106 snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED) 107{ 108 return -EIO; 109} 110 111snd_pcm_sframes_t 112snd_pcm_plugin_undo_read_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED, 113 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED, 114 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED, 115 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED, 116 snd_pcm_uframes_t slave_undo_size) 117{ 118 return slave_undo_size; 119} 120 121snd_pcm_sframes_t 122snd_pcm_plugin_undo_write_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED, 123 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED, 124 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED, 125 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED, 126 snd_pcm_uframes_t slave_undo_size) 127{ 128 return slave_undo_size; 129} 130 131void snd_pcm_plugin_init(snd_pcm_plugin_t *plugin) 132{ 133 memset(plugin, 0, sizeof(snd_pcm_plugin_t)); 134 plugin->undo_read = snd_pcm_plugin_undo_read; 135 plugin->undo_write = snd_pcm_plugin_undo_write; 136} 137 138static int snd_pcm_plugin_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) 139{ 140 snd_pcm_plugin_t *plugin = pcm->private_data; 141 snd_pcm_sframes_t sd; 142 int err = snd_pcm_delay(plugin->gen.slave, &sd); 143 if (err < 0) 144 return err; 145 *delayp = sd; 146 return 0; 147} 148 149static int snd_pcm_plugin_call_init_cb(snd_pcm_t *pcm, snd_pcm_plugin_t *plugin) 150{ 151 snd_pcm_t *slave = plugin->gen.slave; 152 int err; 153 154 assert(pcm->boundary == slave->boundary); 155 *pcm->hw.ptr = *slave->hw.ptr; 156 *pcm->appl.ptr = *slave->appl.ptr; 157 if (plugin->init) { 158 err = plugin->init(pcm); 159 if (err < 0) 160 return err; 161 } 162 return 0; 163} 164 165static int snd_pcm_plugin_prepare(snd_pcm_t *pcm) 166{ 167 snd_pcm_plugin_t *plugin = pcm->private_data; 168 int err; 169 err = snd_pcm_prepare(plugin->gen.slave); 170 if (err < 0) 171 return err; 172 return snd_pcm_plugin_call_init_cb(pcm, plugin); 173} 174 175static int snd_pcm_plugin_reset(snd_pcm_t *pcm) 176{ 177 snd_pcm_plugin_t *plugin = pcm->private_data; 178 int err; 179 err = snd_pcm_reset(plugin->gen.slave); 180 if (err < 0) 181 return err; 182 return snd_pcm_plugin_call_init_cb(pcm, plugin); 183} 184 185static snd_pcm_sframes_t snd_pcm_plugin_rewindable(snd_pcm_t *pcm) 186{ 187 return snd_pcm_mmap_hw_rewindable(pcm); 188} 189 190snd_pcm_sframes_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 191{ 192 snd_pcm_plugin_t *plugin = pcm->private_data; 193 snd_pcm_sframes_t n = snd_pcm_plugin_rewindable(pcm); 194 snd_pcm_sframes_t sframes; 195 196 if ((snd_pcm_uframes_t)n < frames) 197 frames = n; 198 if (frames == 0) 199 return 0; 200 201 sframes = frames; 202 sframes = snd_pcm_rewind(plugin->gen.slave, sframes); 203 if (sframes < 0) 204 return sframes; 205 snd_pcm_mmap_appl_backward(pcm, (snd_pcm_uframes_t) sframes); 206 return (snd_pcm_sframes_t) sframes; 207} 208 209static snd_pcm_sframes_t snd_pcm_plugin_forwardable(snd_pcm_t *pcm) 210{ 211 return snd_pcm_mmap_avail(pcm); 212} 213 214snd_pcm_sframes_t snd_pcm_plugin_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 215{ 216 snd_pcm_plugin_t *plugin = pcm->private_data; 217 snd_pcm_sframes_t n = snd_pcm_plugin_forwardable(pcm); 218 snd_pcm_sframes_t sframes; 219 220 if ((snd_pcm_uframes_t)n < frames) 221 frames = n; 222 if (frames == 0) 223 return 0; 224 225 sframes = frames; 226 sframes = INTERNAL(snd_pcm_forward)(plugin->gen.slave, sframes); 227 if (sframes < 0) 228 return sframes; 229 snd_pcm_mmap_appl_forward(pcm, (snd_pcm_uframes_t) frames); 230 return (snd_pcm_sframes_t) frames; 231} 232 233static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm, 234 const snd_pcm_channel_area_t *areas, 235 snd_pcm_uframes_t offset, 236 snd_pcm_uframes_t size) 237{ 238 snd_pcm_plugin_t *plugin = pcm->private_data; 239 snd_pcm_t *slave = plugin->gen.slave; 240 snd_pcm_uframes_t xfer = 0; 241 snd_pcm_sframes_t result; 242 int err; 243 244 while (size > 0) { 245 snd_pcm_uframes_t frames = size; 246 const snd_pcm_channel_area_t *slave_areas; 247 snd_pcm_uframes_t slave_offset; 248 snd_pcm_uframes_t slave_frames = ULONG_MAX; 249 250 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); 251 if (result < 0) { 252 err = result; 253 goto error; 254 } 255 if (slave_frames == 0) 256 break; 257 frames = plugin->write(pcm, areas, offset, frames, 258 slave_areas, slave_offset, &slave_frames); 259 if (CHECK_SANITY(slave_frames > snd_pcm_mmap_playback_avail(slave))) { 260 SNDMSG("write overflow %ld > %ld", slave_frames, 261 snd_pcm_mmap_playback_avail(slave)); 262 err = -EPIPE; 263 goto error; 264 } 265 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); 266 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) { 267 snd_pcm_sframes_t res; 268 res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result); 269 if (res < 0) { 270 err = res; 271 goto error; 272 } 273 frames -= res; 274 } 275 if (result <= 0) { 276 err = result; 277 goto error; 278 } 279 snd_pcm_mmap_appl_forward(pcm, frames); 280 offset += frames; 281 xfer += frames; 282 size -= frames; 283 } 284 return (snd_pcm_sframes_t)xfer; 285 286 error: 287 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 288} 289 290static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm, 291 const snd_pcm_channel_area_t *areas, 292 snd_pcm_uframes_t offset, 293 snd_pcm_uframes_t size) 294{ 295 snd_pcm_plugin_t *plugin = pcm->private_data; 296 snd_pcm_t *slave = plugin->gen.slave; 297 snd_pcm_uframes_t xfer = 0; 298 snd_pcm_sframes_t result; 299 int err; 300 301 while (size > 0) { 302 snd_pcm_uframes_t frames = size; 303 const snd_pcm_channel_area_t *slave_areas; 304 snd_pcm_uframes_t slave_offset; 305 snd_pcm_uframes_t slave_frames = ULONG_MAX; 306 307 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); 308 if (result < 0) { 309 err = result; 310 goto error; 311 } 312 if (slave_frames == 0) 313 break; 314 frames = (plugin->read)(pcm, areas, offset, frames, 315 slave_areas, slave_offset, &slave_frames); 316 if (CHECK_SANITY(slave_frames > snd_pcm_mmap_capture_avail(slave))) { 317 SNDMSG("read overflow %ld > %ld", slave_frames, 318 snd_pcm_mmap_playback_avail(slave)); 319 err = -EPIPE; 320 goto error; 321 } 322 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); 323 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) { 324 snd_pcm_sframes_t res; 325 326 res = plugin->undo_read(slave, areas, offset, frames, slave_frames - result); 327 if (res < 0) { 328 err = res; 329 goto error; 330 } 331 frames -= res; 332 } 333 if (result <= 0) { 334 err = result; 335 goto error; 336 } 337 snd_pcm_mmap_appl_forward(pcm, frames); 338 offset += frames; 339 xfer += frames; 340 size -= frames; 341 } 342 return (snd_pcm_sframes_t)xfer; 343 344 error: 345 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 346} 347 348 349static snd_pcm_sframes_t 350snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) 351{ 352 snd_pcm_channel_area_t areas[pcm->channels]; 353 snd_pcm_areas_from_buf(pcm, areas, (void*)buffer); 354 return snd_pcm_write_areas(pcm, areas, 0, size, 355 snd_pcm_plugin_write_areas); 356} 357 358static snd_pcm_sframes_t 359snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) 360{ 361 snd_pcm_channel_area_t areas[pcm->channels]; 362 snd_pcm_areas_from_bufs(pcm, areas, bufs); 363 return snd_pcm_write_areas(pcm, areas, 0, size, 364 snd_pcm_plugin_write_areas); 365} 366 367static snd_pcm_sframes_t 368snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size) 369{ 370 snd_pcm_channel_area_t areas[pcm->channels]; 371 snd_pcm_areas_from_buf(pcm, areas, buffer); 372 return snd_pcm_read_areas(pcm, areas, 0, size, 373 snd_pcm_plugin_read_areas); 374} 375 376static snd_pcm_sframes_t 377snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) 378{ 379 snd_pcm_channel_area_t areas[pcm->channels]; 380 snd_pcm_areas_from_bufs(pcm, areas, bufs); 381 return snd_pcm_read_areas(pcm, areas, 0, size, 382 snd_pcm_plugin_read_areas); 383} 384 385static snd_pcm_sframes_t 386snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm, 387 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED, 388 snd_pcm_uframes_t size) 389{ 390 snd_pcm_plugin_t *plugin = pcm->private_data; 391 snd_pcm_t *slave = plugin->gen.slave; 392 const snd_pcm_channel_area_t *areas; 393 snd_pcm_uframes_t appl_offset; 394 snd_pcm_sframes_t slave_size; 395 snd_pcm_sframes_t xfer; 396 int err; 397 398 if (pcm->stream == SND_PCM_STREAM_CAPTURE) { 399 snd_pcm_mmap_appl_forward(pcm, size); 400 return size; 401 } 402 slave_size = snd_pcm_avail_update(slave); 403 if (slave_size < 0) 404 return slave_size; 405 areas = snd_pcm_mmap_areas(pcm); 406 appl_offset = snd_pcm_mmap_offset(pcm); 407 xfer = 0; 408 while (size > 0 && slave_size > 0) { 409 snd_pcm_uframes_t frames = size; 410 snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset; 411 const snd_pcm_channel_area_t *slave_areas; 412 snd_pcm_uframes_t slave_offset; 413 snd_pcm_uframes_t slave_frames = ULONG_MAX; 414 snd_pcm_sframes_t result; 415 416 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); 417 if (result < 0) { 418 err = result; 419 goto error; 420 } 421 if (frames > cont) 422 frames = cont; 423 frames = plugin->write(pcm, areas, appl_offset, frames, 424 slave_areas, slave_offset, &slave_frames); 425 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); 426 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) { 427 snd_pcm_sframes_t res; 428 429 res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result); 430 if (res < 0) { 431 err = res; 432 goto error; 433 } 434 frames -= res; 435 } 436 if (result <= 0) { 437 err = result; 438 goto error; 439 } 440 snd_pcm_mmap_appl_forward(pcm, frames); 441 if (frames == cont) 442 appl_offset = 0; 443 else 444 appl_offset += result; 445 size -= frames; 446 slave_size -= frames; 447 xfer += frames; 448 } 449 if (CHECK_SANITY(size)) { 450 SNDMSG("short commit: %ld", size); 451 return -EPIPE; 452 } 453 return xfer; 454 455 error: 456 return xfer > 0 ? xfer : err; 457} 458 459static snd_pcm_sframes_t 460snd_pcm_plugin_sync_hw_ptr_capture(snd_pcm_t *pcm, 461 snd_pcm_sframes_t slave_size) 462{ 463 snd_pcm_plugin_t *plugin = pcm->private_data; 464 snd_pcm_t *slave = plugin->gen.slave; 465 const snd_pcm_channel_area_t *areas; 466 snd_pcm_uframes_t xfer, hw_offset, size; 467 int err; 468 469 xfer = snd_pcm_mmap_capture_avail(pcm); 470 size = pcm->buffer_size - xfer; 471 areas = snd_pcm_mmap_areas(pcm); 472 hw_offset = snd_pcm_mmap_hw_offset(pcm); 473 while (size > 0 && slave_size > 0) { 474 snd_pcm_uframes_t frames = size; 475 snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset; 476 const snd_pcm_channel_area_t *slave_areas; 477 snd_pcm_uframes_t slave_offset; 478 snd_pcm_uframes_t slave_frames = ULONG_MAX; 479 snd_pcm_sframes_t result; 480 /* As mentioned in the ALSA API (see pcm/pcm.c:942): 481 * The function #snd_pcm_avail_update() 482 * have to be called before any mmap begin+commit operation. 483 * Otherwise the snd_pcm_areas_copy will not called a second time. 484 * But this is needed, if the ring buffer wrap is reached and 485 * there is more data available. 486 */ 487 slave_size = snd_pcm_avail_update(slave); 488 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); 489 if (result < 0) { 490 err = result; 491 goto error; 492 } 493 if (frames > cont) 494 frames = cont; 495 frames = (plugin->read)(pcm, areas, hw_offset, frames, 496 slave_areas, slave_offset, &slave_frames); 497 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); 498 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) { 499 snd_pcm_sframes_t res; 500 res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result); 501 if (res < 0) { 502 err = res; 503 goto error; 504 } 505 frames -= res; 506 } 507 if (result <= 0) { 508 err = result; 509 goto error; 510 } 511 snd_pcm_mmap_hw_forward(pcm, frames); 512 if (frames == cont) 513 hw_offset = 0; 514 else 515 hw_offset += frames; 516 size -= frames; 517 slave_size -= slave_frames; 518 xfer += frames; 519 } 520 return (snd_pcm_sframes_t)xfer; 521error: 522 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 523} 524 525static snd_pcm_sframes_t snd_pcm_plugin_sync_hw_ptr(snd_pcm_t *pcm, 526 snd_pcm_uframes_t slave_hw_ptr, 527 snd_pcm_sframes_t slave_size) 528{ 529 if (pcm->stream == SND_PCM_STREAM_CAPTURE && 530 pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED && 531 pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) 532 return snd_pcm_plugin_sync_hw_ptr_capture(pcm, slave_size); 533 *pcm->hw.ptr = slave_hw_ptr; 534 return slave_size; 535} 536 537static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) 538{ 539 snd_pcm_plugin_t *plugin = pcm->private_data; 540 snd_pcm_t *slave = plugin->gen.slave; 541 snd_pcm_sframes_t slave_size; 542 543 slave_size = snd_pcm_avail_update(slave); 544 return snd_pcm_plugin_sync_hw_ptr(pcm, *slave->hw.ptr, slave_size); 545} 546 547static int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status) 548{ 549 snd_pcm_plugin_t *plugin = pcm->private_data; 550 snd_pcm_sframes_t err, diff; 551 552 err = snd_pcm_status(plugin->gen.slave, status); 553 if (err < 0) 554 return err; 555 snd_pcm_plugin_sync_hw_ptr(pcm, status->hw_ptr, status->avail); 556 /* 557 * For capture stream, the situation is more complicated, because 558 * snd_pcm_plugin_avail_update() commits the data to the slave pcm. 559 * It means that the slave appl_ptr is updated. Calculate diff and 560 * update the delay and avail. 561 * 562 * This resolves the data inconsistency for immediate calls: 563 * snd_pcm_avail_update() 564 * snd_pcm_status() 565 */ 566 if (pcm->stream == SND_PCM_STREAM_CAPTURE) { 567 diff = pcm_frame_diff(status->appl_ptr, *pcm->appl.ptr, pcm->boundary); 568 status->appl_ptr = *pcm->appl.ptr; 569 status->avail += diff; 570 status->delay += diff; 571 } else { 572 assert(status->appl_ptr == *pcm->appl.ptr); 573 } 574 return 0; 575} 576 577int snd_pcm_plugin_may_wait_for_avail_min_conv( 578 snd_pcm_t *pcm, 579 snd_pcm_uframes_t avail, 580 snd_pcm_uframes_t (*conv)(snd_pcm_t *, snd_pcm_uframes_t)) 581{ 582 if (pcm->stream == SND_PCM_STREAM_CAPTURE && 583 pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED && 584 pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) { 585 /* mmap access on capture device already consumes data from 586 * slave in avail_update operation. Entering snd_pcm_wait after 587 * having already consumed some fragments leads to waiting for 588 * too long time, as slave will unnecessarily wait for avail_min 589 * condition reached again. To avoid unnecessary wait times we 590 * adapt the avail_min threshold on slave dynamically. Just 591 * modifying slave->avail_min as a shortcut and lightweight 592 * solution does not work for all slave plugin types and in 593 * addition it will not propagate the change through all 594 * downstream plugins, so we have to use the sw_params API. 595 * note: reading fragmental parts from slave will only happen 596 * in case 597 * a) the slave can provide contineous hw_ptr between periods 598 * b) avail_min does not match one slave_period 599 */ 600 snd_pcm_generic_t *generic = pcm->private_data; 601 /* 602 * do not use snd_pcm_plugin_t pointer here 603 * this code is used from the generic plugins, too 604 */ 605 snd_pcm_t *slave = generic->slave; 606 snd_pcm_uframes_t needed_slave_avail_min; 607 snd_pcm_sframes_t available; 608 609 /* update, as it might have changed. This will also call 610 * avail_update on slave and also can return error 611 */ 612 available = snd_pcm_avail_update(pcm); 613 if (available < 0) 614 return 0; 615 616 if ((snd_pcm_uframes_t)available >= pcm->avail_min) 617 /* don't wait at all. As we can't configure avail_min 618 * of slave to 0 return here 619 */ 620 return 0; 621 622 needed_slave_avail_min = pcm->avail_min - available; 623 624 /* proportional adaption if rate converter is in place.. 625 * Can happen only on built-in rate plugin. 626 * This code is also used by extplug, but extplug does not allow to alter the sampling rate. 627 */ 628 if (conv) 629 needed_slave_avail_min = conv(pcm, needed_slave_avail_min); 630 631 if (slave->avail_min != needed_slave_avail_min) { 632 snd_pcm_sw_params_t *swparams; 633 snd_pcm_sw_params_alloca(&swparams); 634 /* pray that changing sw_params while running is 635 * properly implemented in all downstream plugins... 636 * it's legal but not commonly used. 637 */ 638 snd_pcm_sw_params_current(slave, swparams); 639 /* snd_pcm_sw_params_set_avail_min() restricts setting 640 * to >= period size. This conflicts at least with our 641 * dshare patch which allows combining multiple periods 642 * or with slaves which return hw postions between 643 * periods -> set directly in sw_param structure 644 */ 645 swparams->avail_min = needed_slave_avail_min; 646 snd_pcm_sw_params(slave, swparams); 647 } 648 avail = available; 649 } 650 return snd_pcm_generic_may_wait_for_avail_min(pcm, avail); 651} 652 653int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm, 654 snd_pcm_uframes_t avail) 655{ 656 return snd_pcm_plugin_may_wait_for_avail_min_conv(pcm, avail, NULL); 657} 658 659const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = { 660 .status = snd_pcm_plugin_status, 661 .state = snd_pcm_generic_state, 662 .hwsync = snd_pcm_generic_hwsync, 663 .delay = snd_pcm_plugin_delay, 664 .prepare = snd_pcm_plugin_prepare, 665 .reset = snd_pcm_plugin_reset, 666 .start = snd_pcm_generic_start, 667 .drop = snd_pcm_generic_drop, 668 .drain = snd_pcm_generic_drain, 669 .pause = snd_pcm_generic_pause, 670 .rewindable = snd_pcm_plugin_rewindable, 671 .rewind = snd_pcm_plugin_rewind, 672 .forwardable = snd_pcm_plugin_forwardable, 673 .forward = snd_pcm_plugin_forward, 674 .resume = snd_pcm_generic_resume, 675 .link = snd_pcm_generic_link, 676 .link_slaves = snd_pcm_generic_link_slaves, 677 .unlink = snd_pcm_generic_unlink, 678 .writei = snd_pcm_plugin_writei, 679 .writen = snd_pcm_plugin_writen, 680 .readi = snd_pcm_plugin_readi, 681 .readn = snd_pcm_plugin_readn, 682 .avail_update = snd_pcm_plugin_avail_update, 683 .mmap_commit = snd_pcm_plugin_mmap_commit, 684 .htimestamp = snd_pcm_generic_htimestamp, 685 .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, 686 .poll_descriptors = snd_pcm_generic_poll_descriptors, 687 .poll_revents = snd_pcm_generic_poll_revents, 688 .may_wait_for_avail_min = snd_pcm_plugin_may_wait_for_avail_min, 689}; 690 691#endif 692