1/** 2 * \file pcm/pcm_ioplug.c 3 * \ingroup Plugin_SDK 4 * \brief I/O Plugin SDK 5 * \author Takashi Iwai <tiwai@suse.de> 6 * \date 2005 7 */ 8/* 9 * PCM - External I/O Plugin SDK 10 * Copyright (c) 2005 by Takashi Iwai <tiwai@suse.de> 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 26 * 27 */ 28 29#include "pcm_local.h" 30#include "pcm_ioplug.h" 31#include "pcm_ext_parm.h" 32#include "pcm_generic.h" 33 34#ifndef PIC 35/* entry for static linking */ 36const char *_snd_module_pcm_ioplug = ""; 37#endif 38 39#ifndef DOC_HIDDEN 40 41/* hw_params */ 42typedef struct snd_pcm_ioplug_priv { 43 snd_pcm_ioplug_t *data; 44 struct snd_ext_parm params[SND_PCM_IOPLUG_HW_PARAMS]; 45 unsigned int last_hw; 46 snd_pcm_uframes_t avail_max; 47 snd_htimestamp_t trigger_tstamp; 48} ioplug_priv_t; 49 50/* update the hw pointer */ 51static void snd_pcm_ioplug_hw_ptr_update(snd_pcm_t *pcm) 52{ 53 ioplug_priv_t *io = pcm->private_data; 54 snd_pcm_sframes_t hw; 55 56 hw = io->data->callback->pointer(io->data); 57 if (hw >= 0) { 58 unsigned int delta; 59 if ((unsigned int)hw >= io->last_hw) 60 delta = hw - io->last_hw; 61 else 62 delta = pcm->buffer_size + hw - io->last_hw; 63 io->data->hw_ptr += delta; 64 io->last_hw = hw; 65 } else 66 io->data->state = SNDRV_PCM_STATE_XRUN; 67} 68 69static int snd_pcm_ioplug_info(snd_pcm_t *pcm, snd_pcm_info_t *info) 70{ 71 memset(info, 0, sizeof(*info)); 72 info->stream = pcm->stream; 73 info->card = -1; 74 if (pcm->name) { 75 strncpy((char *)info->id, pcm->name, sizeof(info->id)); 76 strncpy((char *)info->name, pcm->name, sizeof(info->name)); 77 strncpy((char *)info->subname, pcm->name, sizeof(info->subname)); 78 } 79 info->subdevices_count = 1; 80 return 0; 81} 82 83static int snd_pcm_ioplug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) 84{ 85 return snd_pcm_channel_info_shm(pcm, info, -1); 86} 87 88static int snd_pcm_ioplug_status(snd_pcm_t *pcm, snd_pcm_status_t * status) 89{ 90 ioplug_priv_t *io = pcm->private_data; 91 92 memset(status, 0, sizeof(*status)); 93 snd_pcm_ioplug_hw_ptr_update(pcm); 94 status->state = io->data->state; 95 status->trigger_tstamp = io->trigger_tstamp; 96 status->avail = snd_pcm_mmap_avail(pcm); 97 status->avail_max = io->avail_max; 98 return 0; 99} 100 101static snd_pcm_state_t snd_pcm_ioplug_state(snd_pcm_t *pcm) 102{ 103 ioplug_priv_t *io = pcm->private_data; 104 return io->data->state; 105} 106 107static int snd_pcm_ioplug_hwsync(snd_pcm_t *pcm) 108{ 109 snd_pcm_ioplug_hw_ptr_update(pcm); 110 return 0; 111} 112 113static int snd_pcm_ioplug_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) 114{ 115 ioplug_priv_t *io = pcm->private_data; 116 117 if (io->data->version >= 0x010001 && 118 io->data->callback->delay) 119 return io->data->callback->delay(io->data, delayp); 120 else { 121 snd_pcm_ioplug_hw_ptr_update(pcm); 122 *delayp = snd_pcm_mmap_hw_avail(pcm); 123 } 124 return 0; 125} 126 127static int snd_pcm_ioplug_reset(snd_pcm_t *pcm) 128{ 129 ioplug_priv_t *io = pcm->private_data; 130 131 io->data->appl_ptr = 0; 132 io->data->hw_ptr = 0; 133 io->last_hw = 0; 134 io->avail_max = 0; 135 return 0; 136} 137 138static int snd_pcm_ioplug_prepare(snd_pcm_t *pcm) 139{ 140 ioplug_priv_t *io = pcm->private_data; 141 142 io->data->state = SND_PCM_STATE_PREPARED; 143 snd_pcm_ioplug_reset(pcm); 144 if (io->data->callback->prepare) 145 return io->data->callback->prepare(io->data); 146 return 0; 147} 148 149static const int hw_params_type[SND_PCM_IOPLUG_HW_PARAMS] = { 150 [SND_PCM_IOPLUG_HW_ACCESS] = SND_PCM_HW_PARAM_ACCESS, 151 [SND_PCM_IOPLUG_HW_FORMAT] = SND_PCM_HW_PARAM_FORMAT, 152 [SND_PCM_IOPLUG_HW_CHANNELS] = SND_PCM_HW_PARAM_CHANNELS, 153 [SND_PCM_IOPLUG_HW_RATE] = SND_PCM_HW_PARAM_RATE, 154 [SND_PCM_IOPLUG_HW_PERIOD_BYTES] = SND_PCM_HW_PARAM_PERIOD_BYTES, 155 [SND_PCM_IOPLUG_HW_BUFFER_BYTES] = SND_PCM_HW_PARAM_BUFFER_BYTES, 156 [SND_PCM_IOPLUG_HW_PERIODS] = SND_PCM_HW_PARAM_PERIODS, 157}; 158 159/* x = a * b */ 160static int rule_mul(snd_pcm_hw_params_t *params, int x, int a, int b) 161{ 162 snd_interval_t t; 163 164 snd_interval_mul(hw_param_interval(params, a), 165 hw_param_interval(params, b), &t); 166 return snd_interval_refine(hw_param_interval(params, x), &t); 167} 168 169/* x = a / b */ 170static int rule_div(snd_pcm_hw_params_t *params, int x, int a, int b) 171{ 172 snd_interval_t t; 173 174 snd_interval_div(hw_param_interval(params, a), 175 hw_param_interval(params, b), &t); 176 return snd_interval_refine(hw_param_interval(params, x), &t); 177} 178 179/* x = a * b / k */ 180static int rule_muldivk(snd_pcm_hw_params_t *params, int x, int a, int b, int k) 181{ 182 snd_interval_t t; 183 184 snd_interval_muldivk(hw_param_interval(params, a), 185 hw_param_interval(params, b), k, &t); 186 return snd_interval_refine(hw_param_interval(params, x), &t); 187} 188 189/* x = a * k / b */ 190static int rule_mulkdiv(snd_pcm_hw_params_t *params, int x, int a, int k, int b) 191{ 192 snd_interval_t t; 193 194 snd_interval_mulkdiv(hw_param_interval(params, a), k, 195 hw_param_interval(params, b), &t); 196 return snd_interval_refine(hw_param_interval(params, x), &t); 197} 198 199#if 0 200static void dump_parm(snd_pcm_hw_params_t *params) 201{ 202 snd_output_t *log; 203 snd_output_stdio_attach(&log, stderr, 0); 204 snd_pcm_hw_params_dump(params, log); 205 snd_output_close(log); 206} 207#endif 208 209/* refine *_TIME and *_SIZE, then update *_BYTES */ 210static int refine_time_and_size(snd_pcm_hw_params_t *params, 211 int time, int size, int bytes) 212{ 213 int err, change1 = 0; 214 215 /* size = time * rate / 1000000 */ 216 err = rule_muldivk(params, size, time, 217 SND_PCM_HW_PARAM_RATE, 1000000); 218 if (err < 0) 219 return err; 220 change1 |= err; 221 222 /* bytes = size * framebits / 8 */ 223 err = rule_muldivk(params, bytes, size, 224 SND_PCM_HW_PARAM_FRAME_BITS, 8); 225 if (err < 0) 226 return err; 227 change1 |= err; 228 return change1; 229} 230 231/* refine *_TIME and *_SIZE from *_BYTES */ 232static int refine_back_time_and_size(snd_pcm_hw_params_t *params, 233 int time, int size, int bytes) 234{ 235 int err; 236 237 /* size = bytes * 8 / framebits */ 238 err = rule_mulkdiv(params, size, bytes, 8, SND_PCM_HW_PARAM_FRAME_BITS); 239 if (err < 0) 240 return err; 241 /* time = size * 1000000 / rate */ 242 err = rule_mulkdiv(params, time, size, 1000000, SND_PCM_HW_PARAM_RATE); 243 if (err < 0) 244 return err; 245 return 0; 246} 247 248 249static int snd_pcm_ioplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 250{ 251 int change = 0, change1, change2, err; 252 ioplug_priv_t *io = pcm->private_data; 253 struct snd_ext_parm *p; 254 unsigned int i; 255 256 /* access, format */ 257 for (i = SND_PCM_IOPLUG_HW_ACCESS; i <= SND_PCM_IOPLUG_HW_FORMAT; i++) { 258 err = snd_ext_parm_mask_refine(hw_param_mask(params, hw_params_type[i]), 259 io->params, i); 260 if (err < 0) 261 return err; 262 change |= err; 263 } 264 /* channels, rate */ 265 for (; i <= SND_PCM_IOPLUG_HW_RATE; i++) { 266 err = snd_ext_parm_interval_refine(hw_param_interval(params, hw_params_type[i]), 267 io->params, i); 268 if (err < 0) 269 return err; 270 change |= err; 271 } 272 273 if (params->rmask & ((1 << SND_PCM_HW_PARAM_ACCESS) | 274 (1 << SND_PCM_HW_PARAM_FORMAT) | 275 (1 << SND_PCM_HW_PARAM_SUBFORMAT) | 276 (1 << SND_PCM_HW_PARAM_CHANNELS) | 277 (1 << SND_PCM_HW_PARAM_RATE))) { 278 err = snd_pcm_hw_refine_soft(pcm, params); 279 if (err < 0) 280 return err; 281 change |= err; 282 } 283 284 change1 = refine_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME, 285 SND_PCM_HW_PARAM_PERIOD_SIZE, 286 SND_PCM_HW_PARAM_PERIOD_BYTES); 287 if (change1 < 0) 288 return change1; 289 err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_BYTES), 290 io->params, SND_PCM_IOPLUG_HW_PERIOD_BYTES); 291 if (err < 0) 292 return err; 293 change1 |= err; 294 if (change1) { 295 change |= change1; 296 err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME, 297 SND_PCM_HW_PARAM_PERIOD_SIZE, 298 SND_PCM_HW_PARAM_PERIOD_BYTES); 299 if (err < 0) 300 return err; 301 } 302 303 change1 = refine_time_and_size(params, SND_PCM_HW_PARAM_BUFFER_TIME, 304 SND_PCM_HW_PARAM_BUFFER_SIZE, 305 SND_PCM_HW_PARAM_BUFFER_BYTES); 306 if (change1 < 0) 307 return change1; 308 change |= change1; 309 310 do { 311 change2 = 0; 312 err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_BUFFER_BYTES), 313 io->params, SND_PCM_IOPLUG_HW_BUFFER_BYTES); 314 if (err < 0) 315 return err; 316 change2 |= err; 317 /* periods = buffer_bytes / period_bytes */ 318 err = rule_div(params, SND_PCM_HW_PARAM_PERIODS, 319 SND_PCM_HW_PARAM_BUFFER_BYTES, 320 SND_PCM_HW_PARAM_PERIOD_BYTES); 321 if (err < 0) 322 return err; 323 change2 |= err; 324 err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIODS), 325 io->params, SND_PCM_IOPLUG_HW_PERIODS); 326 if (err < 0) 327 return err; 328 change2 |= err; 329 /* buffer_bytes = periods * period_bytes */ 330 err = rule_mul(params, SND_PCM_HW_PARAM_BUFFER_BYTES, 331 SND_PCM_HW_PARAM_PERIOD_BYTES, 332 SND_PCM_HW_PARAM_PERIODS); 333 if (err < 0) 334 return err; 335 change2 |= err; 336 change1 |= change2; 337 } while (change2); 338 change |= change1; 339 340 if (change1) { 341 err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_BUFFER_TIME, 342 SND_PCM_HW_PARAM_BUFFER_SIZE, 343 SND_PCM_HW_PARAM_BUFFER_BYTES); 344 if (err < 0) 345 return err; 346 } 347 348 /* period_bytes = buffer_bytes / periods */ 349 err = rule_div(params, SND_PCM_HW_PARAM_PERIOD_BYTES, 350 SND_PCM_HW_PARAM_BUFFER_BYTES, 351 SND_PCM_HW_PARAM_PERIODS); 352 if (err < 0) 353 return err; 354 if (err) { 355 /* update period_size and period_time */ 356 change |= err; 357 err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_BYTES), 358 io->params, SND_PCM_IOPLUG_HW_PERIOD_BYTES); 359 if (err < 0) 360 return err; 361 err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME, 362 SND_PCM_HW_PARAM_PERIOD_SIZE, 363 SND_PCM_HW_PARAM_PERIOD_BYTES); 364 if (err < 0) 365 return err; 366 } 367 368 params->info = SND_PCM_INFO_BLOCK_TRANSFER; 369 p = &io->params[SND_PCM_IOPLUG_HW_ACCESS]; 370 if (p->active) { 371 for (i = 0; i < p->num_list; i++) 372 switch (p->list[i]) { 373 case SND_PCM_ACCESS_MMAP_INTERLEAVED: 374 case SND_PCM_ACCESS_RW_INTERLEAVED: 375 params->info |= SND_PCM_INFO_INTERLEAVED; 376 break; 377 case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: 378 case SND_PCM_ACCESS_RW_NONINTERLEAVED: 379 params->info |= SND_PCM_INFO_NONINTERLEAVED; 380 break; 381 } 382 } 383 if (io->data->callback->pause) 384 params->info |= SND_PCM_INFO_PAUSE; 385 if (io->data->callback->resume) 386 params->info |= SND_PCM_INFO_RESUME; 387 388#if 0 389 fprintf(stderr, "XXX\n"); 390 dump_parm(params); 391#endif 392 return change; 393} 394 395static int snd_pcm_ioplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 396{ 397 ioplug_priv_t *io = pcm->private_data; 398 int err; 399 400 INTERNAL(snd_pcm_hw_params_get_access)(params, &io->data->access); 401 INTERNAL(snd_pcm_hw_params_get_format)(params, &io->data->format); 402 INTERNAL(snd_pcm_hw_params_get_channels)(params, &io->data->channels); 403 INTERNAL(snd_pcm_hw_params_get_rate)(params, &io->data->rate, 0); 404 INTERNAL(snd_pcm_hw_params_get_period_size)(params, &io->data->period_size, 0); 405 INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &io->data->buffer_size); 406 if (io->data->callback->hw_params) { 407 err = io->data->callback->hw_params(io->data, params); 408 if (err < 0) 409 return err; 410 INTERNAL(snd_pcm_hw_params_get_access)(params, &io->data->access); 411 INTERNAL(snd_pcm_hw_params_get_format)(params, &io->data->format); 412 INTERNAL(snd_pcm_hw_params_get_channels)(params, &io->data->channels); 413 INTERNAL(snd_pcm_hw_params_get_rate)(params, &io->data->rate, 0); 414 INTERNAL(snd_pcm_hw_params_get_period_size)(params, &io->data->period_size, 0); 415 INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &io->data->buffer_size); 416 } 417 return 0; 418} 419 420static int snd_pcm_ioplug_hw_free(snd_pcm_t *pcm) 421{ 422 ioplug_priv_t *io = pcm->private_data; 423 424 if (io->data->callback->hw_free) 425 return io->data->callback->hw_free(io->data); 426 return 0; 427} 428 429static int snd_pcm_ioplug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) 430{ 431 ioplug_priv_t *io = pcm->private_data; 432 433 if (io->data->callback->sw_params) 434 return io->data->callback->sw_params(io->data, params); 435 return 0; 436} 437 438 439static int snd_pcm_ioplug_start(snd_pcm_t *pcm) 440{ 441 ioplug_priv_t *io = pcm->private_data; 442 int err; 443 444 if (io->data->state != SND_PCM_STATE_PREPARED) 445 return -EBADFD; 446 447 err = io->data->callback->start(io->data); 448 if (err < 0) 449 return err; 450 451 gettimestamp(&io->trigger_tstamp, pcm->monotonic); 452 io->data->state = SND_PCM_STATE_RUNNING; 453 454 return 0; 455} 456 457static int snd_pcm_ioplug_drop(snd_pcm_t *pcm) 458{ 459 ioplug_priv_t *io = pcm->private_data; 460 461 if (io->data->state == SND_PCM_STATE_OPEN) 462 return -EBADFD; 463 464 io->data->callback->stop(io->data); 465 466 gettimestamp(&io->trigger_tstamp, pcm->monotonic); 467 io->data->state = SND_PCM_STATE_SETUP; 468 469 return 0; 470} 471 472static int snd_pcm_ioplug_drain(snd_pcm_t *pcm) 473{ 474 ioplug_priv_t *io = pcm->private_data; 475 476 if (io->data->state == SND_PCM_STATE_OPEN) 477 return -EBADFD; 478 if (io->data->callback->drain) 479 io->data->callback->drain(io->data); 480 return snd_pcm_ioplug_drop(pcm); 481} 482 483static int snd_pcm_ioplug_pause(snd_pcm_t *pcm, int enable) 484{ 485 ioplug_priv_t *io = pcm->private_data; 486 static const snd_pcm_state_t states[2] = { 487 SND_PCM_STATE_RUNNING, SND_PCM_STATE_PAUSED 488 }; 489 int prev, err; 490 491 prev = !enable; 492 enable = !prev; 493 if (io->data->state != states[prev]) 494 return -EBADFD; 495 if (io->data->callback->pause) { 496 err = io->data->callback->pause(io->data, enable); 497 if (err < 0) 498 return err; 499 } 500 io->data->state = states[enable]; 501 return 0; 502} 503 504static snd_pcm_sframes_t snd_pcm_ioplug_rewindable(snd_pcm_t *pcm) 505{ 506 return snd_pcm_mmap_hw_avail(pcm); 507} 508 509static snd_pcm_sframes_t snd_pcm_ioplug_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 510{ 511 snd_pcm_mmap_appl_backward(pcm, frames); 512 return frames; 513} 514 515static snd_pcm_sframes_t snd_pcm_ioplug_forwardable(snd_pcm_t *pcm) 516{ 517 return snd_pcm_mmap_avail(pcm); 518} 519 520static snd_pcm_sframes_t snd_pcm_ioplug_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 521{ 522 snd_pcm_mmap_appl_forward(pcm, frames); 523 return frames; 524} 525 526static int snd_pcm_ioplug_resume(snd_pcm_t *pcm) 527{ 528 ioplug_priv_t *io = pcm->private_data; 529 530 if (io->data->callback->resume) 531 io->data->callback->resume(io->data); 532 return 0; 533} 534 535static snd_pcm_sframes_t ioplug_priv_transfer_areas(snd_pcm_t *pcm, 536 const snd_pcm_channel_area_t *areas, 537 snd_pcm_uframes_t offset, 538 snd_pcm_uframes_t size) 539{ 540 ioplug_priv_t *io = pcm->private_data; 541 snd_pcm_sframes_t result; 542 543 if (! size) 544 return 0; 545 if (io->data->callback->transfer) 546 result = io->data->callback->transfer(io->data, areas, offset, size); 547 else 548 result = size; 549 if (result > 0) 550 snd_pcm_mmap_appl_forward(pcm, result); 551 return result; 552} 553 554static snd_pcm_sframes_t snd_pcm_ioplug_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) 555{ 556 if (pcm->mmap_rw) 557 return snd_pcm_mmap_writei(pcm, buffer, size); 558 else { 559 snd_pcm_channel_area_t areas[pcm->channels]; 560 snd_pcm_areas_from_buf(pcm, areas, (void*)buffer); 561 return snd_pcm_write_areas(pcm, areas, 0, size, 562 ioplug_priv_transfer_areas); 563 } 564} 565 566static snd_pcm_sframes_t snd_pcm_ioplug_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) 567{ 568 if (pcm->mmap_rw) 569 return snd_pcm_mmap_writen(pcm, bufs, size); 570 else { 571 snd_pcm_channel_area_t areas[pcm->channels]; 572 snd_pcm_areas_from_bufs(pcm, areas, bufs); 573 return snd_pcm_write_areas(pcm, areas, 0, size, 574 ioplug_priv_transfer_areas); 575 } 576} 577 578static snd_pcm_sframes_t snd_pcm_ioplug_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size) 579{ 580 if (pcm->mmap_rw) 581 return snd_pcm_mmap_readi(pcm, buffer, size); 582 else { 583 snd_pcm_channel_area_t areas[pcm->channels]; 584 snd_pcm_areas_from_buf(pcm, areas, buffer); 585 return snd_pcm_read_areas(pcm, areas, 0, size, 586 ioplug_priv_transfer_areas); 587 } 588} 589 590static snd_pcm_sframes_t snd_pcm_ioplug_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) 591{ 592 if (pcm->mmap_rw) 593 return snd_pcm_mmap_readn(pcm, bufs, size); 594 else { 595 snd_pcm_channel_area_t areas[pcm->channels]; 596 snd_pcm_areas_from_bufs(pcm, areas, bufs); 597 return snd_pcm_read_areas(pcm, areas, 0, size, 598 ioplug_priv_transfer_areas); 599 } 600} 601 602static snd_pcm_sframes_t snd_pcm_ioplug_mmap_commit(snd_pcm_t *pcm, 603 snd_pcm_uframes_t offset, 604 snd_pcm_uframes_t size) 605{ 606 if (pcm->stream == SND_PCM_STREAM_PLAYBACK && 607 pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED && 608 pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) { 609 const snd_pcm_channel_area_t *areas; 610 snd_pcm_uframes_t ofs, frames = size; 611 612 snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames); 613 if (ofs != offset) 614 return -EIO; 615 return ioplug_priv_transfer_areas(pcm, areas, offset, frames); 616 } 617 618 snd_pcm_mmap_appl_forward(pcm, size); 619 return size; 620} 621 622static snd_pcm_sframes_t snd_pcm_ioplug_avail_update(snd_pcm_t *pcm) 623{ 624 ioplug_priv_t *io = pcm->private_data; 625 snd_pcm_uframes_t avail; 626 627 snd_pcm_ioplug_hw_ptr_update(pcm); 628 if (io->data->state == SNDRV_PCM_STATE_XRUN) 629 return -EPIPE; 630 if (pcm->stream == SND_PCM_STREAM_CAPTURE && 631 pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED && 632 pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) { 633 if (io->data->callback->transfer) { 634 const snd_pcm_channel_area_t *areas; 635 snd_pcm_uframes_t offset, size = UINT_MAX; 636 snd_pcm_sframes_t result; 637 638 snd_pcm_mmap_begin(pcm, &areas, &offset, &size); 639 result = io->data->callback->transfer(io->data, areas, offset, size); 640 if (result < 0) 641 return result; 642 } 643 } 644 avail = snd_pcm_mmap_avail(pcm); 645 if (avail > io->avail_max) 646 io->avail_max = avail; 647 return (snd_pcm_sframes_t)avail; 648} 649 650static int snd_pcm_ioplug_nonblock(snd_pcm_t *pcm, int nonblock) 651{ 652 ioplug_priv_t *io = pcm->private_data; 653 654 io->data->nonblock = nonblock; 655 return 0; 656} 657 658static int snd_pcm_ioplug_poll_descriptors_count(snd_pcm_t *pcm) 659{ 660 ioplug_priv_t *io = pcm->private_data; 661 662 if (io->data->callback->poll_descriptors_count) 663 return io->data->callback->poll_descriptors_count(io->data); 664 else 665 return 1; 666} 667 668static int snd_pcm_ioplug_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space) 669{ 670 ioplug_priv_t *io = pcm->private_data; 671 672 if (io->data->callback->poll_descriptors) 673 return io->data->callback->poll_descriptors(io->data, pfds, space); 674 if (pcm->poll_fd < 0) 675 return -EIO; 676 if (space >= 1 && pfds) { 677 pfds->fd = pcm->poll_fd; 678 pfds->events = pcm->poll_events | POLLERR | POLLNVAL; 679 } else { 680 return 0; 681 } 682 return 1; 683} 684 685static int snd_pcm_ioplug_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) 686{ 687 ioplug_priv_t *io = pcm->private_data; 688 689 if (io->data->callback->poll_revents) 690 return io->data->callback->poll_revents(io->data, pfds, nfds, revents); 691 else 692 *revents = pfds->revents; 693 return 0; 694} 695 696static int snd_pcm_ioplug_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) 697{ 698 return 0; 699} 700 701static int snd_pcm_ioplug_async(snd_pcm_t *pcm ATTRIBUTE_UNUSED, 702 int sig ATTRIBUTE_UNUSED, 703 pid_t pid ATTRIBUTE_UNUSED) 704{ 705 return -ENOSYS; 706} 707 708static int snd_pcm_ioplug_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) 709{ 710 return 0; 711} 712 713static void snd_pcm_ioplug_dump(snd_pcm_t *pcm, snd_output_t *out) 714{ 715 ioplug_priv_t *io = pcm->private_data; 716 717 if (io->data->callback->dump) 718 io->data->callback->dump(io->data, out); 719 else { 720 if (io->data->name) 721 snd_output_printf(out, "%s\n", io->data->name); 722 else 723 snd_output_printf(out, "IO-PCM Plugin\n"); 724 if (pcm->setup) { 725 snd_output_printf(out, "Its setup is:\n"); 726 snd_pcm_dump_setup(pcm, out); 727 } 728 } 729} 730 731static void clear_io_params(ioplug_priv_t *io) 732{ 733 int i; 734 for (i = 0; i < SND_PCM_IOPLUG_HW_PARAMS; i++) 735 snd_ext_parm_clear(&io->params[i]); 736} 737 738static int snd_pcm_ioplug_close(snd_pcm_t *pcm) 739{ 740 ioplug_priv_t *io = pcm->private_data; 741 742 clear_io_params(io); 743 if (io->data->callback->close) 744 io->data->callback->close(io->data); 745 free(io); 746 747 return 0; 748} 749 750static const snd_pcm_ops_t snd_pcm_ioplug_ops = { 751 .close = snd_pcm_ioplug_close, 752 .nonblock = snd_pcm_ioplug_nonblock, 753 .async = snd_pcm_ioplug_async, 754 .info = snd_pcm_ioplug_info, 755 .hw_refine = snd_pcm_ioplug_hw_refine, 756 .hw_params = snd_pcm_ioplug_hw_params, 757 .hw_free = snd_pcm_ioplug_hw_free, 758 .sw_params = snd_pcm_ioplug_sw_params, 759 .channel_info = snd_pcm_ioplug_channel_info, 760 .dump = snd_pcm_ioplug_dump, 761 .mmap = snd_pcm_ioplug_mmap, 762 .munmap = snd_pcm_ioplug_munmap, 763}; 764 765static const snd_pcm_fast_ops_t snd_pcm_ioplug_fast_ops = { 766 .status = snd_pcm_ioplug_status, 767 .prepare = snd_pcm_ioplug_prepare, 768 .reset = snd_pcm_ioplug_reset, 769 .start = snd_pcm_ioplug_start, 770 .drop = snd_pcm_ioplug_drop, 771 .drain = snd_pcm_ioplug_drain, 772 .pause = snd_pcm_ioplug_pause, 773 .state = snd_pcm_ioplug_state, 774 .hwsync = snd_pcm_ioplug_hwsync, 775 .delay = snd_pcm_ioplug_delay, 776 .resume = snd_pcm_ioplug_resume, 777 .link = NULL, 778 .link_slaves = NULL, 779 .unlink = NULL, 780 .rewindable = snd_pcm_ioplug_rewindable, 781 .rewind = snd_pcm_ioplug_rewind, 782 .forwardable = snd_pcm_ioplug_forwardable, 783 .forward = snd_pcm_ioplug_forward, 784 .writei = snd_pcm_ioplug_writei, 785 .writen = snd_pcm_ioplug_writen, 786 .readi = snd_pcm_ioplug_readi, 787 .readn = snd_pcm_ioplug_readn, 788 .avail_update = snd_pcm_ioplug_avail_update, 789 .mmap_commit = snd_pcm_ioplug_mmap_commit, 790 .htimestamp = snd_pcm_generic_real_htimestamp, 791 .poll_descriptors_count = snd_pcm_ioplug_poll_descriptors_count, 792 .poll_descriptors = snd_pcm_ioplug_poll_descriptors, 793 .poll_revents = snd_pcm_ioplug_poll_revents, 794}; 795 796#endif /* !DOC_HIDDEN */ 797 798/* 799 * Exported functions 800 */ 801 802/*! \page pcm_external_plugins PCM External Plugin SDK 803 804\section pcm_ioplug External Plugin: I/O Plugin 805 806The I/O-type plugin is a PCM plugin to work as the input or output terminal point, 807i.e. as a user-space PCM driver. 808 809The new plugin is created via #snd_pcm_ioplug_create() function. 810The first argument is a pointer of the pluging information. Some of 811this struct must be initialized in prior to call 812#snd_pcm_ioplug_create(). Then the function fills other fields in 813return. The rest arguments, name, stream and mode, are usually 814identical with the values passed from the ALSA plugin constructor. 815 816The following fields are mandatory: version, name, callback. 817Otherfields are optional and should be initialized with zero. 818 819The constant #SND_PCM_IOPLUG_VERSION must be passed to the version 820field for the version check in alsa-lib. A non-NULL ASCII string 821has to be passed to the name field. The callback field contains the 822table of callback functions for this plugin (defined as 823#snd_pcm_ioplug_callback_t). 824 825flags field specifies the optional bit-flags. poll_fd and poll_events 826specify the poll file descriptor and the corresponding poll events 827(POLLIN, POLLOUT) for the plugin. If the plugin requires multiple 828poll descriptors or poll descriptor(s) dynamically varying, set 829poll_descriptors and poll_descriptors_count callbacks to the callback 830table. Then the poll_fd and poll_events field are ignored. 831 832mmap_rw specifies whether the plugin behaves in the pseudo mmap mode. 833When this value is set to 1, the plugin creates always a local buffer 834and performs read/write calls using this buffer as if it's mmapped. 835The address of local buffer can be obtained via 836#snd_pcm_ioplug_mmap_areas() function. 837When poll_fd, poll_events and mmap_rw fields are changed after 838#snd_pcm_ioplug_create(), call #snd_pcm_ioplug_reinit_status() to 839reflect the changes. 840 841The driver can set an arbitrary value (pointer) to private_data 842field to refer its own data in the callbacks. 843 844The rest fields are filled by #snd_pcm_ioplug_create(). The pcm field 845is the resultant PCM handle. The others are the current status of the 846PCM. 847 848The callback functions in #snd_pcm_ioplug_callback_t define the real 849behavior of the driver. 850At least, start, stop and pointer callbacks must be given. Other 851callbacks are optional. The start and stop callbacks are called when 852the PCM stream is started and stopped, repsectively. The pointer 853callback returns the current DMA position, which may be called at any 854time. 855 856The transfer callback is called when any data transfer happens. It 857receives the area array, offset and the size to transfer. The area 858array contains the array of snd_pcm_channel_area_t with the elements 859of number of channels. 860 861When the PCM is closed, close callback is called. If the driver 862allocates any internal buffers, they should be released in this 863callback. The hw_params and hw_free callbacks are called when 864hw_params are set and reset, respectively. Note that they may be 865called multiple times according to the application. Similarly, 866sw_params callback is called when sw_params is set or changed. 867 868The prepare, drain, pause and resume callbacks are called when 869#snd_pcm_prepare(), #snd_pcm_drain(), #snd_pcm_pause(), and 870#snd_pcm_resume() are called. The poll_descriptors_count and 871poll_descriptors callbacks are used to return the multiple or dynamic 872poll descriptors as mentioned above. The poll_revents callback is 873used to modify poll events. If the driver needs to mangle the native 874poll events to proper poll events for PCM, you can do it in this 875callback. 876 877Finally, the dump callback is used to print the status of the plugin. 878 879The hw_params constraints can be defined via either 880#snd_pcm_ioplug_set_param_minmax() and #snd_pcm_ioplug_set_param_list() 881functions after calling #snd_pcm_ioplug_create(). 882The former defines the minimal and maximal acceptable values for the 883given hw_params parameter (SND_PCM_IOPLUG_HW_XXX). 884This function can't be used for the format parameter. The latter 885function specifies the available parameter values as the list. 886 887To clear the parameter constraints, call #snd_pcm_ioplug_params_reset() function. 888 889*/ 890 891/** 892 * \brief Create an ioplug instance 893 * \param ioplug the ioplug handle 894 * \param name name of PCM 895 * \param stream stream direction 896 * \param mode PCM open mode 897 * \return 0 if successful, or a negative error code 898 * 899 * Creates the ioplug instance. 900 * 901 * The callback is the mandatory field of ioplug handle. At least, start, stop and 902 * pointer callbacks must be set before calling this function. 903 * 904 */ 905int snd_pcm_ioplug_create(snd_pcm_ioplug_t *ioplug, const char *name, 906 snd_pcm_stream_t stream, int mode) 907{ 908 ioplug_priv_t *io; 909 int err; 910 snd_pcm_t *pcm; 911 912 assert(ioplug && ioplug->callback); 913 assert(ioplug->callback->start && 914 ioplug->callback->stop && 915 ioplug->callback->pointer); 916 917 /* We support 1.0.0 to current */ 918 if (ioplug->version < 0x010000 || 919 ioplug->version > SND_PCM_IOPLUG_VERSION) { 920 SNDERR("ioplug: Plugin version mismatch: 0x%x\n", 921 ioplug->version); 922 return -ENXIO; 923 } 924 925 io = calloc(1, sizeof(*io)); 926 if (! io) 927 return -ENOMEM; 928 929 io->data = ioplug; 930 ioplug->state = SND_PCM_STATE_OPEN; 931 ioplug->stream = stream; 932 933 err = snd_pcm_new(&pcm, SND_PCM_TYPE_IOPLUG, name, stream, mode); 934 if (err < 0) { 935 free(io); 936 return err; 937 } 938 939 ioplug->pcm = pcm; 940 pcm->ops = &snd_pcm_ioplug_ops; 941 pcm->fast_ops = &snd_pcm_ioplug_fast_ops; 942 pcm->private_data = io; 943 944 snd_pcm_set_hw_ptr(pcm, &ioplug->hw_ptr, -1, 0); 945 snd_pcm_set_appl_ptr(pcm, &ioplug->appl_ptr, -1, 0); 946 947 snd_pcm_ioplug_reinit_status(ioplug); 948 949 return 0; 950} 951 952/** 953 * \brief Delete the ioplug instance 954 * \param ioplug the ioplug handle 955 * \return 0 if successful, or a negative error code 956 */ 957int snd_pcm_ioplug_delete(snd_pcm_ioplug_t *ioplug) 958{ 959 return snd_pcm_close(ioplug->pcm); 960} 961 962 963/** 964 * \brief Reset ioplug parameters 965 * \param ioplug the ioplug handle 966 * 967 * Resets the all parameters for the given ioplug handle. 968 */ 969void snd_pcm_ioplug_params_reset(snd_pcm_ioplug_t *ioplug) 970{ 971 ioplug_priv_t *io = ioplug->pcm->private_data; 972 clear_io_params(io); 973} 974 975/** 976 * \brief Set parameter as the list 977 * \param ioplug the ioplug handle 978 * \param type parameter type 979 * \param num_list number of available values 980 * \param list the list of available values 981 * \return 0 if successful, or a negative error code 982 * 983 * Sets the parameter as the list. 984 * The available values of the given parameter type is restricted to the ones of the given list. 985 */ 986int snd_pcm_ioplug_set_param_list(snd_pcm_ioplug_t *ioplug, int type, unsigned int num_list, const unsigned int *list) 987{ 988 ioplug_priv_t *io = ioplug->pcm->private_data; 989 if (type < 0 && type >= SND_PCM_IOPLUG_HW_PARAMS) { 990 SNDERR("IOPLUG: invalid parameter type %d", type); 991 return -EINVAL; 992 } 993 if (type == SND_PCM_IOPLUG_HW_PERIODS) 994 io->params[type].integer = 1; 995 return snd_ext_parm_set_list(&io->params[type], num_list, list); 996} 997 998/** 999 * \brief Set parameter as the min/max values 1000 * \param ioplug the ioplug handle 1001 * \param type parameter type 1002 * \param min the minimum value 1003 * \param max the maximum value 1004 * \return 0 if successful, or a negative error code 1005 * 1006 * Sets the parameter as the min/max values. 1007 * The available values of the given parameter type is restricted between the given 1008 * minimum and maximum values. 1009 */ 1010int snd_pcm_ioplug_set_param_minmax(snd_pcm_ioplug_t *ioplug, int type, unsigned int min, unsigned int max) 1011{ 1012 ioplug_priv_t *io = ioplug->pcm->private_data; 1013 if (type < 0 && type >= SND_PCM_IOPLUG_HW_PARAMS) { 1014 SNDERR("IOPLUG: invalid parameter type %d", type); 1015 return -EINVAL; 1016 } 1017 if (type == SND_PCM_IOPLUG_HW_ACCESS || type == SND_PCM_IOPLUG_HW_FORMAT) { 1018 SNDERR("IOPLUG: invalid parameter type %d", type); 1019 return -EINVAL; 1020 } 1021 if (type == SND_PCM_IOPLUG_HW_PERIODS) 1022 io->params[type].integer = 1; 1023 return snd_ext_parm_set_minmax(&io->params[type], min, max); 1024} 1025 1026/** 1027 * \brief Reinitialize the poll and mmap status 1028 * \param ioplug the ioplug handle 1029 * \return 0 if successful, or a negative error code 1030 * 1031 * Reinitializes the poll and the mmap status of the PCM. 1032 * Call this function to propagate the status change in the ioplug instance to 1033 * its PCM internals. 1034 */ 1035int snd_pcm_ioplug_reinit_status(snd_pcm_ioplug_t *ioplug) 1036{ 1037 ioplug->pcm->poll_fd = ioplug->poll_fd; 1038 ioplug->pcm->poll_events = ioplug->poll_events; 1039 ioplug->pcm->monotonic = (ioplug->flags & SND_PCM_IOPLUG_FLAG_MONOTONIC) != 0; 1040 ioplug->pcm->mmap_rw = ioplug->mmap_rw; 1041 return 0; 1042} 1043 1044/** 1045 * \brief Get mmap area of ioplug 1046 * \param ioplug the ioplug handle 1047 * \return the mmap channel areas if available, or NULL 1048 * 1049 * Returns the mmap channel areas if available. When mmap_rw field is not set, 1050 * this function always returns NULL. 1051 */ 1052const snd_pcm_channel_area_t *snd_pcm_ioplug_mmap_areas(snd_pcm_ioplug_t *ioplug) 1053{ 1054 if (ioplug->mmap_rw) 1055 return snd_pcm_mmap_areas(ioplug->pcm); 1056 return NULL; 1057} 1058 1059/** 1060 * \brief Change the ioplug PCM status 1061 * \param ioplug the ioplug handle 1062 * \param state the PCM status 1063 * \return zero if successful or a negative error code 1064 * 1065 * Changes the PCM status of the ioplug to the given value. 1066 * This function can be used for external plugins to notify the status 1067 * change, e.g. XRUN. 1068 */ 1069int snd_pcm_ioplug_set_state(snd_pcm_ioplug_t *ioplug, snd_pcm_state_t state) 1070{ 1071 ioplug->state = state; 1072 return 0; 1073} 1074