1 /**
2 * \file pcm/pcm_dsnoop.c
3 * \ingroup PCM_Plugins
4 * \brief PCM Capture Stream Snooping (dsnoop) Plugin Interface
5 * \author Jaroslav Kysela <perex@perex.cz>
6 * \date 2003
7 */
8 /*
9 * PCM - Capture Stream Snooping
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_dsnoop = "";
51 #endif
52
53 /*
54 *
55 */
56
snoop_timestamp(snd_pcm_t * pcm)57 static int snoop_timestamp(snd_pcm_t *pcm)
58 {
59 snd_pcm_direct_t *dsnoop = pcm->private_data;
60 snd_pcm_uframes_t ptr1 = -2LL /* invalid value */, ptr2;
61
62 /* loop is required to sync hw.ptr with timestamp */
63 while (1) {
64 ptr2 = *dsnoop->spcm->hw.ptr;
65 if (ptr1 == ptr2)
66 break;
67 ptr1 = ptr2;
68 dsnoop->update_tstamp = snd_pcm_hw_fast_tstamp(dsnoop->spcm);
69 }
70 dsnoop->slave_hw_ptr = ptr1;
71 return 0;
72 }
73
snoop_areas(snd_pcm_direct_t * dsnoop,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)74 static void snoop_areas(snd_pcm_direct_t *dsnoop,
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, schn, channels;
82 snd_pcm_format_t format;
83
84 channels = dsnoop->channels;
85 format = dsnoop->shmptr->s.format;
86 if (dsnoop->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 schn = dsnoop->bindings ? dsnoop->bindings[chn] : chn;
94 snd_pcm_area_copy(&dst_areas[chn], dst_ofs, &src_areas[schn], src_ofs, size, format);
95 }
96 }
97 }
98
99 /*
100 * synchronize shm ring buffer with hardware
101 */
snd_pcm_dsnoop_sync_area(snd_pcm_t * pcm,snd_pcm_uframes_t slave_hw_ptr,snd_pcm_uframes_t size)102 static void snd_pcm_dsnoop_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr, snd_pcm_uframes_t size)
103 {
104 snd_pcm_direct_t *dsnoop = pcm->private_data;
105 snd_pcm_uframes_t hw_ptr = dsnoop->hw_ptr;
106 snd_pcm_uframes_t transfer;
107 const snd_pcm_channel_area_t *src_areas, *dst_areas;
108
109 /* add sample areas here */
110 dst_areas = snd_pcm_mmap_areas(pcm);
111 src_areas = snd_pcm_mmap_areas(dsnoop->spcm);
112 hw_ptr %= pcm->buffer_size;
113 slave_hw_ptr %= dsnoop->slave_buffer_size;
114 while (size > 0) {
115 transfer = hw_ptr + size > pcm->buffer_size ? pcm->buffer_size - hw_ptr : size;
116 transfer = slave_hw_ptr + transfer > dsnoop->slave_buffer_size ?
117 dsnoop->slave_buffer_size - slave_hw_ptr : transfer;
118 size -= transfer;
119 snoop_areas(dsnoop, src_areas, dst_areas, slave_hw_ptr, hw_ptr, transfer);
120 slave_hw_ptr += transfer;
121 slave_hw_ptr %= dsnoop->slave_buffer_size;
122 hw_ptr += transfer;
123 hw_ptr %= pcm->buffer_size;
124 }
125 }
126
127 /*
128 * synchronize hardware pointer (hw_ptr) with ours
129 */
snd_pcm_dsnoop_sync_ptr(snd_pcm_t * pcm)130 static int snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
131 {
132 snd_pcm_direct_t *dsnoop = pcm->private_data;
133 snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
134 snd_pcm_sframes_t diff;
135 int err;
136
137 switch (snd_pcm_state(dsnoop->spcm)) {
138 case SND_PCM_STATE_DISCONNECTED:
139 dsnoop->state = SNDRV_PCM_STATE_DISCONNECTED;
140 return -ENODEV;
141 case SND_PCM_STATE_XRUN:
142 if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
143 return err;
144 break;
145 default:
146 break;
147 }
148 if (snd_pcm_direct_client_chk_xrun(dsnoop, pcm))
149 return -EPIPE;
150 if (dsnoop->slowptr)
151 snd_pcm_hwsync(dsnoop->spcm);
152 old_slave_hw_ptr = dsnoop->slave_hw_ptr;
153 snoop_timestamp(pcm);
154 slave_hw_ptr = dsnoop->slave_hw_ptr;
155 diff = pcm_frame_diff(slave_hw_ptr, old_slave_hw_ptr, dsnoop->slave_boundary);
156 if (diff == 0) /* fast path */
157 return 0;
158 snd_pcm_dsnoop_sync_area(pcm, old_slave_hw_ptr, diff);
159 dsnoop->hw_ptr += diff;
160 dsnoop->hw_ptr %= pcm->boundary;
161 // printf("sync ptr diff = %li\n", diff);
162 if (pcm->stop_threshold >= pcm->boundary) /* don't care */
163 return 0;
164 if ((avail = snd_pcm_mmap_capture_avail(pcm)) >= pcm->stop_threshold) {
165 gettimestamp(&dsnoop->trigger_tstamp, pcm->tstamp_type);
166 dsnoop->state = SND_PCM_STATE_XRUN;
167 dsnoop->avail_max = avail;
168 return -EPIPE;
169 }
170 if (avail > dsnoop->avail_max)
171 dsnoop->avail_max = avail;
172 return 0;
173 }
174
175 /*
176 * plugin implementation
177 */
178
snd_pcm_dsnoop_status(snd_pcm_t * pcm,snd_pcm_status_t * status)179 static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
180 {
181 snd_pcm_direct_t *dsnoop = pcm->private_data;
182 snd_pcm_state_t state;
183
184 switch(dsnoop->state) {
185 case SNDRV_PCM_STATE_DRAINING:
186 case SNDRV_PCM_STATE_RUNNING:
187 snd_pcm_dsnoop_sync_ptr(pcm);
188 break;
189 default:
190 break;
191 }
192 memset(status, 0, sizeof(*status));
193 snd_pcm_status(dsnoop->spcm, status);
194 state = snd_pcm_state(dsnoop->spcm);
195 status->state = state == SND_PCM_STATE_RUNNING ? dsnoop->state : state;
196 status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */
197 status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */
198 status->trigger_tstamp = dsnoop->trigger_tstamp;
199 status->avail = snd_pcm_mmap_capture_avail(pcm);
200 status->avail_max = status->avail > dsnoop->avail_max ? status->avail : dsnoop->avail_max;
201 dsnoop->avail_max = 0;
202 status->delay = snd_pcm_mmap_capture_delay(pcm);
203 return 0;
204 }
205
snd_pcm_dsnoop_state(snd_pcm_t * pcm)206 static snd_pcm_state_t snd_pcm_dsnoop_state(snd_pcm_t *pcm)
207 {
208 snd_pcm_direct_t *dsnoop = pcm->private_data;
209 int err;
210 snd_pcm_state_t state;
211 state = snd_pcm_state(dsnoop->spcm);
212 switch (state) {
213 case SND_PCM_STATE_SUSPENDED:
214 case SND_PCM_STATE_DISCONNECTED:
215 dsnoop->state = state;
216 return state;
217 case SND_PCM_STATE_XRUN:
218 if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
219 return err;
220 break;
221 default:
222 break;
223 }
224 snd_pcm_direct_client_chk_xrun(dsnoop, pcm);
225 return dsnoop->state;
226 }
227
snd_pcm_dsnoop_delay(snd_pcm_t * pcm,snd_pcm_sframes_t * delayp)228 static int snd_pcm_dsnoop_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
229 {
230 snd_pcm_direct_t *dsnoop = pcm->private_data;
231 int err;
232
233 switch(dsnoop->state) {
234 case SNDRV_PCM_STATE_DRAINING:
235 case SNDRV_PCM_STATE_RUNNING:
236 err = snd_pcm_dsnoop_sync_ptr(pcm);
237 if (err < 0)
238 return err;
239 /* Fall through */
240 case SNDRV_PCM_STATE_PREPARED:
241 case SNDRV_PCM_STATE_SUSPENDED:
242 *delayp = snd_pcm_mmap_capture_avail(pcm);
243 return 0;
244 case SNDRV_PCM_STATE_XRUN:
245 return -EPIPE;
246 case SNDRV_PCM_STATE_DISCONNECTED:
247 return -ENODEV;
248 default:
249 return -EBADFD;
250 }
251 }
252
snd_pcm_dsnoop_hwsync(snd_pcm_t * pcm)253 static int snd_pcm_dsnoop_hwsync(snd_pcm_t *pcm)
254 {
255 snd_pcm_direct_t *dsnoop = pcm->private_data;
256
257 switch(dsnoop->state) {
258 case SNDRV_PCM_STATE_DRAINING:
259 case SNDRV_PCM_STATE_RUNNING:
260 return snd_pcm_dsnoop_sync_ptr(pcm);
261 case SNDRV_PCM_STATE_PREPARED:
262 case SNDRV_PCM_STATE_SUSPENDED:
263 return 0;
264 case SNDRV_PCM_STATE_XRUN:
265 return -EPIPE;
266 case SNDRV_PCM_STATE_DISCONNECTED:
267 return -ENODEV;
268 default:
269 return -EBADFD;
270 }
271 }
272
snd_pcm_dsnoop_reset(snd_pcm_t * pcm)273 static int snd_pcm_dsnoop_reset(snd_pcm_t *pcm)
274 {
275 snd_pcm_direct_t *dsnoop = pcm->private_data;
276 dsnoop->hw_ptr %= pcm->period_size;
277 dsnoop->appl_ptr = dsnoop->hw_ptr;
278 dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr;
279 snd_pcm_direct_reset_slave_ptr(pcm, dsnoop);
280 return 0;
281 }
282
snd_pcm_dsnoop_start(snd_pcm_t * pcm)283 static int snd_pcm_dsnoop_start(snd_pcm_t *pcm)
284 {
285 snd_pcm_direct_t *dsnoop = pcm->private_data;
286 int err;
287
288 if (dsnoop->state != SND_PCM_STATE_PREPARED)
289 return -EBADFD;
290 snd_pcm_hwsync(dsnoop->spcm);
291 snoop_timestamp(pcm);
292 dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr;
293 snd_pcm_direct_reset_slave_ptr(pcm, dsnoop);
294 err = snd_timer_start(dsnoop->timer);
295 if (err < 0)
296 return err;
297 dsnoop->state = SND_PCM_STATE_RUNNING;
298 dsnoop->trigger_tstamp = dsnoop->update_tstamp;
299 return 0;
300 }
301
snd_pcm_dsnoop_drop(snd_pcm_t * pcm)302 static int snd_pcm_dsnoop_drop(snd_pcm_t *pcm)
303 {
304 snd_pcm_direct_t *dsnoop = pcm->private_data;
305 if (dsnoop->state == SND_PCM_STATE_OPEN)
306 return -EBADFD;
307 dsnoop->state = SND_PCM_STATE_SETUP;
308 snd_timer_stop(dsnoop->timer);
309 return 0;
310 }
311
312 /* locked version */
__snd_pcm_dsnoop_drain(snd_pcm_t * pcm)313 static int __snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
314 {
315 snd_pcm_direct_t *dsnoop = pcm->private_data;
316 snd_pcm_uframes_t stop_threshold;
317 int err;
318
319 if (dsnoop->state == SND_PCM_STATE_OPEN)
320 return -EBADFD;
321 stop_threshold = pcm->stop_threshold;
322 if (pcm->stop_threshold > pcm->buffer_size)
323 pcm->stop_threshold = pcm->buffer_size;
324 while (dsnoop->state == SND_PCM_STATE_RUNNING) {
325 err = snd_pcm_dsnoop_sync_ptr(pcm);
326 if (err < 0)
327 break;
328 if (pcm->mode & SND_PCM_NONBLOCK)
329 return -EAGAIN;
330 __snd_pcm_wait_in_lock(pcm, -1);
331 }
332 pcm->stop_threshold = stop_threshold;
333 return snd_pcm_dsnoop_drop(pcm);
334 }
335
snd_pcm_dsnoop_drain(snd_pcm_t * pcm)336 static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
337 {
338 int err;
339
340 snd_pcm_lock(pcm);
341 err = __snd_pcm_dsnoop_drain(pcm);
342 snd_pcm_unlock(pcm);
343 return err;
344 }
345
snd_pcm_dsnoop_pause(snd_pcm_t * pcm ATTRIBUTE_UNUSED,int enable ATTRIBUTE_UNUSED)346 static int snd_pcm_dsnoop_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
347 {
348 return -EIO;
349 }
350
snd_pcm_dsnoop_rewindable(snd_pcm_t * pcm)351 static snd_pcm_sframes_t snd_pcm_dsnoop_rewindable(snd_pcm_t *pcm)
352 {
353 return snd_pcm_mmap_capture_hw_rewindable(pcm);
354 }
355
snd_pcm_dsnoop_rewind(snd_pcm_t * pcm,snd_pcm_uframes_t frames)356 static snd_pcm_sframes_t snd_pcm_dsnoop_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
357 {
358 snd_pcm_sframes_t avail;
359
360 avail = snd_pcm_dsnoop_rewindable(pcm);
361 if (frames > (snd_pcm_uframes_t)avail)
362 frames = avail;
363 snd_pcm_mmap_appl_backward(pcm, frames);
364 return frames;
365 }
366
snd_pcm_dsnoop_forwardable(snd_pcm_t * pcm)367 static snd_pcm_sframes_t snd_pcm_dsnoop_forwardable(snd_pcm_t *pcm)
368 {
369 return snd_pcm_mmap_capture_avail(pcm);
370 }
371
snd_pcm_dsnoop_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)372 static snd_pcm_sframes_t snd_pcm_dsnoop_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
373 {
374 snd_pcm_sframes_t avail;
375
376 avail = snd_pcm_dsnoop_forwardable(pcm);
377 if (frames > (snd_pcm_uframes_t)avail)
378 frames = avail;
379 snd_pcm_mmap_appl_forward(pcm, frames);
380 return frames;
381 }
382
snd_pcm_dsnoop_writei(snd_pcm_t * pcm ATTRIBUTE_UNUSED,const void * buffer ATTRIBUTE_UNUSED,snd_pcm_uframes_t size ATTRIBUTE_UNUSED)383 static snd_pcm_sframes_t snd_pcm_dsnoop_writei(snd_pcm_t *pcm ATTRIBUTE_UNUSED, const void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
384 {
385 return -ENODEV;
386 }
387
snd_pcm_dsnoop_writen(snd_pcm_t * pcm ATTRIBUTE_UNUSED,void ** bufs ATTRIBUTE_UNUSED,snd_pcm_uframes_t size ATTRIBUTE_UNUSED)388 static snd_pcm_sframes_t snd_pcm_dsnoop_writen(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
389 {
390 return -ENODEV;
391 }
392
snd_pcm_dsnoop_close(snd_pcm_t * pcm)393 static int snd_pcm_dsnoop_close(snd_pcm_t *pcm)
394 {
395 snd_pcm_direct_t *dsnoop = pcm->private_data;
396
397 if (dsnoop->timer)
398 snd_timer_close(dsnoop->timer);
399 snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
400 snd_pcm_close(dsnoop->spcm);
401 if (dsnoop->server)
402 snd_pcm_direct_server_discard(dsnoop);
403 if (dsnoop->client)
404 snd_pcm_direct_client_discard(dsnoop);
405 if (snd_pcm_direct_shm_discard(dsnoop)) {
406 if (snd_pcm_direct_semaphore_discard(dsnoop))
407 snd_pcm_direct_semaphore_final(dsnoop, DIRECT_IPC_SEM_CLIENT);
408 } else
409 snd_pcm_direct_semaphore_final(dsnoop, DIRECT_IPC_SEM_CLIENT);
410 free(dsnoop->bindings);
411 pcm->private_data = NULL;
412 free(dsnoop);
413 return 0;
414 }
415
snd_pcm_dsnoop_mmap_commit(snd_pcm_t * pcm,snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)416 static snd_pcm_sframes_t snd_pcm_dsnoop_mmap_commit(snd_pcm_t *pcm,
417 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
418 snd_pcm_uframes_t size)
419 {
420 snd_pcm_direct_t *dsnoop = pcm->private_data;
421 int err;
422
423 switch (snd_pcm_state(dsnoop->spcm)) {
424 case SND_PCM_STATE_XRUN:
425 if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
426 return err;
427 break;
428 case SND_PCM_STATE_SUSPENDED:
429 return -ESTRPIPE;
430 default:
431 break;
432 }
433 if (snd_pcm_direct_client_chk_xrun(dsnoop, pcm))
434 return -EPIPE;
435 if (dsnoop->state == SND_PCM_STATE_RUNNING) {
436 err = snd_pcm_dsnoop_sync_ptr(pcm);
437 if (err < 0)
438 return err;
439 }
440 snd_pcm_mmap_appl_forward(pcm, size);
441 /* clear timer queue to avoid a bogus return from poll */
442 if (snd_pcm_mmap_capture_avail(pcm) < pcm->avail_min)
443 snd_pcm_direct_clear_timer_queue(dsnoop);
444 return size;
445 }
446
snd_pcm_dsnoop_avail_update(snd_pcm_t * pcm)447 static snd_pcm_sframes_t snd_pcm_dsnoop_avail_update(snd_pcm_t *pcm)
448 {
449 snd_pcm_direct_t *dsnoop = pcm->private_data;
450 int err;
451
452 if (dsnoop->state == SND_PCM_STATE_RUNNING) {
453 err = snd_pcm_dsnoop_sync_ptr(pcm);
454 if (err < 0)
455 return err;
456 }
457 if (dsnoop->state == SND_PCM_STATE_XRUN)
458 return -EPIPE;
459
460 return snd_pcm_mmap_capture_avail(pcm);
461 }
462
snd_pcm_dsnoop_htimestamp(snd_pcm_t * pcm,snd_pcm_uframes_t * avail,snd_htimestamp_t * tstamp)463 static int snd_pcm_dsnoop_htimestamp(snd_pcm_t *pcm,
464 snd_pcm_uframes_t *avail,
465 snd_htimestamp_t *tstamp)
466 {
467 snd_pcm_direct_t *dsnoop = pcm->private_data;
468 snd_pcm_uframes_t avail1;
469 int ok = 0;
470
471 while (1) {
472 if (dsnoop->state == SND_PCM_STATE_RUNNING ||
473 dsnoop->state == SND_PCM_STATE_DRAINING)
474 snd_pcm_dsnoop_sync_ptr(pcm);
475 avail1 = snd_pcm_mmap_capture_avail(pcm);
476 if (ok && *avail == avail1)
477 break;
478 *avail = avail1;
479 *tstamp = snd_pcm_hw_fast_tstamp(dsnoop->spcm);
480 ok = 1;
481 }
482 return 0;
483 }
484
snd_pcm_dsnoop_dump(snd_pcm_t * pcm,snd_output_t * out)485 static void snd_pcm_dsnoop_dump(snd_pcm_t *pcm, snd_output_t *out)
486 {
487 snd_pcm_direct_t *dsnoop = pcm->private_data;
488
489 snd_output_printf(out, "Direct Snoop PCM\n");
490 if (pcm->setup) {
491 snd_output_printf(out, "Its setup is:\n");
492 snd_pcm_dump_setup(pcm, out);
493 }
494 if (dsnoop->spcm)
495 snd_pcm_dump(dsnoop->spcm, out);
496 }
497
498 static const snd_pcm_ops_t snd_pcm_dsnoop_ops = {
499 .close = snd_pcm_dsnoop_close,
500 .info = snd_pcm_direct_info,
501 .hw_refine = snd_pcm_direct_hw_refine,
502 .hw_params = snd_pcm_direct_hw_params,
503 .hw_free = snd_pcm_direct_hw_free,
504 .sw_params = snd_pcm_direct_sw_params,
505 .channel_info = snd_pcm_direct_channel_info,
506 .dump = snd_pcm_dsnoop_dump,
507 .nonblock = snd_pcm_direct_nonblock,
508 .async = snd_pcm_direct_async,
509 .mmap = snd_pcm_direct_mmap,
510 .munmap = snd_pcm_direct_munmap,
511 .query_chmaps = snd_pcm_direct_query_chmaps,
512 .get_chmap = snd_pcm_direct_get_chmap,
513 .set_chmap = snd_pcm_direct_set_chmap,
514 };
515
516 static const snd_pcm_fast_ops_t snd_pcm_dsnoop_fast_ops = {
517 .status = snd_pcm_dsnoop_status,
518 .state = snd_pcm_dsnoop_state,
519 .hwsync = snd_pcm_dsnoop_hwsync,
520 .delay = snd_pcm_dsnoop_delay,
521 .prepare = snd_pcm_direct_prepare,
522 .reset = snd_pcm_dsnoop_reset,
523 .start = snd_pcm_dsnoop_start,
524 .drop = snd_pcm_dsnoop_drop,
525 .drain = snd_pcm_dsnoop_drain,
526 .pause = snd_pcm_dsnoop_pause,
527 .rewindable = snd_pcm_dsnoop_rewindable,
528 .rewind = snd_pcm_dsnoop_rewind,
529 .forwardable = snd_pcm_dsnoop_forwardable,
530 .forward = snd_pcm_dsnoop_forward,
531 .resume = snd_pcm_direct_resume,
532 .link = NULL,
533 .link_slaves = NULL,
534 .unlink = NULL,
535 .writei = snd_pcm_dsnoop_writei,
536 .writen = snd_pcm_dsnoop_writen,
537 .readi = snd_pcm_mmap_readi,
538 .readn = snd_pcm_mmap_readn,
539 .avail_update = snd_pcm_dsnoop_avail_update,
540 .mmap_commit = snd_pcm_dsnoop_mmap_commit,
541 .htimestamp = snd_pcm_dsnoop_htimestamp,
542 .poll_descriptors = snd_pcm_direct_poll_descriptors,
543 .poll_descriptors_count = NULL,
544 .poll_revents = snd_pcm_direct_poll_revents,
545 };
546
547 /**
548 * \brief Creates a new dsnoop PCM
549 * \param pcmp Returns created PCM handle
550 * \param name Name of PCM
551 * \param opts Direct PCM configurations
552 * \param params Parameters for slave
553 * \param root Configuration root
554 * \param sconf Slave configuration
555 * \param stream PCM Direction (stream)
556 * \param mode PCM Mode
557 * \retval zero on success otherwise a negative error code
558 * \warning Using of this function might be dangerous in the sense
559 * of compatibility reasons. The prototype might be freely
560 * changed in future.
561 */
snd_pcm_dsnoop_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)562 int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
563 struct snd_pcm_direct_open_conf *opts,
564 struct slave_params *params,
565 snd_config_t *root, snd_config_t *sconf,
566 snd_pcm_stream_t stream, int mode)
567 {
568 snd_pcm_t *pcm, *spcm = NULL;
569 snd_pcm_direct_t *dsnoop;
570 int ret, first_instance;
571
572 assert(pcmp);
573
574 if (stream != SND_PCM_STREAM_CAPTURE) {
575 SNDERR("The dsnoop plugin supports only capture stream");
576 return -EINVAL;
577 }
578
579 ret = _snd_pcm_direct_new(&pcm, &dsnoop, SND_PCM_TYPE_DSNOOP, name, opts, params, stream, mode);
580 if (ret < 0)
581 return ret;
582 first_instance = ret;
583
584 pcm->ops = &snd_pcm_dsnoop_ops;
585 pcm->fast_ops = &snd_pcm_dsnoop_fast_ops;
586 pcm->private_data = dsnoop;
587 dsnoop->state = SND_PCM_STATE_OPEN;
588 dsnoop->slowptr = opts->slowptr;
589 dsnoop->max_periods = opts->max_periods;
590 dsnoop->var_periodsize = opts->var_periodsize;
591 dsnoop->sync_ptr = snd_pcm_dsnoop_sync_ptr;
592 dsnoop->hw_ptr_alignment = opts->hw_ptr_alignment;
593
594 retry:
595 if (first_instance) {
596 /* recursion is already checked in
597 snd_pcm_direct_get_slave_ipc_offset() */
598 ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
599 mode | SND_PCM_NONBLOCK, NULL);
600 if (ret < 0) {
601 SNDERR("unable to open slave");
602 goto _err;
603 }
604
605 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
606 SNDERR("dsnoop plugin can be only connected to hw plugin");
607 goto _err;
608 }
609
610 ret = snd_pcm_direct_initialize_slave(dsnoop, spcm, params);
611 if (ret < 0) {
612 SNDERR("unable to initialize slave");
613 goto _err;
614 }
615
616 dsnoop->spcm = spcm;
617
618 if (dsnoop->shmptr->use_server) {
619 ret = snd_pcm_direct_server_create(dsnoop);
620 if (ret < 0) {
621 SNDERR("unable to create server");
622 goto _err;
623 }
624 }
625
626 dsnoop->shmptr->type = spcm->type;
627 } else {
628 if (dsnoop->shmptr->use_server) {
629 /* up semaphore to avoid deadlock */
630 snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
631 ret = snd_pcm_direct_client_connect(dsnoop);
632 if (ret < 0) {
633 SNDERR("unable to connect client");
634 goto _err_nosem;
635 }
636
637 snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
638
639 ret = snd_pcm_direct_open_secondary_client(&spcm, dsnoop, "dsnoop_client");
640 if (ret < 0)
641 goto _err;
642 } else {
643
644 ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
645 mode | SND_PCM_NONBLOCK |
646 SND_PCM_APPEND,
647 NULL);
648 if (ret < 0) {
649 /* all other streams have been closed;
650 * retry as the first instance
651 */
652 if (ret == -EBADFD) {
653 first_instance = 1;
654 goto retry;
655 }
656 SNDERR("unable to open slave");
657 goto _err;
658 }
659 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
660 SNDERR("dsnoop plugin can be only connected to hw plugin");
661 ret = -EINVAL;
662 goto _err;
663 }
664
665 ret = snd_pcm_direct_initialize_secondary_slave(dsnoop, spcm, params);
666 if (ret < 0) {
667 SNDERR("unable to initialize slave");
668 goto _err;
669 }
670 }
671
672 dsnoop->spcm = spcm;
673 }
674
675 ret = snd_pcm_direct_initialize_poll_fd(dsnoop);
676 if (ret < 0) {
677 SNDERR("unable to initialize poll_fd");
678 goto _err;
679 }
680
681 pcm->poll_fd = dsnoop->poll_fd;
682 pcm->poll_events = POLLIN; /* it's different than other plugins */
683 pcm->tstamp_type = spcm->tstamp_type;
684 pcm->mmap_rw = 1;
685 snd_pcm_set_hw_ptr(pcm, &dsnoop->hw_ptr, -1, 0);
686 snd_pcm_set_appl_ptr(pcm, &dsnoop->appl_ptr, -1, 0);
687
688 if (dsnoop->channels == UINT_MAX)
689 dsnoop->channels = dsnoop->shmptr->s.channels;
690
691 snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
692
693 *pcmp = pcm;
694 return 0;
695
696 _err:
697 if (dsnoop->timer)
698 snd_timer_close(dsnoop->timer);
699 if (dsnoop->server)
700 snd_pcm_direct_server_discard(dsnoop);
701 if (dsnoop->client)
702 snd_pcm_direct_client_discard(dsnoop);
703 if (spcm)
704 snd_pcm_close(spcm);
705 if ((dsnoop->shmid >= 0) && (snd_pcm_direct_shm_discard(dsnoop))) {
706 if (snd_pcm_direct_semaphore_discard(dsnoop))
707 snd_pcm_direct_semaphore_final(dsnoop, DIRECT_IPC_SEM_CLIENT);
708 } else
709 snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
710
711 _err_nosem:
712 free(dsnoop->bindings);
713 free(dsnoop);
714 snd_pcm_free(pcm);
715 return ret;
716 }
717
718 /*! \page pcm_plugins
719
720 \section pcm_plugins_dsnoop Plugin: dsnoop
721
722 This plugin splits one capture stream to more.
723 It works the reverse way of \ref pcm_plugins_dmix "dmix plugin",
724 reading the shared capture buffer from many clients concurrently.
725 The meaning of parameters below are almost identical with
726 dmix plugin.
727
728 \code
729 pcm.name {
730 type dsnoop # Direct snoop
731 ipc_key INT # unique IPC key
732 ipc_key_add_uid BOOL # add current uid to unique IPC key
733 ipc_perm INT # IPC permissions (octal, default 0600)
734 hw_ptr_alignment STR # Slave application and hw pointer alignment type
735 # STR can be one of the below strings :
736 # no
737 # roundup
738 # rounddown
739 # auto (default)
740 tstamp_type STR # timestamp type
741 # STR can be one of the below strings :
742 # default, gettimeofday, monotonic, monotonic_raw
743 slave STR
744 # or
745 slave { # Slave definition
746 pcm STR # slave PCM name
747 # or
748 pcm { } # slave PCM definition
749 format STR # format definition
750 rate INT # rate definition
751 channels INT
752 period_time INT # in usec
753 # or
754 period_size INT # in frames
755 buffer_time INT # in usec
756 # or
757 buffer_size INT # in frames
758 periods INT # when buffer_size or buffer_time is not specified
759 }
760 bindings { # note: this is client independent!!!
761 N INT # maps slave channel to client channel N
762 }
763 slowptr BOOL # slow but more precise pointer updates
764 }
765 \endcode
766
767 <code>hw_ptr_alignment</code> specifies slave application and hw
768 pointer alignment type. By default hw_ptr_alignment is auto. Below are
769 the possible configurations:
770 - no: minimal latency with minimal frames dropped at startup. But
771 wakeup of application (return from snd_pcm_wait() or poll()) can
772 take up to 2 * period.
773 - roundup: It is guaranteed that all frames will be played at
774 startup. But the latency will increase upto period-1 frames.
775 - rounddown: It is guaranteed that a wakeup will happen for each
776 period and frames can be written from application. But on startup
777 upto period-1 frames will be dropped.
778 - auto: Selects the best approach depending on the used period and
779 buffer size.
780 If the application buffer size is < 2 * application period,
781 "roundup" will be selected to avoid over runs. If the slave_period
782 is < 10ms we could expect that there are low latency
783 requirements. Therefore "rounddown" will be chosen to avoid long
784 wakeup times. Else "no" will be chosen.
785
786 \subsection pcm_plugins_dsnoop_funcref Function reference
787
788 <UL>
789 <LI>snd_pcm_dsnoop_open()
790 <LI>_snd_pcm_dsnoop_open()
791 </UL>
792
793 */
794
795 /**
796 * \brief Creates a new dsnoop PCM
797 * \param pcmp Returns created PCM handle
798 * \param name Name of PCM
799 * \param root Root configuration node
800 * \param conf Configuration node with dsnoop PCM description
801 * \param stream PCM Stream
802 * \param mode PCM Mode
803 * \warning Using of this function might be dangerous in the sense
804 * of compatibility reasons. The prototype might be freely
805 * changed in future.
806 */
_snd_pcm_dsnoop_open(snd_pcm_t ** pcmp,const char * name,snd_config_t * root,snd_config_t * conf,snd_pcm_stream_t stream,int mode)807 int _snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
808 snd_config_t *root, snd_config_t *conf,
809 snd_pcm_stream_t stream, int mode)
810 {
811 snd_config_t *sconf;
812 struct slave_params params;
813 struct snd_pcm_direct_open_conf dopen;
814 int bsize, psize;
815 int err;
816
817 err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
818 if (err < 0)
819 return err;
820
821 /* the default settings, it might be invalid for some hardware */
822 params.format = SND_PCM_FORMAT_S16;
823 params.rate = 48000;
824 params.channels = 2;
825 params.period_time = -1;
826 params.buffer_time = -1;
827 bsize = psize = -1;
828 params.periods = 3;
829 err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8,
830 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, ¶ms.format,
831 SND_PCM_HW_PARAM_RATE, 0, ¶ms.rate,
832 SND_PCM_HW_PARAM_CHANNELS, 0, ¶ms.channels,
833 SND_PCM_HW_PARAM_PERIOD_TIME, 0, ¶ms.period_time,
834 SND_PCM_HW_PARAM_BUFFER_TIME, 0, ¶ms.buffer_time,
835 SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
836 SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
837 SND_PCM_HW_PARAM_PERIODS, 0, ¶ms.periods);
838 if (err < 0)
839 return err;
840
841 /* set a reasonable default */
842 if (psize == -1 && params.period_time == -1)
843 params.period_time = 125000; /* 0.125 seconds */
844
845 if (params.format == -2)
846 params.format = SND_PCM_FORMAT_UNKNOWN;
847
848 params.period_size = psize;
849 params.buffer_size = bsize;
850
851 err = snd_pcm_dsnoop_open(pcmp, name, &dopen, ¶ms,
852 root, sconf, stream, mode);
853 snd_config_delete(sconf);
854 return err;
855 }
856 #ifndef DOC_HIDDEN
857 SND_DLSYM_BUILD_VERSION(_snd_pcm_dsnoop_open, SND_PCM_DLSYM_VERSION);
858 #endif
859