OSDN Git Service

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