OSDN Git Service

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