OSDN Git Service

a9ea1429b60594e365f4f09186feed3e64292dd6
[android-x86/external-alsa-lib.git] / src / pcm / pcm_dsnoop.c
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@suse.cz>
6  * \date 2003
7  */
8 /*
9  *  PCM - Capture Stream Snooping
10  *  Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
11  *
12  *
13  *   This library is free software; you can redistribute it and/or modify
14  *   it under the terms of the GNU Lesser General Public License as
15  *   published by the Free Software Foundation; either version 2.1 of
16  *   the License, or (at your option) any later version.
17  *
18  *   This program is distributed in the hope that it will be useful,
19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *   GNU Lesser General Public License for more details.
22  *
23  *   You should have received a copy of the GNU Lesser General Public
24  *   License along with this library; if not, write to the Free Software
25  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
26  *
27  */
28   
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stddef.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <ctype.h>
37 #include <grp.h>
38 #include <sys/ioctl.h>
39 #include <sys/mman.h>
40 #include <sys/shm.h>
41 #include <sys/sem.h>
42 #include <sys/wait.h>
43 #include <sys/socket.h>
44 #include <sys/un.h>
45 #include <sys/mman.h>
46 #include "pcm_direct.h"
47
48 #ifndef PIC
49 /* entry for static linking */
50 const char *_snd_module_pcm_dsnoop = "";
51 #endif
52
53 /*
54  *
55  */
56
57 static void snoop_areas(snd_pcm_direct_t *dsnoop,
58                         const snd_pcm_channel_area_t *src_areas,
59                         const snd_pcm_channel_area_t *dst_areas,
60                         snd_pcm_uframes_t src_ofs,
61                         snd_pcm_uframes_t dst_ofs,
62                         snd_pcm_uframes_t size)
63 {
64         unsigned int chn, schn, channels;
65         snd_pcm_format_t format;
66
67         channels = dsnoop->channels;
68         format = dsnoop->shmptr->s.format;
69         if (dsnoop->interleaved) {
70                 unsigned int fbytes = snd_pcm_format_physical_width(format) / 8;
71                 memcpy(((char *)dst_areas[0].addr) + (dst_ofs * channels * fbytes),
72                        ((char *)src_areas[0].addr) + (src_ofs * channels * fbytes),
73                        size * channels * fbytes);
74         } else {
75                 for (chn = 0; chn < channels; chn++) {
76                         schn = dsnoop->bindings ? dsnoop->bindings[chn] : chn;
77                         snd_pcm_area_copy(&dst_areas[chn], dst_ofs, &src_areas[schn], src_ofs, size, format);
78                 }
79         }
80 }
81
82 /*
83  *  synchronize shm ring buffer with hardware
84  */
85 static void snd_pcm_dsnoop_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr, snd_pcm_uframes_t size)
86 {
87         snd_pcm_direct_t *dsnoop = pcm->private_data;
88         snd_pcm_uframes_t hw_ptr = dsnoop->hw_ptr;
89         snd_pcm_uframes_t transfer;
90         const snd_pcm_channel_area_t *src_areas, *dst_areas;
91         
92         /* add sample areas here */
93         dst_areas = snd_pcm_mmap_areas(pcm);
94         src_areas = snd_pcm_mmap_areas(dsnoop->spcm);
95         hw_ptr %= pcm->buffer_size;
96         slave_hw_ptr %= dsnoop->slave_buffer_size;
97         while (size > 0) {
98                 transfer = hw_ptr + size > pcm->buffer_size ? pcm->buffer_size - hw_ptr : size;
99                 transfer = slave_hw_ptr + transfer > dsnoop->slave_buffer_size ?
100                         dsnoop->slave_buffer_size - slave_hw_ptr : transfer;
101                 size -= transfer;
102                 snoop_areas(dsnoop, src_areas, dst_areas, slave_hw_ptr, hw_ptr, transfer);
103                 slave_hw_ptr += transfer;
104                 slave_hw_ptr %= dsnoop->slave_buffer_size;
105                 hw_ptr += transfer;
106                 hw_ptr %= pcm->buffer_size;
107         }
108 }
109
110 /*
111  *  synchronize hardware pointer (hw_ptr) with ours
112  */
113 static int snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
114 {
115         snd_pcm_direct_t *dsnoop = pcm->private_data;
116         snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
117         snd_pcm_sframes_t diff;
118         
119         switch (snd_pcm_state(dsnoop->spcm)) {
120         case SND_PCM_STATE_DISCONNECTED:
121                 dsnoop->state = SNDRV_PCM_STATE_DISCONNECTED;
122                 return -ENODEV;
123         default:
124                 break;
125         }
126         if (dsnoop->slowptr)
127                 snd_pcm_hwsync(dsnoop->spcm);
128         old_slave_hw_ptr = dsnoop->slave_hw_ptr;
129         slave_hw_ptr = dsnoop->slave_hw_ptr = *dsnoop->spcm->hw.ptr;
130         diff = slave_hw_ptr - old_slave_hw_ptr;
131         if (diff == 0)          /* fast path */
132                 return 0;
133         if (diff < 0) {
134                 slave_hw_ptr += dsnoop->slave_boundary;
135                 diff = slave_hw_ptr - old_slave_hw_ptr;
136         }
137         snd_pcm_dsnoop_sync_area(pcm, old_slave_hw_ptr, diff);
138         dsnoop->hw_ptr += diff;
139         dsnoop->hw_ptr %= pcm->boundary;
140         // printf("sync ptr diff = %li\n", diff);
141         if (pcm->stop_threshold >= pcm->boundary)       /* don't care */
142                 return 0;
143         if ((avail = snd_pcm_mmap_capture_hw_avail(pcm)) >= pcm->stop_threshold) {
144                 struct timeval tv;
145                 gettimeofday(&tv, 0);
146                 dsnoop->trigger_tstamp.tv_sec = tv.tv_sec;
147                 dsnoop->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
148                 dsnoop->state = SND_PCM_STATE_XRUN;
149                 dsnoop->avail_max = avail;
150                 return -EPIPE;
151         }
152         if (avail > dsnoop->avail_max)
153                 dsnoop->avail_max = avail;
154         return 0;
155 }
156
157 /*
158  *  plugin implementation
159  */
160
161 static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
162 {
163         snd_pcm_direct_t *dsnoop = pcm->private_data;
164         snd_pcm_state_t state;
165
166         switch(dsnoop->state) {
167         case SNDRV_PCM_STATE_DRAINING:
168         case SNDRV_PCM_STATE_RUNNING:
169                 snd_pcm_dsnoop_sync_ptr(pcm);
170                 break;
171         default:
172                 break;
173         }
174         memset(status, 0, sizeof(*status));
175         state = snd_pcm_state(dsnoop->spcm);
176         status->state = state == SND_PCM_STATE_RUNNING ? dsnoop->state : state;
177         status->trigger_tstamp = dsnoop->trigger_tstamp;
178         status->tstamp = snd_pcm_hw_fast_tstamp(dsnoop->spcm);
179         status->avail = snd_pcm_mmap_capture_avail(pcm);
180         status->avail_max = status->avail > dsnoop->avail_max ? status->avail : dsnoop->avail_max;
181         dsnoop->avail_max = 0;
182         return 0;
183 }
184
185 static snd_pcm_state_t snd_pcm_dsnoop_state(snd_pcm_t *pcm)
186 {
187         snd_pcm_direct_t *dsnoop = pcm->private_data;
188         switch (snd_pcm_state(dsnoop->spcm)) {
189         case SND_PCM_STATE_SUSPENDED:
190                 return SND_PCM_STATE_SUSPENDED;
191         case SND_PCM_STATE_DISCONNECTED:
192                 dsnoop->state = SNDRV_PCM_STATE_DISCONNECTED;
193                 return -ENODEV;
194         default:
195                 break;
196         }
197         return dsnoop->state;
198 }
199
200 static int snd_pcm_dsnoop_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
201 {
202         snd_pcm_direct_t *dsnoop = pcm->private_data;
203         int err;
204         
205         switch(dsnoop->state) {
206         case SNDRV_PCM_STATE_DRAINING:
207         case SNDRV_PCM_STATE_RUNNING:
208                 err = snd_pcm_dsnoop_sync_ptr(pcm);
209                 if (err < 0)
210                         return err;
211         case SNDRV_PCM_STATE_PREPARED:
212         case SNDRV_PCM_STATE_SUSPENDED:
213                 *delayp = snd_pcm_mmap_capture_hw_avail(pcm);
214                 return 0;
215         case SNDRV_PCM_STATE_XRUN:
216                 return -EPIPE;
217         case SNDRV_PCM_STATE_DISCONNECTED:
218                 return -ENODEV;
219         default:
220                 return -EBADFD;
221         }
222 }
223
224 static int snd_pcm_dsnoop_hwsync(snd_pcm_t *pcm)
225 {
226         snd_pcm_direct_t *dsnoop = pcm->private_data;
227
228         switch(dsnoop->state) {
229         case SNDRV_PCM_STATE_DRAINING:
230         case SNDRV_PCM_STATE_RUNNING:
231                 return snd_pcm_dsnoop_sync_ptr(pcm);
232         case SNDRV_PCM_STATE_PREPARED:
233         case SNDRV_PCM_STATE_SUSPENDED:
234                 return 0;
235         case SNDRV_PCM_STATE_XRUN:
236                 return -EPIPE;
237         case SNDRV_PCM_STATE_DISCONNECTED:
238                 return -ENODEV;
239         default:
240                 return -EBADFD;
241         }
242 }
243
244 static int snd_pcm_dsnoop_prepare(snd_pcm_t *pcm)
245 {
246         snd_pcm_direct_t *dsnoop = pcm->private_data;
247
248         snd_pcm_direct_check_interleave(dsnoop, pcm);
249         dsnoop->state = SND_PCM_STATE_PREPARED;
250         dsnoop->appl_ptr = 0;
251         dsnoop->hw_ptr = 0;
252         return snd_pcm_direct_set_timer_params(dsnoop);
253 }
254
255 static int snd_pcm_dsnoop_reset(snd_pcm_t *pcm)
256 {
257         snd_pcm_direct_t *dsnoop = pcm->private_data;
258         dsnoop->hw_ptr %= pcm->period_size;
259         dsnoop->appl_ptr = dsnoop->hw_ptr;
260         dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr = *dsnoop->spcm->hw.ptr;
261         return 0;
262 }
263
264 static int snd_pcm_dsnoop_start(snd_pcm_t *pcm)
265 {
266         snd_pcm_direct_t *dsnoop = pcm->private_data;
267         struct timeval tv;
268         int err;
269         
270         if (dsnoop->state != SND_PCM_STATE_PREPARED)
271                 return -EBADFD;
272         snd_pcm_hwsync(dsnoop->spcm);
273         dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr = *dsnoop->spcm->hw.ptr;
274         err = snd_timer_start(dsnoop->timer);
275         if (err < 0)
276                 return err;
277         dsnoop->state = SND_PCM_STATE_RUNNING;
278         gettimeofday(&tv, 0);
279         dsnoop->trigger_tstamp.tv_sec = tv.tv_sec;
280         dsnoop->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
281         return 0;
282 }
283
284 static int snd_pcm_dsnoop_drop(snd_pcm_t *pcm)
285 {
286         snd_pcm_direct_t *dsnoop = pcm->private_data;
287         if (dsnoop->state == SND_PCM_STATE_OPEN)
288                 return -EBADFD;
289         snd_timer_stop(dsnoop->timer);
290         dsnoop->state = SND_PCM_STATE_SETUP;
291         return 0;
292 }
293
294 static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
295 {
296         snd_pcm_direct_t *dsnoop = pcm->private_data;
297         snd_pcm_uframes_t stop_threshold;
298         int err;
299
300         if (dsnoop->state == SND_PCM_STATE_OPEN)
301                 return -EBADFD;
302         stop_threshold = pcm->stop_threshold;
303         if (pcm->stop_threshold > pcm->buffer_size)
304                 pcm->stop_threshold = pcm->buffer_size;
305         while (dsnoop->state == SND_PCM_STATE_RUNNING) {
306                 err = snd_pcm_dsnoop_sync_ptr(pcm);
307                 if (err < 0)
308                         break;
309                 if (pcm->mode & SND_PCM_NONBLOCK)
310                         return -EAGAIN;
311                 snd_pcm_wait(pcm, -1);
312         }
313         pcm->stop_threshold = stop_threshold;
314         return snd_pcm_dsnoop_drop(pcm);
315 }
316
317 static int snd_pcm_dsnoop_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
318 {
319         return -EIO;
320 }
321
322 static snd_pcm_sframes_t snd_pcm_dsnoop_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
323 {
324         snd_pcm_mmap_appl_backward(pcm, frames);
325         return frames;
326 }
327
328 static snd_pcm_sframes_t snd_pcm_dsnoop_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
329 {
330         snd_pcm_sframes_t avail;
331
332         avail = snd_pcm_mmap_capture_hw_avail(pcm);
333         if (avail < 0)
334                 return 0;
335         if (frames > (snd_pcm_uframes_t)avail)
336                 frames = avail;
337         snd_pcm_mmap_appl_forward(pcm, frames);
338         return frames;
339 }
340
341 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)
342 {
343         return -ENODEV;
344 }
345
346 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)
347 {
348         return -ENODEV;
349 }
350
351 static int snd_pcm_dsnoop_close(snd_pcm_t *pcm)
352 {
353         snd_pcm_direct_t *dsnoop = pcm->private_data;
354
355         if (dsnoop->timer)
356                 snd_timer_close(dsnoop->timer);
357         snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
358         snd_pcm_close(dsnoop->spcm);
359         if (dsnoop->server)
360                 snd_pcm_direct_server_discard(dsnoop);
361         if (dsnoop->client)
362                 snd_pcm_direct_client_discard(dsnoop);
363         snd_pcm_direct_shm_discard(dsnoop);
364         snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
365         free(dsnoop->bindings);
366         pcm->private_data = NULL;
367         free(dsnoop);
368         return 0;
369 }
370
371 static snd_pcm_sframes_t snd_pcm_dsnoop_mmap_commit(snd_pcm_t *pcm,
372                                                     snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
373                                                     snd_pcm_uframes_t size)
374 {
375         snd_pcm_direct_t *dsnoop = pcm->private_data;
376         int err;
377
378         switch (snd_pcm_state(dsnoop->spcm)) {
379         case SND_PCM_STATE_XRUN:
380                 return -EPIPE;
381         case SND_PCM_STATE_SUSPENDED:
382                 return -ESTRPIPE;
383         default:
384                 break;
385         }
386         if (dsnoop->state == SND_PCM_STATE_RUNNING) {
387                 err = snd_pcm_dsnoop_sync_ptr(pcm);
388                 if (err < 0)
389                         return err;
390         }
391         snd_pcm_mmap_appl_forward(pcm, size);
392         /* clear timer queue to avoid a bogus return from poll */
393         if (snd_pcm_mmap_capture_avail(pcm) < pcm->avail_min)
394                 snd_pcm_direct_clear_timer_queue(dsnoop);
395         return size;
396 }
397
398 static snd_pcm_sframes_t snd_pcm_dsnoop_avail_update(snd_pcm_t *pcm)
399 {
400         snd_pcm_direct_t *dsnoop = pcm->private_data;
401         int err;
402         
403         if (dsnoop->state == SND_PCM_STATE_RUNNING) {
404                 err = snd_pcm_dsnoop_sync_ptr(pcm);
405                 if (err < 0)
406                         return err;
407         }
408         return snd_pcm_mmap_capture_avail(pcm);
409 }
410
411 static void snd_pcm_dsnoop_dump(snd_pcm_t *pcm, snd_output_t *out)
412 {
413         snd_pcm_direct_t *dsnoop = pcm->private_data;
414
415         snd_output_printf(out, "Direct Snoop PCM\n");
416         if (pcm->setup) {
417                 snd_output_printf(out, "Its setup is:\n");
418                 snd_pcm_dump_setup(pcm, out);
419         }
420         if (dsnoop->spcm)
421                 snd_pcm_dump(dsnoop->spcm, out);
422 }
423
424 static snd_pcm_ops_t snd_pcm_dsnoop_ops = {
425         .close = snd_pcm_dsnoop_close,
426         .info = snd_pcm_direct_info,
427         .hw_refine = snd_pcm_direct_hw_refine,
428         .hw_params = snd_pcm_direct_hw_params,
429         .hw_free = snd_pcm_direct_hw_free,
430         .sw_params = snd_pcm_direct_sw_params,
431         .channel_info = snd_pcm_direct_channel_info,
432         .dump = snd_pcm_dsnoop_dump,
433         .nonblock = snd_pcm_direct_nonblock,
434         .async = snd_pcm_direct_async,
435         .mmap = snd_pcm_direct_mmap,
436         .munmap = snd_pcm_direct_munmap,
437 };
438
439 static snd_pcm_fast_ops_t snd_pcm_dsnoop_fast_ops = {
440         .status = snd_pcm_dsnoop_status,
441         .state = snd_pcm_dsnoop_state,
442         .hwsync = snd_pcm_dsnoop_hwsync,
443         .delay = snd_pcm_dsnoop_delay,
444         .prepare = snd_pcm_dsnoop_prepare,
445         .reset = snd_pcm_dsnoop_reset,
446         .start = snd_pcm_dsnoop_start,
447         .drop = snd_pcm_dsnoop_drop,
448         .drain = snd_pcm_dsnoop_drain,
449         .pause = snd_pcm_dsnoop_pause,
450         .rewind = snd_pcm_dsnoop_rewind,
451         .forward = snd_pcm_dsnoop_forward,
452         .resume = snd_pcm_direct_resume,
453         .link_fd = NULL,
454         .link = NULL,
455         .unlink = NULL,
456         .writei = snd_pcm_dsnoop_writei,
457         .writen = snd_pcm_dsnoop_writen,
458         .readi = snd_pcm_mmap_readi,
459         .readn = snd_pcm_mmap_readn,
460         .avail_update = snd_pcm_dsnoop_avail_update,
461         .mmap_commit = snd_pcm_dsnoop_mmap_commit,
462         .poll_descriptors = NULL,
463         .poll_descriptors_count = NULL,
464         .poll_revents = snd_pcm_direct_poll_revents,
465 };
466
467 /**
468  * \brief Creates a new dsnoop PCM
469  * \param pcmp Returns created PCM handle
470  * \param name Name of PCM
471  * \param ipc_key IPC key for semaphore and shared memory
472  * \param ipc_perm IPC permissions for semaphore and shared memory
473  * \param ipc_gid IPC group ID for semaphore and shared memory
474  * \param params Parameters for slave
475  * \param bindings Channel bindings
476  * \param slowptr Slow but more precise pointer updates
477  * \param root Configuration root
478  * \param sconf Slave configuration
479  * \param stream PCM Direction (stream)
480  * \param mode PCM Mode
481  * \retval zero on success otherwise a negative error code
482  * \warning Using of this function might be dangerous in the sense
483  *          of compatibility reasons. The prototype might be freely
484  *          changed in future.
485  */
486 int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
487                         key_t ipc_key, mode_t ipc_perm, int ipc_gid,
488                         struct slave_params *params,
489                         snd_config_t *bindings,
490                         int slowptr,
491                         snd_config_t *root, snd_config_t *sconf,
492                         snd_pcm_stream_t stream, int mode)
493 {
494         snd_pcm_t *pcm = NULL, *spcm = NULL;
495         snd_pcm_direct_t *dsnoop = NULL;
496         int ret, first_instance, fail_sem_loop = 10;
497
498         assert(pcmp);
499
500         if (stream != SND_PCM_STREAM_CAPTURE) {
501                 SNDERR("The dsnoop plugin supports only capture stream");
502                 return -EINVAL;
503         }
504
505         dsnoop = calloc(1, sizeof(snd_pcm_direct_t));
506         if (!dsnoop) {
507                 ret = -ENOMEM;
508                 goto _err_nosem;
509         }
510         
511         ret = snd_pcm_direct_parse_bindings(dsnoop, bindings);
512         if (ret < 0)
513                 goto _err_nosem;
514         
515         dsnoop->ipc_key = ipc_key;
516         dsnoop->ipc_perm = ipc_perm;
517         dsnoop->ipc_gid = ipc_gid;
518         dsnoop->semid = -1;
519         dsnoop->shmid = -1;
520
521         ret = snd_pcm_new(&pcm, dsnoop->type = SND_PCM_TYPE_DSNOOP, name, stream, mode);
522         if (ret < 0)
523                 goto _err_nosem;
524
525         while (1) {
526                 ret = snd_pcm_direct_semaphore_create_or_connect(dsnoop);
527                 if (ret < 0) {
528                         SNDERR("unable to create IPC semaphore");
529                         goto _err_nosem;
530                 }
531         
532                 ret = snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
533                 if (ret < 0) {
534                         snd_pcm_direct_semaphore_discard(dsnoop);
535                         if (--fail_sem_loop <= 0)
536                                 goto _err_nosem;
537                         continue;
538                 }
539                 break;
540         }
541                 
542         first_instance = ret = snd_pcm_direct_shm_create_or_connect(dsnoop);
543         if (ret < 0) {
544                 SNDERR("unable to create IPC shm instance");
545                 goto _err;
546         }
547                 
548         pcm->ops = &snd_pcm_dsnoop_ops;
549         pcm->fast_ops = &snd_pcm_dsnoop_fast_ops;
550         pcm->private_data = dsnoop;
551         dsnoop->state = SND_PCM_STATE_OPEN;
552         dsnoop->slowptr = slowptr;
553         dsnoop->sync_ptr = snd_pcm_dsnoop_sync_ptr;
554
555         if (first_instance) {
556                 /* recursion is already checked in
557                    snd_pcm_direct_get_slave_ipc_offset() */
558                 ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
559                                          mode | SND_PCM_NONBLOCK, NULL);
560                 if (ret < 0) {
561                         SNDERR("unable to open slave");
562                         goto _err;
563                 }
564         
565                 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
566                         SNDERR("dsnoop plugin can be only connected to hw plugin");
567                         goto _err;
568                 }
569                 
570                 ret = snd_pcm_direct_initialize_slave(dsnoop, spcm, params);
571                 if (ret < 0) {
572                         SNDERR("unable to initialize slave");
573                         goto _err;
574                 }
575
576                 dsnoop->spcm = spcm;
577                 
578                 ret = snd_pcm_direct_server_create(dsnoop);
579                 if (ret < 0) {
580                         SNDERR("unable to create server");
581                         goto _err;
582                 }
583
584                 dsnoop->shmptr->type = spcm->type;
585         } else {
586                 /* up semaphore to avoid deadlock */
587                 snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
588                 ret = snd_pcm_direct_client_connect(dsnoop);
589                 if (ret < 0) {
590                         SNDERR("unable to connect client");
591                         goto _err_nosem;
592                 }
593                         
594                 snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
595
596                 ret = snd_pcm_direct_open_secondary_client(&spcm, dsnoop, "dsnoop_client");
597                 if (ret < 0)
598                         goto _err;
599                 dsnoop->spcm = spcm;
600         }
601
602         ret = snd_pcm_direct_initialize_poll_fd(dsnoop);
603         if (ret < 0) {
604                 SNDERR("unable to initialize poll_fd");
605                 goto _err;
606         }
607
608         pcm->poll_fd = dsnoop->poll_fd;
609         pcm->poll_events = POLLIN;      /* it's different than other plugins */
610                 
611         pcm->mmap_rw = 1;
612         snd_pcm_set_hw_ptr(pcm, &dsnoop->hw_ptr, -1, 0);
613         snd_pcm_set_appl_ptr(pcm, &dsnoop->appl_ptr, -1, 0);
614         
615         if (dsnoop->channels == UINT_MAX)
616                 dsnoop->channels = dsnoop->shmptr->s.channels;
617         
618         snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
619
620         *pcmp = pcm;
621         return 0;
622         
623  _err:
624         if (dsnoop->timer)
625                 snd_timer_close(dsnoop->timer);
626         if (dsnoop->server)
627                 snd_pcm_direct_server_discard(dsnoop);
628         if (dsnoop->client)
629                 snd_pcm_direct_client_discard(dsnoop);
630         if (spcm)
631                 snd_pcm_close(spcm);
632         if (dsnoop->shmid >= 0)
633                 snd_pcm_direct_shm_discard(dsnoop);
634         if (snd_pcm_direct_semaphore_discard(dsnoop) < 0)
635                 snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
636  _err_nosem:
637         if (dsnoop) {
638                 free(dsnoop->bindings);
639                 free(dsnoop);
640         }
641         if (pcm)
642                 snd_pcm_free(pcm);
643         return ret;
644 }
645
646 /*! \page pcm_plugins
647
648 \section pcm_plugins_dsnoop Plugin: dsnoop
649
650 This plugin splits one capture stream to more.
651 It works the reverse way of \ref pcm_plugins_dmix "dmix plugin",
652 reading the shared capture buffer from many clients concurrently.
653 The meaning of parameters below are almost identical with
654 dmix plugin.
655
656 \code
657 pcm.name {
658         type dsnoop             # Direct snoop
659         ipc_key INT             # unique IPC key
660         ipc_key_add_uid BOOL    # add current uid to unique IPC key
661         ipc_perm INT            # IPC permissions (octal, default 0600)
662         slave STR
663         # or
664         slave {                 # Slave definition
665                 pcm STR         # slave PCM name
666                 # or
667                 pcm { }         # slave PCM definition
668                 format STR      # format definition
669                 rate INT        # rate definition
670                 channels INT
671                 period_time INT # in usec
672                 # or
673                 period_size INT # in bytes
674                 buffer_time INT # in usec
675                 # or
676                 buffer_size INT # in bytes
677                 periods INT     # when buffer_size or buffer_time is not specified
678         }
679         bindings {              # note: this is client independent!!!
680                 N INT           # maps slave channel to client channel N
681         }
682         slowptr BOOL            # slow but more precise pointer updates
683 }
684 \endcode
685
686 \subsection pcm_plugins_dsnoop_funcref Function reference
687
688 <UL>
689   <LI>snd_pcm_dsnoop_open()
690   <LI>_snd_pcm_dsnoop_open()
691 </UL>
692
693 */
694
695 /**
696  * \brief Creates a new dsnoop PCM
697  * \param pcmp Returns created PCM handle
698  * \param name Name of PCM
699  * \param root Root configuration node
700  * \param conf Configuration node with dsnoop PCM description
701  * \param stream PCM Stream
702  * \param mode PCM Mode
703  * \warning Using of this function might be dangerous in the sense
704  *          of compatibility reasons. The prototype might be freely
705  *          changed in future.
706  */
707 int _snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
708                        snd_config_t *root, snd_config_t *conf,
709                        snd_pcm_stream_t stream, int mode)
710 {
711         snd_config_t *sconf;
712         struct slave_params params;
713         struct snd_pcm_direct_open_conf dopen;
714         int bsize, psize;
715         int ipc_offset;
716         int err;
717
718         err = snd_pcm_direct_parse_open_conf(conf, &dopen);
719         if (err < 0)
720                 return err;
721
722         /* the default settings, it might be invalid for some hardware */
723         params.format = SND_PCM_FORMAT_S16;
724         params.rate = 48000;
725         params.channels = 2;
726         params.period_time = -1;
727         params.buffer_time = -1;
728         bsize = psize = -1;
729         params.periods = 3;
730         err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8,
731                                  SND_PCM_HW_PARAM_FORMAT, 0, &params.format,
732                                  SND_PCM_HW_PARAM_RATE, 0, &params.rate,
733                                  SND_PCM_HW_PARAM_CHANNELS, 0, &params.channels,
734                                  SND_PCM_HW_PARAM_PERIOD_TIME, 0, &params.period_time,
735                                  SND_PCM_HW_PARAM_BUFFER_TIME, 0, &params.buffer_time,
736                                  SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
737                                  SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
738                                  SND_PCM_HW_PARAM_PERIODS, 0, &params.periods);
739         if (err < 0)
740                 return err;
741
742         /* set a reasonable default */  
743         if (psize == -1 && params.period_time == -1)
744                 params.period_time = 125000;    /* 0.125 seconds */
745
746         params.period_size = psize;
747         params.buffer_size = bsize;
748
749         ipc_offset = snd_pcm_direct_get_slave_ipc_offset(root, sconf, stream);
750         if (ipc_offset < 0) {
751                 snd_config_delete(sconf);
752                 return ipc_offset;
753         }
754         dopen.ipc_key += ipc_offset;
755
756         err = snd_pcm_dsnoop_open(pcmp, name, dopen.ipc_key, dopen.ipc_perm, dopen.ipc_gid,
757                                   &params, dopen.bindings, dopen.slowptr,
758                                   root, sconf, stream, mode);
759         if (err < 0)
760                 snd_config_delete(sconf);
761         return err;
762 }
763 #ifndef DOC_HIDDEN
764 SND_DLSYM_BUILD_VERSION(_snd_pcm_dsnoop_open, SND_PCM_DLSYM_VERSION);
765 #endif