OSDN Git Service

fixed dmix crashes by Clemens Ladisch <clemens@ladisch.de>:
[android-x86/external-alsa-lib.git] / src / pcm / pcm_dsnoop.c
1 /**
2  * \file pcm/pcm_dsnoop.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Capture Stream Snooping (dsnoop) Plugin Interface
5  * \author Jaroslav Kysela <perex@suse.cz>
6  * \date 2003
7  */
8 /*
9  *  PCM - Capture Stream Snooping
10  *  Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
11  *
12  *
13  *   This library is free software; you can redistribute it and/or modify
14  *   it under the terms of the GNU Lesser General Public License as
15  *   published by the Free Software Foundation; either version 2.1 of
16  *   the License, or (at your option) any later version.
17  *
18  *   This program is distributed in the hope that it will be useful,
19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *   GNU Lesser General Public License for more details.
22  *
23  *   You should have received a copy of the GNU Lesser General Public
24  *   License along with this library; if not, write to the Free Software
25  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
26  *
27  */
28   
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stddef.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <sys/ioctl.h>
37 #include <sys/mman.h>
38 #include <sys/shm.h>
39 #include <sys/sem.h>
40 #include <sys/wait.h>
41 #include <sys/socket.h>
42 #include <sys/un.h>
43 #include <sys/mman.h>
44 #include "pcm_direct.h"
45
46 #ifndef PIC
47 /* entry for static linking */
48 const char *_snd_module_pcm_dsnoop = "";
49 #endif
50
51 /*
52  *
53  */
54
55 static void snoop_areas(snd_pcm_direct_t *dsnoop,
56                         const snd_pcm_channel_area_t *src_areas,
57                         const snd_pcm_channel_area_t *dst_areas,
58                         snd_pcm_uframes_t src_ofs,
59                         snd_pcm_uframes_t dst_ofs,
60                         snd_pcm_uframes_t size)
61 {
62         unsigned int chn, schn, channels;
63         snd_pcm_format_t format;
64
65         channels = dsnoop->channels;
66         format = dsnoop->shmptr->s.format;
67         if (dsnoop->interleaved) {
68                 unsigned int fbytes = snd_pcm_format_physical_width(format) / 8;
69                 memcpy(((char *)dst_areas[0].addr) + (dst_ofs * channels * fbytes),
70                        ((char *)src_areas[0].addr) + (src_ofs * channels * fbytes),
71                        size * channels * fbytes);
72         } else {
73                 for (chn = 0; chn < channels; chn++) {
74                         schn = dsnoop->bindings ? dsnoop->bindings[chn] : chn;
75                         snd_pcm_area_copy(&dst_areas[chn], dst_ofs, &src_areas[schn], src_ofs, size, format);
76                 }
77         }
78 }
79
80 /*
81  *  synchronize shm ring buffer with hardware
82  */
83 static void snd_pcm_dsnoop_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr, snd_pcm_uframes_t size)
84 {
85         snd_pcm_direct_t *dsnoop = pcm->private_data;
86         snd_pcm_uframes_t hw_ptr = dsnoop->hw_ptr;
87         snd_pcm_uframes_t transfer;
88         const snd_pcm_channel_area_t *src_areas, *dst_areas;
89         
90         /* add sample areas here */
91         dst_areas = snd_pcm_mmap_areas(pcm);
92         src_areas = snd_pcm_mmap_areas(dsnoop->spcm);
93         hw_ptr %= pcm->buffer_size;
94         slave_hw_ptr %= dsnoop->shmptr->s.buffer_size;
95         while (size > 0) {
96                 transfer = hw_ptr + size > pcm->buffer_size ? pcm->buffer_size - hw_ptr : size;
97                 transfer = slave_hw_ptr + transfer > dsnoop->shmptr->s.buffer_size ? dsnoop->shmptr->s.buffer_size - slave_hw_ptr : transfer;
98                 size -= transfer;
99                 snoop_areas(dsnoop, src_areas, dst_areas, slave_hw_ptr, hw_ptr, transfer);
100                 slave_hw_ptr += transfer;
101                 slave_hw_ptr %= dsnoop->shmptr->s.buffer_size;
102                 hw_ptr += transfer;
103                 hw_ptr %= pcm->buffer_size;
104         }
105 }
106
107 /*
108  *  synchronize hardware pointer (hw_ptr) with ours
109  */
110 static snd_pcm_sframes_t snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
111 {
112         snd_pcm_direct_t *dsnoop = pcm->private_data;
113         snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
114         snd_pcm_sframes_t diff;
115         
116         old_slave_hw_ptr = dsnoop->slave_hw_ptr;
117         slave_hw_ptr = dsnoop->slave_hw_ptr = *dsnoop->spcm->hw.ptr;
118         diff = slave_hw_ptr - old_slave_hw_ptr;
119         if (diff == 0)          /* fast path */
120                 return 0;
121         if (diff < 0) {
122                 slave_hw_ptr += dsnoop->shmptr->s.boundary;
123                 diff = slave_hw_ptr - old_slave_hw_ptr;
124         }
125         snd_pcm_dsnoop_sync_area(pcm, old_slave_hw_ptr, diff);
126         dsnoop->hw_ptr += diff;
127         dsnoop->hw_ptr %= pcm->boundary;
128         // printf("sync ptr diff = %li\n", diff);
129         if (pcm->stop_threshold >= pcm->boundary)       /* don't care */
130                 return 0;
131         if ((avail = snd_pcm_mmap_capture_hw_avail(pcm)) >= pcm->stop_threshold) {
132                 struct timeval tv;
133                 gettimeofday(&tv, 0);
134                 dsnoop->trigger_tstamp.tv_sec = tv.tv_sec;
135                 dsnoop->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
136                 dsnoop->state = SND_PCM_STATE_XRUN;
137                 dsnoop->avail_max = avail;
138                 return -EPIPE;
139         }
140         if (avail > dsnoop->avail_max)
141                 dsnoop->avail_max = avail;
142         return diff;
143 }
144
145 /*
146  *  plugin implementation
147  */
148
149 static int snd_pcm_dsnoop_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
150 {
151         /* value is cached for us in pcm->mode (SND_PCM_NONBLOCK flag) */
152         return 0;
153 }
154
155 static int snd_pcm_dsnoop_async(snd_pcm_t *pcm, int sig, pid_t pid)
156 {
157         snd_pcm_direct_t *dsnoop = pcm->private_data;
158         return snd_timer_async(dsnoop->timer, sig, pid);
159 }
160
161 static int snd_pcm_dsnoop_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
162 {
163         snd_pcm_direct_t *dsnoop = pcm->private_data;
164         unsigned short events;
165         static snd_timer_read_t rbuf[5];        /* can be overwriten by multiple plugins, we don't need the value */
166
167         assert(pfds && nfds == 1 && revents);
168         events = pfds[0].revents;
169         if (events & POLLIN) {
170                 events |= POLLOUT;
171                 events &= ~POLLIN;
172                 /* empty the timer read queue */
173                 while (snd_timer_read(dsnoop->timer, &rbuf, sizeof(rbuf)) == sizeof(rbuf)) ;
174         }
175         *revents = events;
176         return 0;
177 }
178
179 static int snd_pcm_dsnoop_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
180 {
181         // snd_pcm_direct_t *dsnoop = pcm->private_data;
182
183         memset(info, 0, sizeof(*info));
184         info->stream = pcm->stream;
185         info->card = -1;
186         /* FIXME: fill this with something more useful: we know the hardware name */
187         if (pcm->name) {
188                 strncpy(info->id, pcm->name, sizeof(info->id));
189                 strncpy(info->name, pcm->name, sizeof(info->name));
190                 strncpy(info->subname, pcm->name, sizeof(info->subname));
191         }
192         info->subdevices_count = 1;
193         return 0;
194 }
195
196 static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params,
197                                         snd_pcm_hw_param_t var)
198 {
199         return &params->masks[var - SND_PCM_HW_PARAM_FIRST_MASK];
200 }
201
202 static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
203                                                 snd_pcm_hw_param_t var)
204 {
205         return &params->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL];
206 }
207
208 static int hw_param_interval_refine_one(snd_pcm_hw_params_t *params,
209                                         snd_pcm_hw_param_t var,
210                                         snd_pcm_hw_params_t *src)
211 {
212         snd_interval_t *i;
213
214         if (!(params->rmask & (1<<var)))        /* nothing to do? */
215                 return 0;
216         i = hw_param_interval(params, var);
217         if (snd_interval_empty(i)) {
218                 SNDERR("dsnoop interval %i empty?", (int)var);
219                 return -EINVAL;
220         }
221         if (snd_interval_refine(i, hw_param_interval(src, var)))
222                 params->cmask |= 1<<var;
223         return 0;
224 }
225
226 #undef REFINE_DEBUG
227
228 static int snd_pcm_dsnoop_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
229 {
230         snd_pcm_direct_t *dsnoop = pcm->private_data;
231         snd_pcm_hw_params_t *hw_params = &dsnoop->shmptr->hw_params;
232         static snd_mask_t access = { .bits = { 
233                                         (1<<SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) |
234                                         (1<<SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) |
235                                         (1<<SNDRV_PCM_ACCESS_RW_INTERLEAVED) |
236                                         (1<<SNDRV_PCM_ACCESS_RW_NONINTERLEAVED),
237                                         0, 0, 0 } };
238         int err;
239
240 #ifdef REFINE_DEBUG
241         snd_output_t *log;
242         snd_output_stdio_attach(&log, stderr, 0);
243         snd_output_puts(log, "DMIX REFINE (begin):\n");
244         snd_pcm_hw_params_dump(params, log);
245 #endif
246         if (params->rmask & (1<<SND_PCM_HW_PARAM_ACCESS)) {
247                 if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS))) {
248                         SNDERR("dsnoop access mask empty?");
249                         return -EINVAL;
250                 }
251                 if (snd_mask_refine(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS), &access))
252                         params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
253         }
254         if (params->rmask & (1<<SND_PCM_HW_PARAM_FORMAT)) {
255                 if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT))) {
256                         SNDERR("dsnoop format mask empty?");
257                         return -EINVAL;
258                 }
259                 if (snd_mask_refine_set(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT),
260                                         snd_mask_value(hw_param_mask(hw_params, SND_PCM_HW_PARAM_FORMAT))))
261                         params->cmask |= 1<<SND_PCM_HW_PARAM_FORMAT;
262         }
263         //snd_mask_none(hw_param_mask(params, SND_PCM_HW_PARAM_SUBFORMAT));
264         if (params->rmask & (1<<SND_PCM_HW_PARAM_CHANNELS)) {
265                 if (snd_interval_empty(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS))) {
266                         SNDERR("dsnoop channels mask empty?");
267                         return -EINVAL;
268                 }
269                 err = snd_interval_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS), dsnoop->channels);
270                 if (err < 0)
271                         return err;
272         }
273         err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_RATE, hw_params);
274         if (err < 0)
275                 return err;
276         err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE, hw_params);
277         if (err < 0)
278                 return err;
279         err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_TIME, hw_params);
280         if (err < 0)
281                 return err;
282         err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE, hw_params);
283         if (err < 0)
284                 return err;
285         err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME, hw_params);
286         if (err < 0)
287                 return err;
288         err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIODS, hw_params);
289         if (err < 0)
290                 return err;
291 #ifdef REFINE_DEBUG
292         snd_output_puts(log, "DMIX REFINE (end):\n");
293         snd_pcm_hw_params_dump(params, log);
294         snd_output_close(log);
295 #endif
296         return 0;
297 }
298
299 static int snd_pcm_dsnoop_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED)
300 {
301         /* values are cached in the pcm structure */
302         
303         return 0;
304 }
305
306 static int snd_pcm_dsnoop_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
307 {
308         /* values are cached in the pcm structure */
309         return 0;
310 }
311
312 static int snd_pcm_dsnoop_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED)
313 {
314         /* values are cached in the pcm structure */
315         return 0;
316 }
317
318 static int snd_pcm_dsnoop_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
319 {
320         return snd_pcm_channel_info_shm(pcm, info, -1);
321 }
322
323 static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
324 {
325         snd_pcm_direct_t *dsnoop = pcm->private_data;
326
327         switch(dsnoop->state) {
328         case SNDRV_PCM_STATE_DRAINING:
329         case SNDRV_PCM_STATE_RUNNING:
330                 snd_pcm_dsnoop_sync_ptr(pcm);
331                 break;
332         default:
333                 break;
334         }
335         memset(status, 0, sizeof(*status));
336         status->state = dsnoop->state;
337         status->trigger_tstamp = dsnoop->trigger_tstamp;
338         status->tstamp = snd_pcm_hw_fast_tstamp(dsnoop->spcm);
339         status->avail = snd_pcm_mmap_capture_avail(pcm);
340         status->avail_max = status->avail > dsnoop->avail_max ? status->avail : dsnoop->avail_max;
341         dsnoop->avail_max = 0;
342         return 0;
343 }
344
345 static snd_pcm_state_t snd_pcm_dsnoop_state(snd_pcm_t *pcm)
346 {
347         snd_pcm_direct_t *dsnoop = pcm->private_data;
348         return dsnoop->state;
349 }
350
351 static int snd_pcm_dsnoop_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
352 {
353         snd_pcm_direct_t *dsnoop = pcm->private_data;
354         int err;
355         
356         switch(dsnoop->state) {
357         case SNDRV_PCM_STATE_DRAINING:
358         case SNDRV_PCM_STATE_RUNNING:
359                 err = snd_pcm_dsnoop_sync_ptr(pcm);
360                 if (err < 0)
361                         return err;
362         case SNDRV_PCM_STATE_PREPARED:
363         case SNDRV_PCM_STATE_SUSPENDED:
364                 *delayp = snd_pcm_mmap_capture_hw_avail(pcm);
365                 return 0;
366         case SNDRV_PCM_STATE_XRUN:
367                 return -EPIPE;
368         default:
369                 return -EBADFD;
370         }
371 }
372
373 static int snd_pcm_dsnoop_hwsync(snd_pcm_t *pcm)
374 {
375         snd_pcm_direct_t *dsnoop = pcm->private_data;
376
377         switch(dsnoop->state) {
378         case SNDRV_PCM_STATE_DRAINING:
379         case SNDRV_PCM_STATE_RUNNING:
380                 return snd_pcm_dsnoop_sync_ptr(pcm);
381         case SNDRV_PCM_STATE_PREPARED:
382         case SNDRV_PCM_STATE_SUSPENDED:
383                 return 0;
384         case SNDRV_PCM_STATE_XRUN:
385                 return -EPIPE;
386         default:
387                 return -EBADFD;
388         }
389 }
390
391 static int snd_pcm_dsnoop_prepare(snd_pcm_t *pcm)
392 {
393         snd_pcm_direct_t *dsnoop = pcm->private_data;
394
395         snd_pcm_direct_check_interleave(dsnoop, pcm);
396         // assert(pcm->boundary == dsnoop->shmptr->s.boundary); /* for sure */
397         dsnoop->state = SND_PCM_STATE_PREPARED;
398         dsnoop->appl_ptr = 0;
399         dsnoop->hw_ptr = 0;
400         return 0;
401 }
402
403 static int snd_pcm_dsnoop_reset(snd_pcm_t *pcm)
404 {
405         snd_pcm_direct_t *dsnoop = pcm->private_data;
406         dsnoop->hw_ptr %= pcm->period_size;
407         dsnoop->appl_ptr = dsnoop->hw_ptr;
408         dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr = *dsnoop->spcm->hw.ptr;
409         return 0;
410 }
411
412 static int snd_pcm_dsnoop_start(snd_pcm_t *pcm)
413 {
414         snd_pcm_direct_t *dsnoop = pcm->private_data;
415         struct timeval tv;
416         int err;
417         
418         if (dsnoop->state != SND_PCM_STATE_PREPARED)
419                 return -EBADFD;
420         err = snd_timer_start(dsnoop->timer);
421         if (err < 0)
422                 return err;
423         dsnoop->state = SND_PCM_STATE_RUNNING;
424         dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr = *dsnoop->spcm->hw.ptr;
425         gettimeofday(&tv, 0);
426         dsnoop->trigger_tstamp.tv_sec = tv.tv_sec;
427         dsnoop->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
428         return 0;
429 }
430
431 static int snd_pcm_dsnoop_drop(snd_pcm_t *pcm)
432 {
433         snd_pcm_direct_t *dsnoop = pcm->private_data;
434         if (dsnoop->state == SND_PCM_STATE_OPEN)
435                 return -EBADFD;
436         snd_timer_stop(dsnoop->timer);
437         dsnoop->state = SND_PCM_STATE_SETUP;
438         return 0;
439 }
440
441 static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
442 {
443         snd_pcm_direct_t *dsnoop = pcm->private_data;
444         snd_pcm_uframes_t stop_threshold;
445         int err;
446
447         if (dsnoop->state == SND_PCM_STATE_OPEN)
448                 return -EBADFD;
449         stop_threshold = pcm->stop_threshold;
450         if (pcm->stop_threshold > pcm->buffer_size)
451                 pcm->stop_threshold = pcm->buffer_size;
452         while (dsnoop->state == SND_PCM_STATE_RUNNING) {
453                 err = snd_pcm_dsnoop_sync_ptr(pcm);
454                 if (err < 0)
455                         break;
456                 if (pcm->mode & SND_PCM_NONBLOCK)
457                         return -EAGAIN;
458                 snd_pcm_wait(pcm, -1);
459         }
460         pcm->stop_threshold = stop_threshold;
461         return snd_pcm_dsnoop_drop(pcm);
462 }
463
464 static int snd_pcm_dsnoop_pause(snd_pcm_t *pcm, int enable)
465 {
466         snd_pcm_direct_t *dsnoop = pcm->private_data;
467         if (enable) {
468                 if (dsnoop->state != SND_PCM_STATE_RUNNING)
469                         return -EBADFD;
470                 dsnoop->state = SND_PCM_STATE_PAUSED;
471                 snd_timer_stop(dsnoop->timer);
472         } else {
473                 if (dsnoop->state != SND_PCM_STATE_PAUSED)
474                         return -EBADFD;
475                 dsnoop->state = SND_PCM_STATE_RUNNING;
476                 snd_timer_start(dsnoop->timer);
477         }
478         return 0;
479 }
480
481 static snd_pcm_sframes_t snd_pcm_dsnoop_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
482 {
483         snd_pcm_mmap_appl_backward(pcm, frames);
484         return frames;
485 }
486
487 static snd_pcm_sframes_t snd_pcm_dsnoop_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
488 {
489         snd_pcm_sframes_t avail;
490
491         avail = snd_pcm_mmap_capture_hw_avail(pcm);
492         if (avail < 0)
493                 return 0;
494         if (frames > (snd_pcm_uframes_t)avail)
495                 frames = avail;
496         snd_pcm_mmap_appl_forward(pcm, frames);
497         return frames;
498 }
499
500 static int snd_pcm_dsnoop_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
501 {
502         // snd_pcm_direct_t *dsnoop = pcm->private_data;
503         // FIXME
504         return 0;
505 }
506
507 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)
508 {
509         return -ENODEV;
510 }
511
512 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)
513 {
514         return -ENODEV;
515 }
516
517 static int snd_pcm_dsnoop_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
518 {
519         return 0;
520 }
521
522 static int snd_pcm_dsnoop_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
523 {
524         return 0;
525 }
526
527 static int snd_pcm_dsnoop_close(snd_pcm_t *pcm)
528 {
529         snd_pcm_direct_t *dsnoop = pcm->private_data;
530
531         if (dsnoop->timer)
532                 snd_timer_close(dsnoop->timer);
533         snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
534         snd_pcm_close(dsnoop->spcm);
535         if (dsnoop->server)
536                 snd_pcm_direct_server_discard(dsnoop);
537         if (dsnoop->client)
538                 snd_pcm_direct_client_discard(dsnoop);
539         if (snd_pcm_direct_shm_discard(dsnoop) > 0) {
540                 if (snd_pcm_direct_semaphore_discard(dsnoop) < 0)
541                         snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
542         } else {
543                 snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
544         }
545         if (dsnoop->bindings)
546                 free(dsnoop->bindings);
547         pcm->private_data = NULL;
548         free(dsnoop);
549         return 0;
550 }
551
552 static snd_pcm_sframes_t snd_pcm_dsnoop_mmap_commit(snd_pcm_t *pcm,
553                                                     snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
554                                                     snd_pcm_uframes_t size)
555 {
556         snd_pcm_direct_t *dsnoop = pcm->private_data;
557         int err;
558
559         if (dsnoop->state == SND_PCM_STATE_RUNNING) {
560                 err = snd_pcm_dsnoop_sync_ptr(pcm);
561                 if (err < 0)
562                         return err;
563         }
564         snd_pcm_mmap_appl_forward(pcm, size);
565         return size;
566 }
567
568 static snd_pcm_sframes_t snd_pcm_dsnoop_avail_update(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
569 {
570         snd_pcm_direct_t *dsnoop = pcm->private_data;
571         int err;
572         
573         if (dsnoop->state == SND_PCM_STATE_RUNNING) {
574                 err = snd_pcm_dsnoop_sync_ptr(pcm);
575                 if (err < 0)
576                         return err;
577         }
578         return snd_pcm_mmap_capture_avail(pcm);
579 }
580
581 static void snd_pcm_dsnoop_dump(snd_pcm_t *pcm, snd_output_t *out)
582 {
583         snd_pcm_direct_t *dsnoop = pcm->private_data;
584
585         snd_output_printf(out, "Direct Stream Mixing PCM\n");
586         if (pcm->setup) {
587                 snd_output_printf(out, "\nIts setup is:\n");
588                 snd_pcm_dump_setup(pcm, out);
589         }
590         if (dsnoop->spcm)
591                 snd_pcm_dump(dsnoop->spcm, out);
592 }
593
594 static snd_pcm_ops_t snd_pcm_dsnoop_ops = {
595         .close = snd_pcm_dsnoop_close,
596         .info = snd_pcm_dsnoop_info,
597         .hw_refine = snd_pcm_dsnoop_hw_refine,
598         .hw_params = snd_pcm_dsnoop_hw_params,
599         .hw_free = snd_pcm_dsnoop_hw_free,
600         .sw_params = snd_pcm_dsnoop_sw_params,
601         .channel_info = snd_pcm_dsnoop_channel_info,
602         .dump = snd_pcm_dsnoop_dump,
603         .nonblock = snd_pcm_dsnoop_nonblock,
604         .async = snd_pcm_dsnoop_async,
605         .poll_revents = snd_pcm_dsnoop_poll_revents,
606         .mmap = snd_pcm_dsnoop_mmap,
607         .munmap = snd_pcm_dsnoop_munmap,
608 };
609
610 static snd_pcm_fast_ops_t snd_pcm_dsnoop_fast_ops = {
611         .status = snd_pcm_dsnoop_status,
612         .state = snd_pcm_dsnoop_state,
613         .hwsync = snd_pcm_dsnoop_hwsync,
614         .delay = snd_pcm_dsnoop_delay,
615         .prepare = snd_pcm_dsnoop_prepare,
616         .reset = snd_pcm_dsnoop_reset,
617         .start = snd_pcm_dsnoop_start,
618         .drop = snd_pcm_dsnoop_drop,
619         .drain = snd_pcm_dsnoop_drain,
620         .pause = snd_pcm_dsnoop_pause,
621         .rewind = snd_pcm_dsnoop_rewind,
622         .forward = snd_pcm_dsnoop_forward,
623         .resume = snd_pcm_dsnoop_resume,
624         .writei = snd_pcm_dsnoop_writei,
625         .writen = snd_pcm_dsnoop_writen,
626         .readi = snd_pcm_mmap_readi,
627         .readn = snd_pcm_mmap_readn,
628         .avail_update = snd_pcm_dsnoop_avail_update,
629         .mmap_commit = snd_pcm_dsnoop_mmap_commit,
630 };
631
632 /**
633  * \brief Creates a new dsnoop PCM
634  * \param pcmp Returns created PCM handle
635  * \param name Name of PCM
636  * \param ipc_key IPC key for semaphore and shared memory
637  * \param params Parameters for slave
638  * \param root Configuration root
639  * \param sconf Slave configuration
640  * \param stream PCM Direction (stream)
641  * \param mode PCM Mode
642  * \retval zero on success otherwise a negative error code
643  * \warning Using of this function might be dangerous in the sense
644  *          of compatibility reasons. The prototype might be freely
645  *          changed in future.
646  */
647 int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
648                       key_t ipc_key, struct slave_params *params,
649                       snd_config_t *bindings,
650                       snd_config_t *root, snd_config_t *sconf,
651                       snd_pcm_stream_t stream, int mode)
652 {
653         snd_pcm_t *pcm = NULL, *spcm = NULL;
654         snd_pcm_direct_t *dsnoop = NULL;
655         int ret, first_instance, fail_sem_loop = 10;
656
657         assert(pcmp);
658
659         if (stream != SND_PCM_STREAM_CAPTURE) {
660                 SNDERR("The dsnoop plugin supports only capture stream");
661                 return -EINVAL;
662         }
663
664         dsnoop = calloc(1, sizeof(snd_pcm_direct_t));
665         if (!dsnoop) {
666                 ret = -ENOMEM;
667                 goto _err;
668         }
669         
670         ret = snd_pcm_direct_parse_bindings(dsnoop, bindings);
671         if (ret < 0)
672                 goto _err;
673         
674         dsnoop->ipc_key = ipc_key;
675         dsnoop->semid = -1;
676         dsnoop->shmid = -1;
677
678         ret = snd_pcm_new(&pcm, dsnoop->type = SND_PCM_TYPE_DSNOOP, name, stream, mode);
679         if (ret < 0)
680                 goto _err;
681
682         while (1) {
683                 ret = snd_pcm_direct_semaphore_create_or_connect(dsnoop);
684                 if (ret < 0) {
685                         SNDERR("unable to create IPC semaphore");
686                         goto _err;
687                 }
688         
689                 ret = snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
690                 if (ret < 0) {
691                         snd_pcm_direct_semaphore_discard(dsnoop);
692                         if (--fail_sem_loop <= 0)
693                                 goto _err;
694                         continue;
695                 }
696                 break;
697         }
698                 
699         first_instance = ret = snd_pcm_direct_shm_create_or_connect(dsnoop);
700         if (ret < 0) {
701                 SNDERR("unable to create IPC shm instance");
702                 goto _err;
703         }
704                 
705         pcm->ops = &snd_pcm_dsnoop_ops;
706         pcm->fast_ops = &snd_pcm_dsnoop_fast_ops;
707         pcm->private_data = dsnoop;
708         dsnoop->state = SND_PCM_STATE_OPEN;
709
710         if (first_instance) {
711                 ret = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
712                 if (ret < 0) {
713                         SNDERR("unable to open slave");
714                         goto _err;
715                 }
716         
717                 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
718                         SNDERR("dsnoop plugin can be only connected to hw plugin");
719                         goto _err;
720                 }
721                 
722                 ret = snd_pcm_direct_initialize_slave(dsnoop, spcm, params);
723                 if (ret < 0) {
724                         SNDERR("unable to initialize slave");
725                         goto _err;
726                 }
727
728                 dsnoop->spcm = spcm;
729                 
730                 ret = snd_pcm_direct_server_create(dsnoop);
731                 if (ret < 0) {
732                         SNDERR("unable to create server");
733                         goto _err;
734                 }
735
736                 dsnoop->shmptr->type = spcm->type;
737         } else {
738                 ret = snd_pcm_direct_client_connect(dsnoop);
739                 if (ret < 0) {
740                         SNDERR("unable to connect client");
741                         return ret;
742                 }
743                         
744                 ret = snd_pcm_hw_open_fd(&spcm, "dsnoop_client", dsnoop->hw_fd, 0);
745                 if (ret < 0) {
746                         SNDERR("unable to open hardware");
747                         goto _err;
748                 }
749                 
750                 spcm->donot_close = 1;
751                 spcm->setup = 1;
752                 spcm->buffer_size = dsnoop->shmptr->s.buffer_size;
753                 spcm->sample_bits = dsnoop->shmptr->s.sample_bits;
754                 spcm->channels = dsnoop->shmptr->s.channels;
755                 spcm->format = dsnoop->shmptr->s.format;
756                 spcm->boundary = dsnoop->shmptr->s.boundary;
757                 ret = snd_pcm_mmap(spcm);
758                 if (ret < 0) {
759                         SNDERR("unable to mmap channels");
760                         goto _err;
761                 }
762                 dsnoop->spcm = spcm;
763         }
764
765         ret = snd_pcm_direct_initialize_poll_fd(dsnoop);
766         if (ret < 0) {
767                 SNDERR("unable to initialize poll_fd");
768                 goto _err;
769         }
770
771         pcm->poll_fd = dsnoop->poll_fd;
772         pcm->poll_events = POLLIN;      /* it's different than other plugins */
773                 
774         pcm->mmap_rw = 1;
775         snd_pcm_set_hw_ptr(pcm, &dsnoop->hw_ptr, -1, 0);
776         snd_pcm_set_appl_ptr(pcm, &dsnoop->appl_ptr, -1, 0);
777         
778         if (dsnoop->channels == UINT_MAX)
779                 dsnoop->channels = dsnoop->shmptr->s.channels;
780         
781         snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
782
783         *pcmp = pcm;
784         return 0;
785         
786  _err:
787         if (dsnoop) {
788                 if (dsnoop->timer)
789                         snd_timer_close(dsnoop->timer);
790                 if (dsnoop->server)
791                         snd_pcm_direct_server_discard(dsnoop);
792                 if (dsnoop->client)
793                         snd_pcm_direct_client_discard(dsnoop);
794                 if (spcm)
795                         snd_pcm_close(spcm);
796                 if (dsnoop->shmid >= 0) {
797                         if (snd_pcm_direct_shm_discard(dsnoop) > 0) {
798                                 if (dsnoop->semid >= 0) {
799                                         if (snd_pcm_direct_semaphore_discard(dsnoop) < 0)
800                                                 snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
801                                 }
802                         }
803                 }
804                 if (dsnoop->bindings)
805                         free(dsnoop->bindings);
806                 free(dsnoop);
807         }
808         if (pcm)
809                 snd_pcm_free(pcm);
810         return ret;
811 }
812
813 /*! \page pcm_plugins
814
815 \section pcm_plugins_snoop Plugin: dsnoop
816
817 This plugin splits one capture stream to more.
818
819 \code
820 pcm.name {
821         type dsnoop             # Direct snoop
822         ipc_key INT             # unique IPC key
823         ipc_key_add_uid BOOL    # add current uid to unique IPC key
824         slave STR
825         # or
826         slave {                 # Slave definition
827                 pcm STR         # slave PCM name
828                 # or
829                 pcm { }         # slave PCM definition
830                 format STR      # format definition
831                 rate INT        # rate definition
832                 channels INT
833                 period_time INT # in usec
834                 # or
835                 period_size INT # in bytes
836                 buffer_time INT # in usec
837                 # or
838                 buffer_size INT # in bytes
839                 periods INT     # when buffer_size or buffer_time is not specified
840         }
841         bindings {              # note: this is client independent!!!
842                 N INT           # maps slave channel to client channel N
843         }
844 }
845 \endcode
846
847 \subsection pcm_plugins_dsnoop_funcref Function reference
848
849 <UL>
850   <LI>snd_pcm_dsnoop_open()
851   <LI>_snd_pcm_dsnoop_open()
852 </UL>
853
854 */
855
856 /**
857  * \brief Creates a new dsnoop PCM
858  * \param pcmp Returns created PCM handle
859  * \param name Name of PCM
860  * \param root Root configuration node
861  * \param conf Configuration node with dsnoop PCM description
862  * \param stream PCM Stream
863  * \param mode PCM Mode
864  * \warning Using of this function might be dangerous in the sense
865  *          of compatibility reasons. The prototype might be freely
866  *          changed in future.
867  */
868 int _snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
869                        snd_config_t *root, snd_config_t *conf,
870                        snd_pcm_stream_t stream, int mode)
871 {
872         snd_config_iterator_t i, next;
873         snd_config_t *slave = NULL, *bindings = NULL, *sconf;
874         struct slave_params params;
875         int bsize, psize, ipc_key_add_uid = 0;
876         key_t ipc_key = 0;
877         int err;
878         snd_config_for_each(i, next, conf) {
879                 snd_config_t *n = snd_config_iterator_entry(i);
880                 const char *id;
881                 if (snd_config_get_id(n, &id) < 0)
882                         continue;
883                 if (snd_pcm_conf_generic_id(id))
884                         continue;
885                 if (strcmp(id, "ipc_key") == 0) {
886                         long key;
887                         err = snd_config_get_integer(n, &key);
888                         if (err < 0) {
889                                 SNDERR("The field ipc_key must be an integer type");
890                                 return err;
891                         }
892                         ipc_key = key;
893                         continue;
894                 }
895                 if (strcmp(id, "ipc_key_add_uid") == 0) {
896                         char *tmp;
897                         err = snd_config_get_ascii(n, &tmp);
898                         if (err < 0) {
899                                 SNDERR("The field ipc_key_add_uid must be a boolean type");
900                                 return err;
901                         }
902                         err = snd_config_get_bool_ascii(tmp);
903                         free(tmp);
904                         if (err < 0) {
905                                 SNDERR("The field ipc_key_add_uid must be a boolean type");
906                                 return err;
907                         }
908                         ipc_key_add_uid = err;
909                         continue;
910                 }
911                 if (strcmp(id, "slave") == 0) {
912                         slave = n;
913                         continue;
914                 }
915                 if (strcmp(id, "bindings") == 0) {
916                         bindings = n;
917                         continue;
918                 }
919                 SNDERR("Unknown field %s", id);
920                 return -EINVAL;
921         }
922         if (!slave) {
923                 SNDERR("slave is not defined");
924                 return -EINVAL;
925         }
926         if (ipc_key_add_uid)
927                 ipc_key += getuid();
928         if (!ipc_key) {
929                 SNDERR("Unique IPC key is not defined");
930                 return -EINVAL;
931         }
932         /* the default settings, it might be invalid for some hardware */
933         params.format = SND_PCM_FORMAT_S16;
934         params.rate = 48000;
935         params.channels = 2;
936         params.period_time = 125000;    /* 0.125 seconds */
937         params.buffer_time = -1;
938         bsize = psize = -1;
939         params.periods = 3;
940         err = snd_pcm_slave_conf(root, slave, &sconf, 8,
941                                  SND_PCM_HW_PARAM_FORMAT, 0, &params.format,
942                                  SND_PCM_HW_PARAM_RATE, 0, &params.rate,
943                                  SND_PCM_HW_PARAM_CHANNELS, 0, &params.channels,
944                                  SND_PCM_HW_PARAM_PERIOD_TIME, 0, &params.period_time,
945                                  SND_PCM_HW_PARAM_BUFFER_TIME, 0, &params.buffer_time,
946                                  SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
947                                  SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
948                                  SND_PCM_HW_PARAM_PERIODS, 0, &params.periods);
949         if (err < 0)
950                 return err;
951
952         /* sorry, limited features */
953         if (params.format != SND_PCM_FORMAT_S16 &&
954             params.format != SND_PCM_FORMAT_S32) {
955                 SNDERR("invalid format, specify s16 or s32");
956                 snd_config_delete(sconf);
957                 return -EINVAL;
958         }
959
960         params.period_size = psize;
961         params.buffer_size = bsize;
962         err = snd_pcm_dsnoop_open(pcmp, name, ipc_key, &params, bindings, root, sconf, stream, mode);
963         if (err < 0)
964                 snd_config_delete(sconf);
965         return err;
966 }
967 #ifndef DOC_HIDDEN
968 SND_DLSYM_BUILD_VERSION(_snd_pcm_dsnoop_open, SND_PCM_DLSYM_VERSION);
969 #endif