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