1 /** 2 * \file pcm/pcm_dshare.c 3 * \ingroup PCM_Plugins 4 * \brief PCM Direct Sharing of Channels Plugin Interface 5 * \author Jaroslav Kysela <perex (at) perex.cz> 6 * \date 2003 7 */ 8 /* 9 * PCM - Direct Sharing of Channels 10 * Copyright (c) 2003 by Jaroslav Kysela <perex (at) perex.cz> 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 <stdio.h> 30 #include <stdlib.h> 31 #include <stddef.h> 32 #include <unistd.h> 33 #include <signal.h> 34 #include <string.h> 35 #include <fcntl.h> 36 #include <ctype.h> 37 #include <grp.h> 38 #include <sys/ioctl.h> 39 #include <sys/mman.h> 40 #include <sys/shm.h> 41 #include <sys/sem.h> 42 #include <sys/wait.h> 43 #include <sys/socket.h> 44 #include <sys/un.h> 45 #include <sys/mman.h> 46 #include "pcm_direct.h" 47 48 #ifndef PIC 49 /* entry for static linking */ 50 const char *_snd_module_pcm_dshare = ""; 51 #endif 52 53 #ifndef DOC_HIDDEN 54 /* start is pending - this state happens when rate plugin does a delayed commit */ 55 #define STATE_RUN_PENDING 1024 56 #endif 57 58 static void do_silence(snd_pcm_t *pcm) 59 { 60 snd_pcm_direct_t *dshare = pcm->private_data; 61 const snd_pcm_channel_area_t *dst_areas; 62 unsigned int chn, dchn, channels; 63 snd_pcm_format_t format; 64 65 dst_areas = snd_pcm_mmap_areas(dshare->spcm); 66 channels = dshare->channels; 67 format = dshare->shmptr->s.format; 68 for (chn = 0; chn < channels; chn++) { 69 dchn = dshare->bindings ? dshare->bindings[chn] : chn; 70 snd_pcm_area_silence(&dst_areas[dchn], 0, dshare->shmptr->s.buffer_size, format); 71 } 72 } 73 74 static void share_areas(snd_pcm_direct_t *dshare, 75 const snd_pcm_channel_area_t *src_areas, 76 const snd_pcm_channel_area_t *dst_areas, 77 snd_pcm_uframes_t src_ofs, 78 snd_pcm_uframes_t dst_ofs, 79 snd_pcm_uframes_t size) 80 { 81 unsigned int chn, dchn, channels; 82 snd_pcm_format_t format; 83 84 channels = dshare->channels; 85 format = dshare->shmptr->s.format; 86 if (dshare->interleaved) { 87 unsigned int fbytes = snd_pcm_format_physical_width(format) / 8; 88 memcpy(((char *)dst_areas[0].addr) + (dst_ofs * channels * fbytes), 89 ((char *)src_areas[0].addr) + (src_ofs * channels * fbytes), 90 size * channels * fbytes); 91 } else { 92 for (chn = 0; chn < channels; chn++) { 93 dchn = dshare->bindings ? dshare->bindings[chn] : chn; 94 snd_pcm_area_copy(&dst_areas[dchn], dst_ofs, &src_areas[chn], src_ofs, size, format); 95 96 } 97 } 98 } 99 100 /* 101 * synchronize shm ring buffer with hardware 102 */ 103 static void snd_pcm_dshare_sync_area(snd_pcm_t *pcm) 104 { 105 snd_pcm_direct_t *dshare = pcm->private_data; 106 snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size; 107 snd_pcm_uframes_t appl_ptr, size; 108 const snd_pcm_channel_area_t *src_areas, *dst_areas; 109 110 /* calculate the size to transfer */ 111 size = dshare->appl_ptr - dshare->last_appl_ptr; 112 if (! size) 113 return; 114 slave_hw_ptr = dshare->slave_hw_ptr; 115 /* don't write on the last active period - this area may be cleared 116 * by the driver during write operation... 117 */ 118 slave_hw_ptr -= slave_hw_ptr % dshare->slave_period_size; 119 slave_hw_ptr += dshare->slave_buffer_size; 120 if (dshare->slave_hw_ptr > dshare->slave_boundary) 121 slave_hw_ptr -= dshare->slave_boundary; 122 if (slave_hw_ptr < dshare->slave_appl_ptr) 123 slave_size = slave_hw_ptr + (dshare->slave_boundary - dshare->slave_appl_ptr); 124 else 125 slave_size = slave_hw_ptr - dshare->slave_appl_ptr; 126 if (slave_size < size) 127 size = slave_size; 128 if (! size) 129 return; 130 131 /* add sample areas here */ 132 src_areas = snd_pcm_mmap_areas(pcm); 133 dst_areas = snd_pcm_mmap_areas(dshare->spcm); 134 appl_ptr = dshare->last_appl_ptr % pcm->buffer_size; 135 dshare->last_appl_ptr += size; 136 dshare->last_appl_ptr %= pcm->boundary; 137 slave_appl_ptr = dshare->slave_appl_ptr % dshare->slave_buffer_size; 138 dshare->slave_appl_ptr += size; 139 dshare->slave_appl_ptr %= dshare->slave_boundary; 140 for (;;) { 141 snd_pcm_uframes_t transfer = size; 142 if (appl_ptr + transfer > pcm->buffer_size) 143 transfer = pcm->buffer_size - appl_ptr; 144 if (slave_appl_ptr + transfer > dshare->slave_buffer_size) 145 transfer = dshare->slave_buffer_size - slave_appl_ptr; 146 share_areas(dshare, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer); 147 size -= transfer; 148 if (! size) 149 break; 150 slave_appl_ptr += transfer; 151 slave_appl_ptr %= dshare->slave_buffer_size; 152 appl_ptr += transfer; 153 appl_ptr %= pcm->buffer_size; 154 } 155 } 156 157 /* 158 * synchronize hardware pointer (hw_ptr) with ours 159 */ 160 static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm) 161 { 162 snd_pcm_direct_t *dshare = pcm->private_data; 163 snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail; 164 snd_pcm_sframes_t diff; 165 166 switch (snd_pcm_state(dshare->spcm)) { 167 case SND_PCM_STATE_DISCONNECTED: 168 dshare->state = SNDRV_PCM_STATE_DISCONNECTED; 169 return -ENODEV; 170 default: 171 break; 172 } 173 if (dshare->slowptr) 174 snd_pcm_hwsync(dshare->spcm); 175 old_slave_hw_ptr = dshare->slave_hw_ptr; 176 slave_hw_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr; 177 diff = slave_hw_ptr - old_slave_hw_ptr; 178 if (diff == 0) /* fast path */ 179 return 0; 180 if (dshare->state != SND_PCM_STATE_RUNNING && 181 dshare->state != SND_PCM_STATE_DRAINING) 182 /* not really started yet - don't update hw_ptr */ 183 return 0; 184 if (diff < 0) { 185 slave_hw_ptr += dshare->slave_boundary; 186 diff = slave_hw_ptr - old_slave_hw_ptr; 187 } 188 dshare->hw_ptr += diff; 189 dshare->hw_ptr %= pcm->boundary; 190 // printf("sync ptr diff = %li\n", diff); 191 if (pcm->stop_threshold >= pcm->boundary) /* don't care */ 192 return 0; 193 avail = snd_pcm_mmap_playback_avail(pcm); 194 if (avail > dshare->avail_max) 195 dshare->avail_max = avail; 196 if (avail >= pcm->stop_threshold) { 197 snd_timer_stop(dshare->timer); 198 gettimestamp(&dshare->trigger_tstamp, pcm->monotonic); 199 if (dshare->state == SND_PCM_STATE_RUNNING) { 200 dshare->state = SND_PCM_STATE_XRUN; 201 return -EPIPE; 202 } 203 dshare->state = SND_PCM_STATE_SETUP; 204 /* clear queue to remove pending poll events */ 205 snd_pcm_direct_clear_timer_queue(dshare); 206 } 207 return 0; 208 } 209 210 /* 211 * plugin implementation 212 */ 213 214 static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status) 215 { 216 snd_pcm_direct_t *dshare = pcm->private_data; 217 218 switch (dshare->state) { 219 case SNDRV_PCM_STATE_DRAINING: 220 case SNDRV_PCM_STATE_RUNNING: 221 snd_pcm_dshare_sync_ptr(pcm); 222 break; 223 default: 224 break; 225 } 226 memset(status, 0, sizeof(*status)); 227 status->state = snd_pcm_state(dshare->spcm); 228 status->trigger_tstamp = dshare->trigger_tstamp; 229 gettimestamp(&status->tstamp, pcm->monotonic); 230 status->avail = snd_pcm_mmap_playback_avail(pcm); 231 status->avail_max = status->avail > dshare->avail_max ? status->avail : dshare->avail_max; 232 dshare->avail_max = 0; 233 return 0; 234 } 235 236 static snd_pcm_state_t snd_pcm_dshare_state(snd_pcm_t *pcm) 237 { 238 snd_pcm_direct_t *dshare = pcm->private_data; 239 switch (snd_pcm_state(dshare->spcm)) { 240 case SND_PCM_STATE_SUSPENDED: 241 return SND_PCM_STATE_SUSPENDED; 242 case SND_PCM_STATE_DISCONNECTED: 243 return SND_PCM_STATE_DISCONNECTED; 244 default: 245 break; 246 } 247 if (dshare->state == STATE_RUN_PENDING) 248 return SNDRV_PCM_STATE_RUNNING; 249 return dshare->state; 250 } 251 252 static int snd_pcm_dshare_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) 253 { 254 snd_pcm_direct_t *dshare = pcm->private_data; 255 int err; 256 257 switch (dshare->state) { 258 case SNDRV_PCM_STATE_DRAINING: 259 case SNDRV_PCM_STATE_RUNNING: 260 err = snd_pcm_dshare_sync_ptr(pcm); 261 if (err < 0) 262 return err; 263 /* fallthru */ 264 case SNDRV_PCM_STATE_PREPARED: 265 case SNDRV_PCM_STATE_SUSPENDED: 266 case STATE_RUN_PENDING: 267 *delayp = snd_pcm_mmap_playback_hw_avail(pcm); 268 return 0; 269 case SNDRV_PCM_STATE_XRUN: 270 return -EPIPE; 271 case SNDRV_PCM_STATE_DISCONNECTED: 272 return -ENODEV; 273 default: 274 return -EBADFD; 275 } 276 } 277 278 static int snd_pcm_dshare_hwsync(snd_pcm_t *pcm) 279 { 280 snd_pcm_direct_t *dshare = pcm->private_data; 281 282 switch(dshare->state) { 283 case SNDRV_PCM_STATE_DRAINING: 284 case SNDRV_PCM_STATE_RUNNING: 285 return snd_pcm_dshare_sync_ptr(pcm); 286 case SNDRV_PCM_STATE_PREPARED: 287 case SNDRV_PCM_STATE_SUSPENDED: 288 return 0; 289 case SNDRV_PCM_STATE_XRUN: 290 return -EPIPE; 291 case SNDRV_PCM_STATE_DISCONNECTED: 292 return -ENODEV; 293 default: 294 return -EBADFD; 295 } 296 } 297 298 static int snd_pcm_dshare_prepare(snd_pcm_t *pcm) 299 { 300 snd_pcm_direct_t *dshare = pcm->private_data; 301 302 snd_pcm_direct_check_interleave(dshare, pcm); 303 dshare->state = SND_PCM_STATE_PREPARED; 304 dshare->appl_ptr = dshare->last_appl_ptr = 0; 305 dshare->hw_ptr = 0; 306 return snd_pcm_direct_set_timer_params(dshare); 307 } 308 309 static int snd_pcm_dshare_reset(snd_pcm_t *pcm) 310 { 311 snd_pcm_direct_t *dshare = pcm->private_data; 312 dshare->hw_ptr %= pcm->period_size; 313 dshare->appl_ptr = dshare->last_appl_ptr = dshare->hw_ptr; 314 dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr; 315 return 0; 316 } 317 318 static int snd_pcm_dshare_start_timer(snd_pcm_direct_t *dshare) 319 { 320 int err; 321 322 snd_pcm_hwsync(dshare->spcm); 323 dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr; 324 err = snd_timer_start(dshare->timer); 325 if (err < 0) 326 return err; 327 dshare->state = SND_PCM_STATE_RUNNING; 328 return 0; 329 } 330 331 static int snd_pcm_dshare_start(snd_pcm_t *pcm) 332 { 333 snd_pcm_direct_t *dshare = pcm->private_data; 334 snd_pcm_sframes_t avail; 335 int err; 336 337 if (dshare->state != SND_PCM_STATE_PREPARED) 338 return -EBADFD; 339 avail = snd_pcm_mmap_playback_hw_avail(pcm); 340 if (avail == 0) 341 dshare->state = STATE_RUN_PENDING; 342 else if (avail < 0) 343 return 0; 344 else { 345 if ((err = snd_pcm_dshare_start_timer(dshare)) < 0) 346 return err; 347 snd_pcm_dshare_sync_area(pcm); 348 } 349 gettimestamp(&dshare->trigger_tstamp, pcm->monotonic); 350 return 0; 351 } 352 353 static int snd_pcm_dshare_drop(snd_pcm_t *pcm) 354 { 355 snd_pcm_direct_t *dshare = pcm->private_data; 356 if (dshare->state == SND_PCM_STATE_OPEN) 357 return -EBADFD; 358 snd_pcm_direct_timer_stop(dshare); 359 do_silence(pcm); 360 dshare->state = SND_PCM_STATE_SETUP; 361 return 0; 362 } 363 364 static int snd_pcm_dshare_drain(snd_pcm_t *pcm) 365 { 366 snd_pcm_direct_t *dshare = pcm->private_data; 367 snd_pcm_uframes_t stop_threshold; 368 int err; 369 370 if (dshare->state == SND_PCM_STATE_OPEN) 371 return -EBADFD; 372 if (pcm->mode & SND_PCM_NONBLOCK) 373 return -EAGAIN; 374 if (dshare->state == SND_PCM_STATE_PREPARED) { 375 if (snd_pcm_mmap_playback_hw_avail(pcm) > 0) 376 snd_pcm_dshare_start(pcm); 377 else { 378 snd_pcm_dshare_drop(pcm); 379 return 0; 380 } 381 } 382 383 if (dshare->state == SND_PCM_STATE_XRUN) { 384 snd_pcm_dshare_drop(pcm); 385 return 0; 386 } 387 388 stop_threshold = pcm->stop_threshold; 389 if (pcm->stop_threshold > pcm->buffer_size) 390 pcm->stop_threshold = pcm->buffer_size; 391 dshare->state = SND_PCM_STATE_DRAINING; 392 do { 393 err = snd_pcm_dshare_sync_ptr(pcm); 394 if (err < 0) { 395 snd_pcm_dshare_drop(pcm); 396 break; 397 } 398 if (dshare->state == SND_PCM_STATE_DRAINING) { 399 snd_pcm_dshare_sync_area(pcm); 400 snd_pcm_wait_nocheck(pcm, -1); 401 snd_pcm_direct_clear_timer_queue(dshare); /* force poll to wait */ 402 } 403 } while (dshare->state == SND_PCM_STATE_DRAINING); 404 pcm->stop_threshold = stop_threshold; 405 return 0; 406 } 407 408 static int snd_pcm_dshare_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED) 409 { 410 return -EIO; 411 } 412 413 static snd_pcm_sframes_t snd_pcm_dshare_rewindable(snd_pcm_t *pcm) 414 { 415 return snd_pcm_mmap_playback_hw_avail(pcm); 416 } 417 418 static snd_pcm_sframes_t snd_pcm_dshare_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 419 { 420 snd_pcm_sframes_t avail; 421 422 avail = snd_pcm_mmap_playback_hw_avail(pcm); 423 if (avail < 0) 424 return 0; 425 if (frames > (snd_pcm_uframes_t)avail) 426 frames = avail; 427 snd_pcm_mmap_appl_backward(pcm, frames); 428 return frames; 429 } 430 431 static snd_pcm_sframes_t snd_pcm_dshare_forwardable(snd_pcm_t *pcm) 432 { 433 return snd_pcm_mmap_playback_avail(pcm); 434 } 435 436 static snd_pcm_sframes_t snd_pcm_dshare_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 437 { 438 snd_pcm_sframes_t avail; 439 440 avail = snd_pcm_mmap_playback_avail(pcm); 441 if (avail < 0) 442 return 0; 443 if (frames > (snd_pcm_uframes_t)avail) 444 frames = avail; 445 snd_pcm_mmap_appl_forward(pcm, frames); 446 return frames; 447 } 448 449 static snd_pcm_sframes_t snd_pcm_dshare_readi(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED) 450 { 451 return -ENODEV; 452 } 453 454 static snd_pcm_sframes_t snd_pcm_dshare_readn(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED) 455 { 456 return -ENODEV; 457 } 458 459 static int snd_pcm_dshare_close(snd_pcm_t *pcm) 460 { 461 snd_pcm_direct_t *dshare = pcm->private_data; 462 463 if (dshare->timer) 464 snd_timer_close(dshare->timer); 465 do_silence(pcm); 466 snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT); 467 dshare->shmptr->u.dshare.chn_mask &= ~dshare->u.dshare.chn_mask; 468 snd_pcm_close(dshare->spcm); 469 if (dshare->server) 470 snd_pcm_direct_server_discard(dshare); 471 if (dshare->client) 472 snd_pcm_direct_client_discard(dshare); 473 if (snd_pcm_direct_shm_discard(dshare)) 474 snd_pcm_direct_semaphore_discard(dshare); 475 else 476 snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT); 477 free(dshare->bindings); 478 pcm->private_data = NULL; 479 free(dshare); 480 return 0; 481 } 482 483 static snd_pcm_sframes_t snd_pcm_dshare_mmap_commit(snd_pcm_t *pcm, 484 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED, 485 snd_pcm_uframes_t size) 486 { 487 snd_pcm_direct_t *dshare = pcm->private_data; 488 int err; 489 490 switch (snd_pcm_state(dshare->spcm)) { 491 case SND_PCM_STATE_XRUN: 492 return -EPIPE; 493 case SND_PCM_STATE_SUSPENDED: 494 return -ESTRPIPE; 495 default: 496 break; 497 } 498 if (! size) 499 return 0; 500 snd_pcm_mmap_appl_forward(pcm, size); 501 if (dshare->state == STATE_RUN_PENDING) { 502 if ((err = snd_pcm_dshare_start_timer(dshare)) < 0) 503 return err; 504 } else if (dshare->state == SND_PCM_STATE_RUNNING || 505 dshare->state == SND_PCM_STATE_DRAINING) 506 snd_pcm_dshare_sync_ptr(pcm); 507 if (dshare->state == SND_PCM_STATE_RUNNING || 508 dshare->state == SND_PCM_STATE_DRAINING) { 509 /* ok, we commit the changes after the validation of area */ 510 /* it's intended, although the result might be crappy */ 511 snd_pcm_dshare_sync_area(pcm); 512 /* clear timer queue to avoid a bogus return from poll */ 513 if (snd_pcm_mmap_playback_avail(pcm) < pcm->avail_min) 514 snd_pcm_direct_clear_timer_queue(dshare); 515 } 516 return size; 517 } 518 519 static snd_pcm_sframes_t snd_pcm_dshare_avail_update(snd_pcm_t *pcm) 520 { 521 snd_pcm_direct_t *dshare = pcm->private_data; 522 523 if (dshare->state == SND_PCM_STATE_RUNNING || 524 dshare->state == SND_PCM_STATE_DRAINING) 525 snd_pcm_dshare_sync_ptr(pcm); 526 return snd_pcm_mmap_playback_avail(pcm); 527 } 528 529 static int snd_pcm_dshare_htimestamp(snd_pcm_t *pcm, 530 snd_pcm_uframes_t *avail, 531 snd_htimestamp_t *tstamp) 532 { 533 snd_pcm_direct_t *dshare = pcm->private_data; 534 snd_pcm_uframes_t avail1; 535 int ok = 0; 536 537 while (1) { 538 if (dshare->state == SND_PCM_STATE_RUNNING || 539 dshare->state == SND_PCM_STATE_DRAINING) 540 snd_pcm_dshare_sync_ptr(pcm); 541 avail1 = snd_pcm_mmap_playback_avail(pcm); 542 if (ok && *avail == avail1) 543 break; 544 *avail = avail1; 545 *tstamp = snd_pcm_hw_fast_tstamp(pcm); 546 } 547 return 0; 548 } 549 550 static void snd_pcm_dshare_dump(snd_pcm_t *pcm, snd_output_t *out) 551 { 552 snd_pcm_direct_t *dshare = pcm->private_data; 553 554 snd_output_printf(out, "Direct Share PCM\n"); 555 if (pcm->setup) { 556 snd_output_printf(out, "Its setup is:\n"); 557 snd_pcm_dump_setup(pcm, out); 558 } 559 if (dshare->spcm) 560 snd_pcm_dump(dshare->spcm, out); 561 } 562 563 static const snd_pcm_ops_t snd_pcm_dshare_ops = { 564 .close = snd_pcm_dshare_close, 565 .info = snd_pcm_direct_info, 566 .hw_refine = snd_pcm_direct_hw_refine, 567 .hw_params = snd_pcm_direct_hw_params, 568 .hw_free = snd_pcm_direct_hw_free, 569 .sw_params = snd_pcm_direct_sw_params, 570 .channel_info = snd_pcm_direct_channel_info, 571 .dump = snd_pcm_dshare_dump, 572 .nonblock = snd_pcm_direct_nonblock, 573 .async = snd_pcm_direct_async, 574 .mmap = snd_pcm_direct_mmap, 575 .munmap = snd_pcm_direct_munmap, 576 }; 577 578 static const snd_pcm_fast_ops_t snd_pcm_dshare_fast_ops = { 579 .status = snd_pcm_dshare_status, 580 .state = snd_pcm_dshare_state, 581 .hwsync = snd_pcm_dshare_hwsync, 582 .delay = snd_pcm_dshare_delay, 583 .prepare = snd_pcm_dshare_prepare, 584 .reset = snd_pcm_dshare_reset, 585 .start = snd_pcm_dshare_start, 586 .drop = snd_pcm_dshare_drop, 587 .drain = snd_pcm_dshare_drain, 588 .pause = snd_pcm_dshare_pause, 589 .rewindable = snd_pcm_dshare_rewindable, 590 .rewind = snd_pcm_dshare_rewind, 591 .forwardable = snd_pcm_dshare_forwardable, 592 .forward = snd_pcm_dshare_forward, 593 .resume = snd_pcm_direct_resume, 594 .link = NULL, 595 .link_slaves = NULL, 596 .unlink = NULL, 597 .writei = snd_pcm_mmap_writei, 598 .writen = snd_pcm_mmap_writen, 599 .readi = snd_pcm_dshare_readi, 600 .readn = snd_pcm_dshare_readn, 601 .avail_update = snd_pcm_dshare_avail_update, 602 .mmap_commit = snd_pcm_dshare_mmap_commit, 603 .htimestamp = snd_pcm_dshare_htimestamp, 604 .poll_descriptors = NULL, 605 .poll_descriptors_count = NULL, 606 .poll_revents = snd_pcm_direct_poll_revents, 607 }; 608 609 /** 610 * \brief Creates a new dshare PCM 611 * \param pcmp Returns created PCM handle 612 * \param name Name of PCM 613 * \param opts Direct PCM configurations 614 * \param params Parameters for slave 615 * \param root Configuration root 616 * \param sconf Slave configuration 617 * \param stream PCM Direction (stream) 618 * \param mode PCM Mode 619 * \retval zero on success otherwise a negative error code 620 * \warning Using of this function might be dangerous in the sense 621 * of compatibility reasons. The prototype might be freely 622 * changed in future. 623 */ 624 int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name, 625 struct snd_pcm_direct_open_conf *opts, 626 struct slave_params *params, 627 snd_config_t *root, snd_config_t *sconf, 628 snd_pcm_stream_t stream, int mode) 629 { 630 snd_pcm_t *pcm = NULL, *spcm = NULL; 631 snd_pcm_direct_t *dshare = NULL; 632 int ret, first_instance; 633 unsigned int chn; 634 int fail_sem_loop = 10; 635 636 assert(pcmp); 637 638 if (stream != SND_PCM_STREAM_PLAYBACK) { 639 SNDERR("The dshare plugin supports only playback stream"); 640 return -EINVAL; 641 } 642 643 dshare = calloc(1, sizeof(snd_pcm_direct_t)); 644 if (!dshare) { 645 ret = -ENOMEM; 646 goto _err_nosem; 647 } 648 649 ret = snd_pcm_direct_parse_bindings(dshare, params, opts->bindings); 650 if (ret < 0) 651 goto _err_nosem; 652 653 if (!dshare->bindings) { 654 SNDERR("dshare: specify bindings!!!"); 655 ret = -EINVAL; 656 goto _err_nosem; 657 } 658 659 dshare->ipc_key = opts->ipc_key; 660 dshare->ipc_perm = opts->ipc_perm; 661 dshare->ipc_gid = opts->ipc_gid; 662 dshare->semid = -1; 663 dshare->shmid = -1; 664 665 ret = snd_pcm_new(&pcm, dshare->type = SND_PCM_TYPE_DSHARE, name, stream, mode); 666 if (ret < 0) 667 goto _err_nosem; 668 669 while (1) { 670 ret = snd_pcm_direct_semaphore_create_or_connect(dshare); 671 if (ret < 0) { 672 SNDERR("unable to create IPC semaphore"); 673 goto _err_nosem; 674 } 675 676 ret = snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT); 677 if (ret < 0) { 678 snd_pcm_direct_semaphore_discard(dshare); 679 if (--fail_sem_loop <= 0) 680 goto _err_nosem; 681 continue; 682 } 683 break; 684 } 685 686 first_instance = ret = snd_pcm_direct_shm_create_or_connect(dshare); 687 if (ret < 0) { 688 SNDERR("unable to create IPC shm instance"); 689 goto _err; 690 } 691 692 pcm->ops = &snd_pcm_dshare_ops; 693 pcm->fast_ops = &snd_pcm_dshare_fast_ops; 694 pcm->private_data = dshare; 695 dshare->state = SND_PCM_STATE_OPEN; 696 dshare->slowptr = opts->slowptr; 697 dshare->max_periods = opts->max_periods; 698 dshare->sync_ptr = snd_pcm_dshare_sync_ptr; 699 700 if (first_instance) { 701 /* recursion is already checked in 702 snd_pcm_direct_get_slave_ipc_offset() */ 703 ret = snd_pcm_open_slave(&spcm, root, sconf, stream, 704 mode | SND_PCM_NONBLOCK, NULL); 705 if (ret < 0) { 706 SNDERR("unable to open slave"); 707 goto _err; 708 } 709 710 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) { 711 SNDERR("dshare plugin can be only connected to hw plugin"); 712 goto _err; 713 } 714 715 ret = snd_pcm_direct_initialize_slave(dshare, spcm, params); 716 if (ret < 0) { 717 SNDERR("unable to initialize slave"); 718 goto _err; 719 } 720 721 dshare->spcm = spcm; 722 723 if (dshare->shmptr->use_server) { 724 ret = snd_pcm_direct_server_create(dshare); 725 if (ret < 0) { 726 SNDERR("unable to create server"); 727 goto _err; 728 } 729 } 730 731 dshare->shmptr->type = spcm->type; 732 } else { 733 if (dshare->shmptr->use_server) { 734 /* up semaphore to avoid deadlock */ 735 snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT); 736 ret = snd_pcm_direct_client_connect(dshare); 737 if (ret < 0) { 738 SNDERR("unable to connect client"); 739 goto _err_nosem; 740 } 741 742 snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT); 743 ret = snd_pcm_direct_open_secondary_client(&spcm, dshare, "dshare_client"); 744 if (ret < 0) 745 goto _err; 746 747 } else { 748 749 ret = snd_pcm_open_slave(&spcm, root, sconf, stream, 750 mode | SND_PCM_NONBLOCK | 751 SND_PCM_APPEND, 752 NULL); 753 if (ret < 0) { 754 SNDERR("unable to open slave"); 755 goto _err; 756 } 757 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) { 758 SNDERR("dshare plugin can be only connected to hw plugin"); 759 ret = -EINVAL; 760 goto _err; 761 } 762 763 ret = snd_pcm_direct_initialize_secondary_slave(dshare, spcm, params); 764 if (ret < 0) { 765 SNDERR("unable to initialize slave"); 766 goto _err; 767 } 768 } 769 770 dshare->spcm = spcm; 771 } 772 773 for (chn = 0; chn < dshare->channels; chn++) 774 dshare->u.dshare.chn_mask |= (1ULL<<dshare->bindings[chn]); 775 if (dshare->shmptr->u.dshare.chn_mask & dshare->u.dshare.chn_mask) { 776 SNDERR("destination channel specified in bindings is already used"); 777 dshare->u.dshare.chn_mask = 0; 778 ret = -EINVAL; 779 goto _err; 780 } 781 dshare->shmptr->u.dshare.chn_mask |= dshare->u.dshare.chn_mask; 782 783 ret = snd_pcm_direct_initialize_poll_fd(dshare); 784 if (ret < 0) { 785 SNDERR("unable to initialize poll_fd"); 786 goto _err; 787 } 788 789 pcm->poll_fd = dshare->poll_fd; 790 pcm->poll_events = POLLIN; /* it's different than other plugins */ 791 792 pcm->mmap_rw = 1; 793 snd_pcm_set_hw_ptr(pcm, &dshare->hw_ptr, -1, 0); 794 snd_pcm_set_appl_ptr(pcm, &dshare->appl_ptr, -1, 0); 795 796 snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT); 797 798 *pcmp = pcm; 799 return 0; 800 801 _err: 802 if (dshare->shmptr) 803 dshare->shmptr->u.dshare.chn_mask &= ~dshare->u.dshare.chn_mask; 804 if (dshare->timer) 805 snd_timer_close(dshare->timer); 806 if (dshare->server) 807 snd_pcm_direct_server_discard(dshare); 808 if (dshare->client) 809 snd_pcm_direct_client_discard(dshare); 810 if (spcm) 811 snd_pcm_close(spcm); 812 if (dshare->shmid >= 0) 813 snd_pcm_direct_shm_discard(dshare); 814 if (snd_pcm_direct_semaphore_discard(dshare) < 0) 815 snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT); 816 _err_nosem: 817 if (dshare) { 818 free(dshare->bindings); 819 free(dshare); 820 } 821 if (pcm) 822 snd_pcm_free(pcm); 823 return ret; 824 } 825 826 /*! \page pcm_plugins 827 828 \section pcm_plugins_dshare Plugin: dshare 829 830 This plugin provides sharing channels. 831 Unlike \ref pcm_plugins_share "share plugin", this plugin doesn't need 832 the explicit server program but accesses the shared buffer concurrently 833 from each client as well as \ref pcm_plugins_dmix "dmix" and 834 \ref pcm_plugins_dsnoop "dsnoop" plugins do. 835 The parameters below are almost identical with these plugins. 836 837 \code 838 pcm.name { 839 type dshare # Direct sharing 840 ipc_key INT # unique IPC key 841 ipc_key_add_uid BOOL # add current uid to unique IPC key 842 ipc_perm INT # IPC permissions (octal, default 0600) 843 slave STR 844 # or 845 slave { # Slave definition 846 pcm STR # slave PCM name 847 # or 848 pcm { } # slave PCM definition 849 format STR # format definition 850 rate INT # rate definition 851 channels INT 852 period_time INT # in usec 853 # or 854 period_size INT # in bytes 855 buffer_time INT # in usec 856 # or 857 buffer_size INT # in bytes 858 periods INT # when buffer_size or buffer_time is not specified 859 } 860 bindings { # note: this is client independent!!! 861 N INT # maps slave channel to client channel N 862 } 863 slowptr BOOL # slow but more precise pointer updates 864 } 865 \endcode 866 867 \subsection pcm_plugins_dshare_funcref Function reference 868 869 <UL> 870 <LI>snd_pcm_dshare_open() 871 <LI>_snd_pcm_dshare_open() 872 </UL> 873 874 */ 875 876 /** 877 * \brief Creates a new dshare PCM 878 * \param pcmp Returns created PCM handle 879 * \param name Name of PCM 880 * \param root Root configuration node 881 * \param conf Configuration node with dshare PCM description 882 * \param stream PCM Stream 883 * \param mode PCM Mode 884 * \warning Using of this function might be dangerous in the sense 885 * of compatibility reasons. The prototype might be freely 886 * changed in future. 887 */ 888 int _snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name, 889 snd_config_t *root, snd_config_t *conf, 890 snd_pcm_stream_t stream, int mode) 891 { 892 snd_config_t *sconf; 893 struct slave_params params; 894 struct snd_pcm_direct_open_conf dopen; 895 int bsize, psize; 896 int err; 897 898 err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen); 899 if (err < 0) 900 return err; 901 902 /* the default settings, it might be invalid for some hardware */ 903 params.format = SND_PCM_FORMAT_S16; 904 params.rate = 48000; 905 params.channels = 2; 906 params.period_time = -1; 907 params.buffer_time = -1; 908 bsize = psize = -1; 909 params.periods = 3; 910 err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8, 911 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, ¶ms.format, 912 SND_PCM_HW_PARAM_RATE, 0, ¶ms.rate, 913 SND_PCM_HW_PARAM_CHANNELS, 0, ¶ms.channels, 914 SND_PCM_HW_PARAM_PERIOD_TIME, 0, ¶ms.period_time, 915 SND_PCM_HW_PARAM_BUFFER_TIME, 0, ¶ms.buffer_time, 916 SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize, 917 SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize, 918 SND_PCM_HW_PARAM_PERIODS, 0, ¶ms.periods); 919 if (err < 0) 920 return err; 921 922 /* set a reasonable default */ 923 if (psize == -1 && params.period_time == -1) 924 params.period_time = 125000; /* 0.125 seconds */ 925 926 if (params.format == -2) 927 params.format = SND_PCM_FORMAT_UNKNOWN; 928 929 params.period_size = psize; 930 params.buffer_size = bsize; 931 932 err = snd_pcm_dshare_open(pcmp, name, &dopen, ¶ms, 933 root, sconf, stream, mode); 934 snd_config_delete(sconf); 935 return err; 936 } 937 #ifndef DOC_HIDDEN 938 SND_DLSYM_BUILD_VERSION(_snd_pcm_dshare_open, SND_PCM_DLSYM_VERSION); 939 #endif 940