OSDN Git Service

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