OSDN Git Service

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