OSDN Git Service

document all PCM plugin parameters
[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         .poll_ask = NULL,
467         .writei = snd_pcm_dsnoop_writei,
468         .writen = snd_pcm_dsnoop_writen,
469         .readi = snd_pcm_mmap_readi,
470         .readn = snd_pcm_mmap_readn,
471         .avail_update = snd_pcm_dsnoop_avail_update,
472         .mmap_commit = snd_pcm_dsnoop_mmap_commit,
473 };
474
475 /**
476  * \brief Creates a new dsnoop PCM
477  * \param pcmp Returns created PCM handle
478  * \param name Name of PCM
479  * \param ipc_key IPC key for semaphore and shared memory
480  * \param ipc_perm IPC permissions for semaphore and shared memory
481  * \param params Parameters for slave
482  * \param bindings Channel bindings
483  * \param slowptr Slow but more precise pointer updates
484  * \param root Configuration root
485  * \param sconf Slave configuration
486  * \param stream PCM Direction (stream)
487  * \param mode PCM Mode
488  * \retval zero on success otherwise a negative error code
489  * \warning Using of this function might be dangerous in the sense
490  *          of compatibility reasons. The prototype might be freely
491  *          changed in future.
492  */
493 int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
494                         key_t ipc_key, mode_t ipc_perm,
495                         struct slave_params *params,
496                         snd_config_t *bindings,
497                         int slowptr,
498                         snd_config_t *root, snd_config_t *sconf,
499                         snd_pcm_stream_t stream, int mode)
500 {
501         snd_pcm_t *pcm = NULL, *spcm = NULL;
502         snd_pcm_direct_t *dsnoop = NULL;
503         int ret, first_instance, fail_sem_loop = 10;
504
505         assert(pcmp);
506
507         if (stream != SND_PCM_STREAM_CAPTURE) {
508                 SNDERR("The dsnoop plugin supports only capture stream");
509                 return -EINVAL;
510         }
511
512         dsnoop = calloc(1, sizeof(snd_pcm_direct_t));
513         if (!dsnoop) {
514                 ret = -ENOMEM;
515                 goto _err;
516         }
517         
518         ret = snd_pcm_direct_parse_bindings(dsnoop, bindings);
519         if (ret < 0)
520                 goto _err;
521         
522         dsnoop->ipc_key = ipc_key;
523         dsnoop->ipc_perm = ipc_perm;
524         dsnoop->semid = -1;
525         dsnoop->shmid = -1;
526
527         ret = snd_pcm_new(&pcm, dsnoop->type = SND_PCM_TYPE_DSNOOP, name, stream, mode);
528         if (ret < 0)
529                 goto _err;
530
531         while (1) {
532                 ret = snd_pcm_direct_semaphore_create_or_connect(dsnoop);
533                 if (ret < 0) {
534                         SNDERR("unable to create IPC semaphore");
535                         goto _err;
536                 }
537         
538                 ret = snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
539                 if (ret < 0) {
540                         snd_pcm_direct_semaphore_discard(dsnoop);
541                         if (--fail_sem_loop <= 0)
542                                 goto _err;
543                         continue;
544                 }
545                 break;
546         }
547                 
548         first_instance = ret = snd_pcm_direct_shm_create_or_connect(dsnoop);
549         if (ret < 0) {
550                 SNDERR("unable to create IPC shm instance");
551                 goto _err;
552         }
553                 
554         pcm->ops = &snd_pcm_dsnoop_ops;
555         pcm->fast_ops = &snd_pcm_dsnoop_fast_ops;
556         pcm->private_data = dsnoop;
557         dsnoop->state = SND_PCM_STATE_OPEN;
558         dsnoop->slowptr = slowptr;
559         dsnoop->sync_ptr = snd_pcm_dsnoop_sync_ptr;
560
561         if (first_instance) {
562                 ret = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
563                 if (ret < 0) {
564                         SNDERR("unable to open slave");
565                         goto _err;
566                 }
567         
568                 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
569                         SNDERR("dsnoop plugin can be only connected to hw plugin");
570                         goto _err;
571                 }
572                 
573                 ret = snd_pcm_direct_initialize_slave(dsnoop, spcm, params);
574                 if (ret < 0) {
575                         SNDERR("unable to initialize slave");
576                         goto _err;
577                 }
578
579                 dsnoop->spcm = spcm;
580                 
581                 ret = snd_pcm_direct_server_create(dsnoop);
582                 if (ret < 0) {
583                         SNDERR("unable to create server");
584                         goto _err;
585                 }
586
587                 dsnoop->shmptr->type = spcm->type;
588         } else {
589                 ret = snd_pcm_direct_client_connect(dsnoop);
590                 if (ret < 0) {
591                         SNDERR("unable to connect client");
592                         return ret;
593                 }
594                         
595                 ret = snd_pcm_hw_open_fd(&spcm, "dsnoop_client", dsnoop->hw_fd, 0);
596                 if (ret < 0) {
597                         SNDERR("unable to open hardware");
598                         goto _err;
599                 }
600                 
601                 spcm->donot_close = 1;
602                 spcm->setup = 1;
603                 spcm->buffer_size = dsnoop->shmptr->s.buffer_size;
604                 spcm->sample_bits = dsnoop->shmptr->s.sample_bits;
605                 spcm->channels = dsnoop->shmptr->s.channels;
606                 spcm->format = dsnoop->shmptr->s.format;
607                 spcm->boundary = dsnoop->shmptr->s.boundary;
608                 spcm->info = dsnoop->shmptr->s.info;
609                 ret = snd_pcm_mmap(spcm);
610                 if (ret < 0) {
611                         SNDERR("unable to mmap channels");
612                         goto _err;
613                 }
614                 dsnoop->spcm = spcm;
615         }
616
617         ret = snd_pcm_direct_initialize_poll_fd(dsnoop);
618         if (ret < 0) {
619                 SNDERR("unable to initialize poll_fd");
620                 goto _err;
621         }
622
623         pcm->poll_fd = dsnoop->poll_fd;
624         pcm->poll_events = POLLIN;      /* it's different than other plugins */
625                 
626         pcm->mmap_rw = 1;
627         snd_pcm_set_hw_ptr(pcm, &dsnoop->hw_ptr, -1, 0);
628         snd_pcm_set_appl_ptr(pcm, &dsnoop->appl_ptr, -1, 0);
629         
630         if (dsnoop->channels == UINT_MAX)
631                 dsnoop->channels = dsnoop->shmptr->s.channels;
632         
633         snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
634
635         *pcmp = pcm;
636         return 0;
637         
638  _err:
639         if (dsnoop) {
640                 if (dsnoop->timer)
641                         snd_timer_close(dsnoop->timer);
642                 if (dsnoop->server)
643                         snd_pcm_direct_server_discard(dsnoop);
644                 if (dsnoop->client)
645                         snd_pcm_direct_client_discard(dsnoop);
646                 if (spcm)
647                         snd_pcm_close(spcm);
648                 if (dsnoop->shmid >= 0) {
649                         if (snd_pcm_direct_shm_discard(dsnoop) > 0) {
650                                 if (dsnoop->semid >= 0) {
651                                         if (snd_pcm_direct_semaphore_discard(dsnoop) < 0)
652                                                 snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
653                                 }
654                         }
655                 }
656                 if (dsnoop->bindings)
657                         free(dsnoop->bindings);
658                 free(dsnoop);
659         }
660         if (pcm)
661                 snd_pcm_free(pcm);
662         return ret;
663 }
664
665 /*! \page pcm_plugins
666
667 \section pcm_plugins_dsnoop Plugin: dsnoop
668
669 This plugin splits one capture stream to more.
670 It works the reverse way of \ref pcm_plugins_dmix "dmix plugin",
671 reading the shared capture buffer from many clients concurrently.
672 The meaning of parameters below are almost identical with
673 dmix plugin.
674
675 \code
676 pcm.name {
677         type dsnoop             # Direct snoop
678         ipc_key INT             # unique IPC key
679         ipc_key_add_uid BOOL    # add current uid to unique IPC key
680         ipc_perm INT            # IPC permissions (octal, default 0600)
681         slave STR
682         # or
683         slave {                 # Slave definition
684                 pcm STR         # slave PCM name
685                 # or
686                 pcm { }         # slave PCM definition
687                 format STR      # format definition
688                 rate INT        # rate definition
689                 channels INT
690                 period_time INT # in usec
691                 # or
692                 period_size INT # in bytes
693                 buffer_time INT # in usec
694                 # or
695                 buffer_size INT # in bytes
696                 periods INT     # when buffer_size or buffer_time is not specified
697         }
698         bindings {              # note: this is client independent!!!
699                 N INT           # maps slave channel to client channel N
700         }
701         slowptr BOOL            # slow but more precise pointer updates
702 }
703 \endcode
704
705 \subsection pcm_plugins_dsnoop_funcref Function reference
706
707 <UL>
708   <LI>snd_pcm_dsnoop_open()
709   <LI>_snd_pcm_dsnoop_open()
710 </UL>
711
712 */
713
714 /**
715  * \brief Creates a new dsnoop PCM
716  * \param pcmp Returns created PCM handle
717  * \param name Name of PCM
718  * \param root Root configuration node
719  * \param conf Configuration node with dsnoop PCM description
720  * \param stream PCM Stream
721  * \param mode PCM Mode
722  * \warning Using of this function might be dangerous in the sense
723  *          of compatibility reasons. The prototype might be freely
724  *          changed in future.
725  */
726 int _snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
727                        snd_config_t *root, snd_config_t *conf,
728                        snd_pcm_stream_t stream, int mode)
729 {
730         snd_config_iterator_t i, next;
731         snd_config_t *slave = NULL, *bindings = NULL, *sconf;
732         struct slave_params params;
733         int bsize, psize, ipc_key_add_uid = 0, slowptr = 0;
734         key_t ipc_key = 0;
735         mode_t ipc_perm = 0600;
736         int err;
737
738         snd_config_for_each(i, next, conf) {
739                 snd_config_t *n = snd_config_iterator_entry(i);
740                 const char *id;
741                 if (snd_config_get_id(n, &id) < 0)
742                         continue;
743                 if (snd_pcm_conf_generic_id(id))
744                         continue;
745                 if (strcmp(id, "ipc_key") == 0) {
746                         long key;
747                         err = snd_config_get_integer(n, &key);
748                         if (err < 0) {
749                                 SNDERR("The field ipc_key must be an integer type");
750                                 return err;
751                         }
752                         ipc_key = key;
753                         continue;
754                 }
755                 if (strcmp(id, "ipc_perm") == 0) {
756                         char *perm;
757                         char *endp;
758                         err = snd_config_get_ascii(n, &perm);
759                         if (err < 0) {
760                                 SNDERR("The field ipc_perm must be a valid file permission");
761                                 return err;
762                         }
763                         if (isdigit(*perm) == 0) {
764                                 SNDERR("The field ipc_perm must be a valid file permission");
765                                 return -EINVAL;
766                         }
767                         ipc_perm = strtol(perm, &endp, 8);
768                         continue;
769                 }
770                 if (strcmp(id, "ipc_key_add_uid") == 0) {
771                         char *tmp;
772                         err = snd_config_get_ascii(n, &tmp);
773                         if (err < 0) {
774                                 SNDERR("The field ipc_key_add_uid must be a boolean type");
775                                 return err;
776                         }
777                         err = snd_config_get_bool_ascii(tmp);
778                         free(tmp);
779                         if (err < 0) {
780                                 SNDERR("The field ipc_key_add_uid must be a boolean type");
781                                 return err;
782                         }
783                         ipc_key_add_uid = err;
784                         continue;
785                 }
786                 if (strcmp(id, "slave") == 0) {
787                         slave = n;
788                         continue;
789                 }
790                 if (strcmp(id, "bindings") == 0) {
791                         bindings = n;
792                         continue;
793                 }
794                 if (strcmp(id, "slowptr") == 0) {
795                         char *tmp;
796                         err = snd_config_get_ascii(n, &tmp);
797                         if (err < 0) {
798                                 SNDERR("The field slowptr must be a boolean type");
799                                 return err;
800                         }
801                         err = snd_config_get_bool_ascii(tmp);
802                         free(tmp);
803                         if (err < 0) {
804                                 SNDERR("The field slowptr must be a boolean type");
805                                 return err;
806                         }
807                         slowptr = err;
808                         continue;
809                 }
810                 SNDERR("Unknown field %s", id);
811                 return -EINVAL;
812         }
813         if (!slave) {
814                 SNDERR("slave is not defined");
815                 return -EINVAL;
816         }
817         if (ipc_key_add_uid)
818                 ipc_key += getuid();
819         if (!ipc_key) {
820                 SNDERR("Unique IPC key is not defined");
821                 return -EINVAL;
822         }
823         /* the default settings, it might be invalid for some hardware */
824         params.format = SND_PCM_FORMAT_S16;
825         params.rate = 48000;
826         params.channels = 2;
827         params.period_time = 125000;    /* 0.125 seconds */
828         params.buffer_time = -1;
829         bsize = psize = -1;
830         params.periods = 3;
831         err = snd_pcm_slave_conf(root, slave, &sconf, 8,
832                                  SND_PCM_HW_PARAM_FORMAT, 0, &params.format,
833                                  SND_PCM_HW_PARAM_RATE, 0, &params.rate,
834                                  SND_PCM_HW_PARAM_CHANNELS, 0, &params.channels,
835                                  SND_PCM_HW_PARAM_PERIOD_TIME, 0, &params.period_time,
836                                  SND_PCM_HW_PARAM_BUFFER_TIME, 0, &params.buffer_time,
837                                  SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
838                                  SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
839                                  SND_PCM_HW_PARAM_PERIODS, 0, &params.periods);
840         if (err < 0)
841                 return err;
842
843         params.period_size = psize;
844         params.buffer_size = bsize;
845         err = snd_pcm_dsnoop_open(pcmp, name, ipc_key, ipc_perm, &params, bindings, slowptr, root, sconf, stream, mode);
846         if (err < 0)
847                 snd_config_delete(sconf);
848         return err;
849 }
850 #ifndef DOC_HIDDEN
851 SND_DLSYM_BUILD_VERSION(_snd_pcm_dsnoop_open, SND_PCM_DLSYM_VERSION);
852 #endif