OSDN Git Service

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