OSDN Git Service

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