OSDN Git Service

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