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