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