OSDN Git Service

31905fde950248f8bc212027850bb8fea352ee76
[android-x86/external-alsa-lib.git] / src / pcm / pcm_dshare.c
1 /**
2  * \file pcm/pcm_dshare.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Direct Sharing of Channels Plugin Interface
5  * \author Jaroslav Kysela <perex@suse.cz>
6  * \date 2003
7  */
8 /*
9  *  PCM - Direct Sharing of Channels
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 <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_dshare = "";
51 #endif
52
53 #ifndef DOC_HIDDEN
54 /* start is pending - this state happens when rate plugin does a delayed commit */
55 #define STATE_RUN_PENDING       1024
56 #endif
57
58 static void do_silence(snd_pcm_t *pcm)
59 {
60         snd_pcm_direct_t *dshare = pcm->private_data;
61         const snd_pcm_channel_area_t *dst_areas;
62         unsigned int chn, dchn, channels;
63         snd_pcm_format_t format;
64
65         dst_areas = snd_pcm_mmap_areas(dshare->spcm);
66         channels = dshare->channels;
67         format = dshare->shmptr->s.format;
68         for (chn = 0; chn < channels; chn++) {
69                 dchn = dshare->bindings ? dshare->bindings[chn] : chn;
70                 snd_pcm_area_silence(&dst_areas[dchn], 0, dshare->shmptr->s.buffer_size, format);
71         }
72 }
73
74 static void share_areas(snd_pcm_direct_t *dshare,
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, dchn, channels;
82         snd_pcm_format_t format;
83
84         channels = dshare->channels;
85         format = dshare->shmptr->s.format;
86         if (dshare->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                         dchn = dshare->bindings ? dshare->bindings[chn] : chn;
94                         snd_pcm_area_copy(&dst_areas[dchn], dst_ofs, &src_areas[chn], src_ofs, size, format);
95
96                 }
97         }
98 }
99
100 /*
101  *  synchronize shm ring buffer with hardware
102  */
103 static void snd_pcm_dshare_sync_area(snd_pcm_t *pcm)
104 {
105         snd_pcm_direct_t *dshare = pcm->private_data;
106         snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size;
107         snd_pcm_uframes_t appl_ptr, size;
108         const snd_pcm_channel_area_t *src_areas, *dst_areas;
109         
110         /* calculate the size to transfer */
111         size = dshare->appl_ptr - dshare->last_appl_ptr;
112         if (! size)
113                 return;
114         slave_hw_ptr = dshare->slave_hw_ptr;
115         /* don't write on the last active period - this area may be cleared
116          * by the driver during write operation...
117          */
118         slave_hw_ptr -= slave_hw_ptr % dshare->slave_period_size;
119         slave_hw_ptr += dshare->slave_buffer_size;
120         if (dshare->slave_hw_ptr > dshare->slave_boundary)
121                 slave_hw_ptr -= dshare->slave_boundary;
122         if (slave_hw_ptr < dshare->slave_appl_ptr)
123                 slave_size = slave_hw_ptr + (dshare->slave_boundary - dshare->slave_appl_ptr);
124         else
125                 slave_size = slave_hw_ptr - dshare->slave_appl_ptr;
126         if (slave_size < size)
127                 size = slave_size;
128         if (! size)
129                 return;
130
131         /* add sample areas here */
132         src_areas = snd_pcm_mmap_areas(pcm);
133         dst_areas = snd_pcm_mmap_areas(dshare->spcm);
134         appl_ptr = dshare->last_appl_ptr % pcm->buffer_size;
135         dshare->last_appl_ptr += size;
136         dshare->last_appl_ptr %= pcm->boundary;
137         slave_appl_ptr = dshare->slave_appl_ptr % dshare->slave_buffer_size;
138         dshare->slave_appl_ptr += size;
139         dshare->slave_appl_ptr %= dshare->slave_boundary;
140         for (;;) {
141                 snd_pcm_uframes_t transfer = size;
142                 if (appl_ptr + transfer > pcm->buffer_size)
143                         transfer = pcm->buffer_size - appl_ptr;
144                 if (slave_appl_ptr + transfer > dshare->slave_buffer_size)
145                         transfer = dshare->slave_buffer_size - slave_appl_ptr;
146                 share_areas(dshare, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
147                 size -= transfer;
148                 if (! size)
149                         break;
150                 slave_appl_ptr += transfer;
151                 slave_appl_ptr %= dshare->slave_buffer_size;
152                 appl_ptr += transfer;
153                 appl_ptr %= pcm->buffer_size;
154         }
155 }
156
157 /*
158  *  synchronize hardware pointer (hw_ptr) with ours
159  */
160 static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm)
161 {
162         snd_pcm_direct_t *dshare = pcm->private_data;
163         snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
164         snd_pcm_sframes_t diff;
165         
166         switch (snd_pcm_state(dshare->spcm)) {
167         case SND_PCM_STATE_DISCONNECTED:
168                 dshare->state = SNDRV_PCM_STATE_DISCONNECTED;
169                 return -ENODEV;
170         default:
171                 break;
172         }
173         if (dshare->slowptr)
174                 snd_pcm_hwsync(dshare->spcm);
175         old_slave_hw_ptr = dshare->slave_hw_ptr;
176         slave_hw_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr;
177         diff = slave_hw_ptr - old_slave_hw_ptr;
178         if (diff == 0)          /* fast path */
179                 return 0;
180         if (dshare->state != SND_PCM_STATE_RUNNING &&
181             dshare->state != SND_PCM_STATE_DRAINING)
182                 /* not really started yet - don't update hw_ptr */
183                 return 0;
184         if (diff < 0) {
185                 slave_hw_ptr += dshare->slave_boundary;
186                 diff = slave_hw_ptr - old_slave_hw_ptr;
187         }
188         dshare->hw_ptr += diff;
189         dshare->hw_ptr %= pcm->boundary;
190         // printf("sync ptr diff = %li\n", diff);
191         if (pcm->stop_threshold >= pcm->boundary)       /* don't care */
192                 return 0;
193         avail = snd_pcm_mmap_playback_avail(pcm);
194         if (avail > dshare->avail_max)
195                 dshare->avail_max = avail;
196         if (avail >= pcm->stop_threshold) {
197                 struct timeval tv;
198                 snd_timer_stop(dshare->timer);
199                 gettimeofday(&tv, 0);
200                 dshare->trigger_tstamp.tv_sec = tv.tv_sec;
201                 dshare->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
202                 if (dshare->state == SND_PCM_STATE_RUNNING) {
203                         dshare->state = SND_PCM_STATE_XRUN;
204                         return -EPIPE;
205                 }
206                 dshare->state = SND_PCM_STATE_SETUP;
207                 /* clear queue to remove pending poll events */
208                 snd_pcm_direct_clear_timer_queue(dshare);
209         }
210         return 0;
211 }
212
213 /*
214  *  plugin implementation
215  */
216
217 static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
218 {
219         snd_pcm_direct_t *dshare = pcm->private_data;
220
221         switch (dshare->state) {
222         case SNDRV_PCM_STATE_DRAINING:
223         case SNDRV_PCM_STATE_RUNNING:
224                 snd_pcm_dshare_sync_ptr(pcm);
225                 break;
226         default:
227                 break;
228         }
229         memset(status, 0, sizeof(*status));
230         status->state = snd_pcm_state(dshare->spcm);
231         status->trigger_tstamp = dshare->trigger_tstamp;
232         status->tstamp = snd_pcm_hw_fast_tstamp(dshare->spcm);
233         status->avail = snd_pcm_mmap_playback_avail(pcm);
234         status->avail_max = status->avail > dshare->avail_max ? status->avail : dshare->avail_max;
235         dshare->avail_max = 0;
236         return 0;
237 }
238
239 static snd_pcm_state_t snd_pcm_dshare_state(snd_pcm_t *pcm)
240 {
241         snd_pcm_direct_t *dshare = pcm->private_data;
242         switch (snd_pcm_state(dshare->spcm)) {
243         case SND_PCM_STATE_SUSPENDED:
244                 return SND_PCM_STATE_SUSPENDED;
245         case SND_PCM_STATE_DISCONNECTED:
246                 return SND_PCM_STATE_DISCONNECTED;
247         default:
248                 break;
249         }
250         if (dshare->state == STATE_RUN_PENDING)
251                 return SNDRV_PCM_STATE_RUNNING;
252         return dshare->state;
253 }
254
255 static int snd_pcm_dshare_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
256 {
257         snd_pcm_direct_t *dshare = pcm->private_data;
258         int err;
259         
260         switch (dshare->state) {
261         case SNDRV_PCM_STATE_DRAINING:
262         case SNDRV_PCM_STATE_RUNNING:
263                 err = snd_pcm_dshare_sync_ptr(pcm);
264                 if (err < 0)
265                         return err;
266                 /* fallthru */
267         case SNDRV_PCM_STATE_PREPARED:
268         case SNDRV_PCM_STATE_SUSPENDED:
269         case STATE_RUN_PENDING:
270                 *delayp = snd_pcm_mmap_playback_hw_avail(pcm);
271                 return 0;
272         case SNDRV_PCM_STATE_XRUN:
273                 return -EPIPE;
274         case SNDRV_PCM_STATE_DISCONNECTED:
275                 return -ENODEV;
276         default:
277                 return -EBADFD;
278         }
279 }
280
281 static int snd_pcm_dshare_hwsync(snd_pcm_t *pcm)
282 {
283         snd_pcm_direct_t *dshare = pcm->private_data;
284
285         switch(dshare->state) {
286         case SNDRV_PCM_STATE_DRAINING:
287         case SNDRV_PCM_STATE_RUNNING:
288                 return snd_pcm_dshare_sync_ptr(pcm);
289         case SNDRV_PCM_STATE_PREPARED:
290         case SNDRV_PCM_STATE_SUSPENDED:
291                 return 0;
292         case SNDRV_PCM_STATE_XRUN:
293                 return -EPIPE;
294         case SNDRV_PCM_STATE_DISCONNECTED:
295                 return -ENODEV;
296         default:
297                 return -EBADFD;
298         }
299 }
300
301 static int snd_pcm_dshare_prepare(snd_pcm_t *pcm)
302 {
303         snd_pcm_direct_t *dshare = pcm->private_data;
304
305         snd_pcm_direct_check_interleave(dshare, pcm);
306         dshare->state = SND_PCM_STATE_PREPARED;
307         dshare->appl_ptr = dshare->last_appl_ptr = 0;
308         dshare->hw_ptr = 0;
309         return snd_pcm_direct_set_timer_params(dshare);
310 }
311
312 static int snd_pcm_dshare_reset(snd_pcm_t *pcm)
313 {
314         snd_pcm_direct_t *dshare = pcm->private_data;
315         dshare->hw_ptr %= pcm->period_size;
316         dshare->appl_ptr = dshare->last_appl_ptr = dshare->hw_ptr;
317         dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr;
318         return 0;
319 }
320
321 static int snd_pcm_dshare_start_timer(snd_pcm_direct_t *dshare)
322 {
323         int err;
324
325         snd_pcm_hwsync(dshare->spcm);
326         dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr;
327         err = snd_timer_start(dshare->timer);
328         if (err < 0)
329                 return err;
330         dshare->state = SND_PCM_STATE_RUNNING;
331         return 0;
332 }
333
334 static int snd_pcm_dshare_start(snd_pcm_t *pcm)
335 {
336         snd_pcm_direct_t *dshare = pcm->private_data;
337         snd_pcm_sframes_t avail;
338         struct timeval tv;
339         int err;
340         
341         if (dshare->state != SND_PCM_STATE_PREPARED)
342                 return -EBADFD;
343         avail = snd_pcm_mmap_playback_hw_avail(pcm);
344         if (avail == 0)
345                 dshare->state = STATE_RUN_PENDING;
346         else if (avail < 0)
347                 return 0;
348         else {
349                 if ((err = snd_pcm_dshare_start_timer(dshare)) < 0)
350                         return err;
351                 snd_pcm_dshare_sync_area(pcm);
352         }
353         gettimeofday(&tv, 0);
354         dshare->trigger_tstamp.tv_sec = tv.tv_sec;
355         dshare->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
356         return 0;
357 }
358
359 static int snd_pcm_dshare_drop(snd_pcm_t *pcm)
360 {
361         snd_pcm_direct_t *dshare = pcm->private_data;
362         if (dshare->state == SND_PCM_STATE_OPEN)
363                 return -EBADFD;
364         snd_pcm_direct_timer_stop(dshare);
365         do_silence(pcm);
366         dshare->state = SND_PCM_STATE_SETUP;
367         return 0;
368 }
369
370 static int snd_pcm_dshare_drain(snd_pcm_t *pcm)
371 {
372         snd_pcm_direct_t *dshare = pcm->private_data;
373         snd_pcm_uframes_t stop_threshold;
374         int err;
375
376         if (dshare->state == SND_PCM_STATE_OPEN)
377                 return -EBADFD;
378         if (pcm->mode & SND_PCM_NONBLOCK)
379                 return -EAGAIN;
380         if (dshare->state == SND_PCM_STATE_PREPARED) {
381                 if (snd_pcm_mmap_playback_hw_avail(pcm) > 0)
382                         snd_pcm_dshare_start(pcm);
383                 else {
384                         snd_pcm_dshare_drop(pcm);
385                         return 0;
386                 }
387         }
388
389         if (dshare->state == SND_PCM_STATE_XRUN) {
390                 snd_pcm_dshare_drop(pcm);
391                 return 0;
392         }
393
394         stop_threshold = pcm->stop_threshold;
395         if (pcm->stop_threshold > pcm->buffer_size)
396                 pcm->stop_threshold = pcm->buffer_size;
397         dshare->state = SND_PCM_STATE_DRAINING;
398         do {
399                 err = snd_pcm_dshare_sync_ptr(pcm);
400                 if (err < 0) {
401                         snd_pcm_dshare_drop(pcm);
402                         break;
403                 }
404                 if (dshare->state == SND_PCM_STATE_DRAINING) {
405                         snd_pcm_dshare_sync_area(pcm);
406                         snd_pcm_wait_nocheck(pcm, -1);
407                         snd_pcm_direct_clear_timer_queue(dshare); /* force poll to wait */
408                 }
409         } while (dshare->state == SND_PCM_STATE_DRAINING);
410         pcm->stop_threshold = stop_threshold;
411         return 0;
412 }
413
414 static int snd_pcm_dshare_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
415 {
416         return -EIO;
417 }
418
419 static snd_pcm_sframes_t snd_pcm_dshare_rewind(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
420 {
421 #if 0
422         /* FIXME: substract samples from the mix ring buffer, too? */
423         snd_pcm_mmap_appl_backward(pcm, frames);
424         return frames;
425 #else
426         return -EIO;
427 #endif
428 }
429
430 static snd_pcm_sframes_t snd_pcm_dshare_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
431 {
432         snd_pcm_sframes_t avail;
433
434         avail = snd_pcm_mmap_playback_avail(pcm);
435         if (avail < 0)
436                 return 0;
437         if (frames > (snd_pcm_uframes_t)avail)
438                 frames = avail;
439         snd_pcm_mmap_appl_forward(pcm, frames);
440         return frames;
441 }
442
443 static snd_pcm_sframes_t snd_pcm_dshare_readi(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
444 {
445         return -ENODEV;
446 }
447
448 static snd_pcm_sframes_t snd_pcm_dshare_readn(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
449 {
450         return -ENODEV;
451 }
452
453 static int snd_pcm_dshare_close(snd_pcm_t *pcm)
454 {
455         snd_pcm_direct_t *dshare = pcm->private_data;
456
457         if (dshare->timer)
458                 snd_timer_close(dshare->timer);
459         do_silence(pcm);
460         snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT);
461         dshare->shmptr->u.dshare.chn_mask &= ~dshare->u.dshare.chn_mask;
462         snd_pcm_close(dshare->spcm);
463         if (dshare->server)
464                 snd_pcm_direct_server_discard(dshare);
465         if (dshare->client)
466                 snd_pcm_direct_client_discard(dshare);
467         snd_pcm_direct_shm_discard(dshare);
468         snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
469         free(dshare->bindings);
470         pcm->private_data = NULL;
471         free(dshare);
472         return 0;
473 }
474
475 static snd_pcm_sframes_t snd_pcm_dshare_mmap_commit(snd_pcm_t *pcm,
476                                                   snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
477                                                   snd_pcm_uframes_t size)
478 {
479         snd_pcm_direct_t *dshare = pcm->private_data;
480         int err;
481
482         switch (snd_pcm_state(dshare->spcm)) {
483         case SND_PCM_STATE_XRUN:
484                 return -EPIPE;
485         case SND_PCM_STATE_SUSPENDED:
486                 return -ESTRPIPE;
487         default:
488                 break;
489         }
490         if (! size)
491                 return 0;
492         snd_pcm_mmap_appl_forward(pcm, size);
493         if (dshare->state == STATE_RUN_PENDING) {
494                 if ((err = snd_pcm_dshare_start_timer(dshare)) < 0)
495                         return err;
496         } else if (dshare->state == SND_PCM_STATE_RUNNING ||
497                    dshare->state == SND_PCM_STATE_DRAINING)
498                 snd_pcm_dshare_sync_ptr(pcm);
499         if (dshare->state == SND_PCM_STATE_RUNNING ||
500             dshare->state == SND_PCM_STATE_DRAINING) {
501                 /* ok, we commit the changes after the validation of area */
502                 /* it's intended, although the result might be crappy */
503                 snd_pcm_dshare_sync_area(pcm);
504                 /* clear timer queue to avoid a bogus return from poll */
505                 if (snd_pcm_mmap_playback_avail(pcm) < pcm->avail_min)
506                         snd_pcm_direct_clear_timer_queue(dshare);
507         }
508         return size;
509 }
510
511 static snd_pcm_sframes_t snd_pcm_dshare_avail_update(snd_pcm_t *pcm)
512 {
513         snd_pcm_direct_t *dshare = pcm->private_data;
514         
515         if (dshare->state == SND_PCM_STATE_RUNNING ||
516             dshare->state == SND_PCM_STATE_DRAINING)
517                 snd_pcm_dshare_sync_ptr(pcm);
518         return snd_pcm_mmap_playback_avail(pcm);
519 }
520
521 static void snd_pcm_dshare_dump(snd_pcm_t *pcm, snd_output_t *out)
522 {
523         snd_pcm_direct_t *dshare = pcm->private_data;
524
525         snd_output_printf(out, "Direct Share PCM\n");
526         if (pcm->setup) {
527                 snd_output_printf(out, "Its setup is:\n");
528                 snd_pcm_dump_setup(pcm, out);
529         }
530         if (dshare->spcm)
531                 snd_pcm_dump(dshare->spcm, out);
532 }
533
534 static snd_pcm_ops_t snd_pcm_dshare_ops = {
535         .close = snd_pcm_dshare_close,
536         .info = snd_pcm_direct_info,
537         .hw_refine = snd_pcm_direct_hw_refine,
538         .hw_params = snd_pcm_direct_hw_params,
539         .hw_free = snd_pcm_direct_hw_free,
540         .sw_params = snd_pcm_direct_sw_params,
541         .channel_info = snd_pcm_direct_channel_info,
542         .dump = snd_pcm_dshare_dump,
543         .nonblock = snd_pcm_direct_nonblock,
544         .async = snd_pcm_direct_async,
545         .mmap = snd_pcm_direct_mmap,
546         .munmap = snd_pcm_direct_munmap,
547 };
548
549 static snd_pcm_fast_ops_t snd_pcm_dshare_fast_ops = {
550         .status = snd_pcm_dshare_status,
551         .state = snd_pcm_dshare_state,
552         .hwsync = snd_pcm_dshare_hwsync,
553         .delay = snd_pcm_dshare_delay,
554         .prepare = snd_pcm_dshare_prepare,
555         .reset = snd_pcm_dshare_reset,
556         .start = snd_pcm_dshare_start,
557         .drop = snd_pcm_dshare_drop,
558         .drain = snd_pcm_dshare_drain,
559         .pause = snd_pcm_dshare_pause,
560         .rewind = snd_pcm_dshare_rewind,
561         .forward = snd_pcm_dshare_forward,
562         .resume = snd_pcm_direct_resume,
563         .link_fd = NULL,
564         .link = NULL,
565         .unlink = NULL,
566         .writei = snd_pcm_mmap_writei,
567         .writen = snd_pcm_mmap_writen,
568         .readi = snd_pcm_dshare_readi,
569         .readn = snd_pcm_dshare_readn,
570         .avail_update = snd_pcm_dshare_avail_update,
571         .mmap_commit = snd_pcm_dshare_mmap_commit,
572         .poll_descriptors = NULL,
573         .poll_descriptors_count = NULL,
574         .poll_revents = snd_pcm_direct_poll_revents,
575 };
576
577 /**
578  * \brief Creates a new dshare PCM
579  * \param pcmp Returns created PCM handle
580  * \param name Name of PCM
581  * \param ipc_key IPC key for semaphore and shared memory
582  * \param ipc_perm IPC permissions for semaphore and shared memory
583  * \param ipc_gid IPC group ID for semaphore and shared memory
584  * \param params Parameters for slave
585  * \param bindings Channel bindings
586  * \param slowptr Slow but more precise pointer updates
587  * \param root Configuration root
588  * \param sconf Slave configuration
589  * \param stream PCM Direction (stream)
590  * \param mode PCM Mode
591  * \retval zero on success otherwise a negative error code
592  * \warning Using of this function might be dangerous in the sense
593  *          of compatibility reasons. The prototype might be freely
594  *          changed in future.
595  */
596 int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
597                         key_t ipc_key, mode_t ipc_perm, int ipc_gid,
598                         struct slave_params *params,
599                         snd_config_t *bindings,
600                         int slowptr,
601                         snd_config_t *root, snd_config_t *sconf,
602                         snd_pcm_stream_t stream, int mode)
603 {
604         snd_pcm_t *pcm = NULL, *spcm = NULL;
605         snd_pcm_direct_t *dshare = NULL;
606         int ret, first_instance;
607         unsigned int chn;
608         int fail_sem_loop = 10;
609
610         assert(pcmp);
611
612         if (stream != SND_PCM_STREAM_PLAYBACK) {
613                 SNDERR("The dshare plugin supports only playback stream");
614                 return -EINVAL;
615         }
616
617         dshare = calloc(1, sizeof(snd_pcm_direct_t));
618         if (!dshare) {
619                 ret = -ENOMEM;
620                 goto _err_nosem;
621         }
622         
623         ret = snd_pcm_direct_parse_bindings(dshare, bindings);
624         if (ret < 0)
625                 goto _err_nosem;
626                 
627         if (!dshare->bindings) {
628                 SNDERR("dshare: specify bindings!!!");
629                 ret = -EINVAL;
630                 goto _err_nosem;
631         }
632         
633         dshare->ipc_key = ipc_key;
634         dshare->ipc_perm = ipc_perm;
635         dshare->ipc_gid = ipc_gid;
636         dshare->semid = -1;
637         dshare->shmid = -1;
638
639         ret = snd_pcm_new(&pcm, dshare->type = SND_PCM_TYPE_DSHARE, name, stream, mode);
640         if (ret < 0)
641                 goto _err_nosem;
642
643         while (1) {
644                 ret = snd_pcm_direct_semaphore_create_or_connect(dshare);
645                 if (ret < 0) {
646                         SNDERR("unable to create IPC semaphore");
647                         goto _err_nosem;
648                 }
649         
650                 ret = snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT);
651                 if (ret < 0) {
652                         snd_pcm_direct_semaphore_discard(dshare);
653                         if (--fail_sem_loop <= 0)
654                                 goto _err_nosem;
655                         continue;
656                 }
657                 break;
658         }
659
660         first_instance = ret = snd_pcm_direct_shm_create_or_connect(dshare);
661         if (ret < 0) {
662                 SNDERR("unable to create IPC shm instance");
663                 goto _err;
664         }
665                 
666         pcm->ops = &snd_pcm_dshare_ops;
667         pcm->fast_ops = &snd_pcm_dshare_fast_ops;
668         pcm->private_data = dshare;
669         dshare->state = SND_PCM_STATE_OPEN;
670         dshare->slowptr = slowptr;
671         dshare->sync_ptr = snd_pcm_dshare_sync_ptr;
672
673         if (first_instance) {
674                 /* recursion is already checked in
675                    snd_pcm_direct_get_slave_ipc_offset() */
676                 ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
677                                          mode | SND_PCM_NONBLOCK, NULL);
678                 if (ret < 0) {
679                         SNDERR("unable to open slave");
680                         goto _err;
681                 }
682         
683                 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
684                         SNDERR("dshare plugin can be only connected to hw plugin");
685                         goto _err;
686                 }
687                 
688                 ret = snd_pcm_direct_initialize_slave(dshare, spcm, params);
689                 if (ret < 0) {
690                         SNDERR("unable to initialize slave");
691                         goto _err;
692                 }
693
694                 dshare->spcm = spcm;
695                 
696                 ret = snd_pcm_direct_server_create(dshare);
697                 if (ret < 0) {
698                         SNDERR("unable to create server");
699                         goto _err;
700                 }
701
702                 dshare->shmptr->type = spcm->type;
703         } else {
704                 /* up semaphore to avoid deadlock */
705                 snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
706                 ret = snd_pcm_direct_client_connect(dshare);
707                 if (ret < 0) {
708                         SNDERR("unable to connect client");
709                         goto _err_nosem;
710                 }
711                         
712                 snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT);
713                 ret = snd_pcm_direct_open_secondary_client(&spcm, dshare, "dshare_client");
714                 ret = snd_pcm_hw_open_fd(&spcm, "dshare_client", dshare->hw_fd, 0, 0);
715                 if (ret < 0)
716                         goto _err;
717                 dshare->spcm = spcm;
718         }
719
720         for (chn = 0; chn < dshare->channels; chn++)
721                 dshare->u.dshare.chn_mask |= (1ULL<<dshare->bindings[chn]);
722         if (dshare->shmptr->u.dshare.chn_mask & dshare->u.dshare.chn_mask) {
723                 SNDERR("destination channel specified in bindings is already used");
724                 dshare->u.dshare.chn_mask = 0;
725                 ret = -EINVAL;
726                 goto _err;
727         }
728         dshare->shmptr->u.dshare.chn_mask |= dshare->u.dshare.chn_mask;
729                 
730         ret = snd_pcm_direct_initialize_poll_fd(dshare);
731         if (ret < 0) {
732                 SNDERR("unable to initialize poll_fd");
733                 goto _err;
734         }
735
736         pcm->poll_fd = dshare->poll_fd;
737         pcm->poll_events = POLLIN;      /* it's different than other plugins */
738                 
739         pcm->mmap_rw = 1;
740         snd_pcm_set_hw_ptr(pcm, &dshare->hw_ptr, -1, 0);
741         snd_pcm_set_appl_ptr(pcm, &dshare->appl_ptr, -1, 0);
742         
743         snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
744
745         *pcmp = pcm;
746         return 0;
747         
748  _err:
749         if (dshare->shmptr)
750                 dshare->shmptr->u.dshare.chn_mask &= ~dshare->u.dshare.chn_mask;
751         if (dshare->timer)
752                 snd_timer_close(dshare->timer);
753         if (dshare->server)
754                 snd_pcm_direct_server_discard(dshare);
755         if (dshare->client)
756                 snd_pcm_direct_client_discard(dshare);
757         if (spcm)
758                 snd_pcm_close(spcm);
759         if (dshare->shmid >= 0)
760                 snd_pcm_direct_shm_discard(dshare);
761         if (snd_pcm_direct_semaphore_discard(dshare) < 0)
762                 snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
763  _err_nosem:
764         if (dshare) {
765                 free(dshare->bindings);
766                 free(dshare);
767         }
768         if (pcm)
769                 snd_pcm_free(pcm);
770         return ret;
771 }
772
773 /*! \page pcm_plugins
774
775 \section pcm_plugins_dshare Plugin: dshare
776
777 This plugin provides sharing channels.
778 Unlike \ref pcm_plugins_share "share plugin", this plugin doesn't need
779 the explicit server program but accesses the shared buffer concurrently
780 from each client as well as \ref pcm_plugins_dmix "dmix" and
781 \ref pcm_plugins_dsnoop "dsnoop" plugins do.
782 The parameters below are almost identical with these plugins.
783
784 \code
785 pcm.name {
786         type dshare             # Direct sharing
787         ipc_key INT             # unique IPC key
788         ipc_key_add_uid BOOL    # add current uid to unique IPC key
789         ipc_perm INT            # IPC permissions (octal, default 0600)
790         slave STR
791         # or
792         slave {                 # Slave definition
793                 pcm STR         # slave PCM name
794                 # or
795                 pcm { }         # slave PCM definition
796                 format STR      # format definition
797                 rate INT        # rate definition
798                 channels INT
799                 period_time INT # in usec
800                 # or
801                 period_size INT # in bytes
802                 buffer_time INT # in usec
803                 # or
804                 buffer_size INT # in bytes
805                 periods INT     # when buffer_size or buffer_time is not specified
806         }
807         bindings {              # note: this is client independent!!!
808                 N INT           # maps slave channel to client channel N
809         }
810         slowptr BOOL            # slow but more precise pointer updates
811 }
812 \endcode
813
814 \subsection pcm_plugins_dshare_funcref Function reference
815
816 <UL>
817   <LI>snd_pcm_dshare_open()
818   <LI>_snd_pcm_dshare_open()
819 </UL>
820
821 */
822
823 /**
824  * \brief Creates a new dshare PCM
825  * \param pcmp Returns created PCM handle
826  * \param name Name of PCM
827  * \param root Root configuration node
828  * \param conf Configuration node with dshare PCM description
829  * \param stream PCM Stream
830  * \param mode PCM Mode
831  * \warning Using of this function might be dangerous in the sense
832  *          of compatibility reasons. The prototype might be freely
833  *          changed in future.
834  */
835 int _snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
836                        snd_config_t *root, snd_config_t *conf,
837                        snd_pcm_stream_t stream, int mode)
838 {
839         snd_config_t *sconf;
840         struct slave_params params;
841         struct snd_pcm_direct_open_conf dopen;
842         int bsize, psize;
843         int ipc_offset;
844         int err;
845
846         err = snd_pcm_direct_parse_open_conf(conf, &dopen);
847         if (err < 0)
848                 return err;
849
850         /* the default settings, it might be invalid for some hardware */
851         params.format = SND_PCM_FORMAT_S16;
852         params.rate = 48000;
853         params.channels = 2;
854         params.period_time = -1;
855         params.buffer_time = -1;
856         bsize = psize = -1;
857         params.periods = 3;
858         err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8,
859                                  SND_PCM_HW_PARAM_FORMAT, 0, &params.format,
860                                  SND_PCM_HW_PARAM_RATE, 0, &params.rate,
861                                  SND_PCM_HW_PARAM_CHANNELS, 0, &params.channels,
862                                  SND_PCM_HW_PARAM_PERIOD_TIME, 0, &params.period_time,
863                                  SND_PCM_HW_PARAM_BUFFER_TIME, 0, &params.buffer_time,
864                                  SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
865                                  SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
866                                  SND_PCM_HW_PARAM_PERIODS, 0, &params.periods);
867         if (err < 0)
868                 return err;
869
870         /* set a reasonable default */
871         if (psize == -1 && params.period_time == -1)
872                 params.period_time = 125000;    /* 0.125 seconds */
873
874         params.period_size = psize;
875         params.buffer_size = bsize;
876
877         ipc_offset = snd_pcm_direct_get_slave_ipc_offset(root, sconf, stream);
878         if (ipc_offset < 0) {
879                 snd_config_delete(sconf);
880                 return ipc_offset;
881         }
882         dopen.ipc_key += ipc_offset;
883
884         err = snd_pcm_dshare_open(pcmp, name, dopen.ipc_key, dopen.ipc_perm, dopen.ipc_gid,
885                                   &params, dopen.bindings, dopen.slowptr,
886                                   root, sconf, stream, mode);
887         if (err < 0)
888                 snd_config_delete(sconf);
889         return err;
890 }
891 #ifndef DOC_HIDDEN
892 SND_DLSYM_BUILD_VERSION(_snd_pcm_dshare_open, SND_PCM_DLSYM_VERSION);
893 #endif