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