Initial Import
[profile/ivi/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@perex.cz>
6  * \date 2003
7  */
8 /*
9  *  PCM - Direct Sharing of Channels
10  *  Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
11  *
12  *
13  *   This library is free software; you can redistribute it and/or modify
14  *   it under the terms of the GNU Lesser General Public License as
15  *   published by the Free Software Foundation; either version 2.1 of
16  *   the License, or (at your option) any later version.
17  *
18  *   This program is distributed in the hope that it will be useful,
19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *   GNU Lesser General Public License for more details.
22  *
23  *   You should have received a copy of the GNU Lesser General Public
24  *   License along with this library; if not, write to the Free Software
25  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
26  *
27  */
28   
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stddef.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <ctype.h>
37 #include <grp.h>
38 #include <sys/ioctl.h>
39 #include <sys/mman.h>
40 #include <sys/shm.h>
41 #include <sys/sem.h>
42 #include <sys/wait.h>
43 #include <sys/socket.h>
44 #include <sys/un.h>
45 #include <sys/mman.h>
46 #include "pcm_direct.h"
47
48 #ifndef PIC
49 /* entry for static linking */
50 const char *_snd_module_pcm_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                 snd_timer_stop(dshare->timer);
198                 gettimestamp(&dshare->trigger_tstamp, pcm->monotonic);
199                 if (dshare->state == SND_PCM_STATE_RUNNING) {
200                         dshare->state = SND_PCM_STATE_XRUN;
201                         return -EPIPE;
202                 }
203                 dshare->state = SND_PCM_STATE_SETUP;
204                 /* clear queue to remove pending poll events */
205                 snd_pcm_direct_clear_timer_queue(dshare);
206         }
207         return 0;
208 }
209
210 /*
211  *  plugin implementation
212  */
213
214 static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
215 {
216         snd_pcm_direct_t *dshare = pcm->private_data;
217
218         switch (dshare->state) {
219         case SNDRV_PCM_STATE_DRAINING:
220         case SNDRV_PCM_STATE_RUNNING:
221                 snd_pcm_dshare_sync_ptr(pcm);
222                 break;
223         default:
224                 break;
225         }
226         memset(status, 0, sizeof(*status));
227         status->state = snd_pcm_state(dshare->spcm);
228         status->trigger_tstamp = dshare->trigger_tstamp;
229         gettimestamp(&status->tstamp, pcm->monotonic);
230         status->avail = snd_pcm_mmap_playback_avail(pcm);
231         status->avail_max = status->avail > dshare->avail_max ? status->avail : dshare->avail_max;
232         dshare->avail_max = 0;
233         return 0;
234 }
235
236 static snd_pcm_state_t snd_pcm_dshare_state(snd_pcm_t *pcm)
237 {
238         snd_pcm_direct_t *dshare = pcm->private_data;
239         switch (snd_pcm_state(dshare->spcm)) {
240         case SND_PCM_STATE_SUSPENDED:
241                 return SND_PCM_STATE_SUSPENDED;
242         case SND_PCM_STATE_DISCONNECTED:
243                 return SND_PCM_STATE_DISCONNECTED;
244         default:
245                 break;
246         }
247         if (dshare->state == STATE_RUN_PENDING)
248                 return SNDRV_PCM_STATE_RUNNING;
249         return dshare->state;
250 }
251
252 static int snd_pcm_dshare_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
253 {
254         snd_pcm_direct_t *dshare = pcm->private_data;
255         int err;
256         
257         switch (dshare->state) {
258         case SNDRV_PCM_STATE_DRAINING:
259         case SNDRV_PCM_STATE_RUNNING:
260                 err = snd_pcm_dshare_sync_ptr(pcm);
261                 if (err < 0)
262                         return err;
263                 /* fallthru */
264         case SNDRV_PCM_STATE_PREPARED:
265         case SNDRV_PCM_STATE_SUSPENDED:
266         case STATE_RUN_PENDING:
267                 *delayp = snd_pcm_mmap_playback_hw_avail(pcm);
268                 return 0;
269         case SNDRV_PCM_STATE_XRUN:
270                 return -EPIPE;
271         case SNDRV_PCM_STATE_DISCONNECTED:
272                 return -ENODEV;
273         default:
274                 return -EBADFD;
275         }
276 }
277
278 static int snd_pcm_dshare_hwsync(snd_pcm_t *pcm)
279 {
280         snd_pcm_direct_t *dshare = pcm->private_data;
281
282         switch(dshare->state) {
283         case SNDRV_PCM_STATE_DRAINING:
284         case SNDRV_PCM_STATE_RUNNING:
285                 return snd_pcm_dshare_sync_ptr(pcm);
286         case SNDRV_PCM_STATE_PREPARED:
287         case SNDRV_PCM_STATE_SUSPENDED:
288                 return 0;
289         case SNDRV_PCM_STATE_XRUN:
290                 return -EPIPE;
291         case SNDRV_PCM_STATE_DISCONNECTED:
292                 return -ENODEV;
293         default:
294                 return -EBADFD;
295         }
296 }
297
298 static int snd_pcm_dshare_prepare(snd_pcm_t *pcm)
299 {
300         snd_pcm_direct_t *dshare = pcm->private_data;
301
302         snd_pcm_direct_check_interleave(dshare, pcm);
303         dshare->state = SND_PCM_STATE_PREPARED;
304         dshare->appl_ptr = dshare->last_appl_ptr = 0;
305         dshare->hw_ptr = 0;
306         return snd_pcm_direct_set_timer_params(dshare);
307 }
308
309 static int snd_pcm_dshare_reset(snd_pcm_t *pcm)
310 {
311         snd_pcm_direct_t *dshare = pcm->private_data;
312         dshare->hw_ptr %= pcm->period_size;
313         dshare->appl_ptr = dshare->last_appl_ptr = dshare->hw_ptr;
314         dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr;
315         return 0;
316 }
317
318 static int snd_pcm_dshare_start_timer(snd_pcm_direct_t *dshare)
319 {
320         int err;
321
322         snd_pcm_hwsync(dshare->spcm);
323         dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr;
324         err = snd_timer_start(dshare->timer);
325         if (err < 0)
326                 return err;
327         dshare->state = SND_PCM_STATE_RUNNING;
328         return 0;
329 }
330
331 static int snd_pcm_dshare_start(snd_pcm_t *pcm)
332 {
333         snd_pcm_direct_t *dshare = pcm->private_data;
334         snd_pcm_sframes_t avail;
335         int err;
336         
337         if (dshare->state != SND_PCM_STATE_PREPARED)
338                 return -EBADFD;
339         avail = snd_pcm_mmap_playback_hw_avail(pcm);
340         if (avail == 0)
341                 dshare->state = STATE_RUN_PENDING;
342         else if (avail < 0)
343                 return 0;
344         else {
345                 if ((err = snd_pcm_dshare_start_timer(dshare)) < 0)
346                         return err;
347                 snd_pcm_dshare_sync_area(pcm);
348         }
349         gettimestamp(&dshare->trigger_tstamp, pcm->monotonic);
350         return 0;
351 }
352
353 static int snd_pcm_dshare_drop(snd_pcm_t *pcm)
354 {
355         snd_pcm_direct_t *dshare = pcm->private_data;
356         if (dshare->state == SND_PCM_STATE_OPEN)
357                 return -EBADFD;
358         dshare->state = SND_PCM_STATE_SETUP;
359         snd_pcm_direct_timer_stop(dshare);
360         do_silence(pcm);
361         return 0;
362 }
363
364 static int snd_pcm_dshare_drain(snd_pcm_t *pcm)
365 {
366         snd_pcm_direct_t *dshare = pcm->private_data;
367         snd_pcm_uframes_t stop_threshold;
368         int err;
369
370         if (dshare->state == SND_PCM_STATE_OPEN)
371                 return -EBADFD;
372         if (pcm->mode & SND_PCM_NONBLOCK)
373                 return -EAGAIN;
374         if (dshare->state == SND_PCM_STATE_PREPARED) {
375                 if (snd_pcm_mmap_playback_hw_avail(pcm) > 0)
376                         snd_pcm_dshare_start(pcm);
377                 else {
378                         snd_pcm_dshare_drop(pcm);
379                         return 0;
380                 }
381         }
382
383         if (dshare->state == SND_PCM_STATE_XRUN) {
384                 snd_pcm_dshare_drop(pcm);
385                 return 0;
386         }
387
388         stop_threshold = pcm->stop_threshold;
389         if (pcm->stop_threshold > pcm->buffer_size)
390                 pcm->stop_threshold = pcm->buffer_size;
391         dshare->state = SND_PCM_STATE_DRAINING;
392         do {
393                 err = snd_pcm_dshare_sync_ptr(pcm);
394                 if (err < 0) {
395                         snd_pcm_dshare_drop(pcm);
396                         break;
397                 }
398                 if (dshare->state == SND_PCM_STATE_DRAINING) {
399                         snd_pcm_dshare_sync_area(pcm);
400                         snd_pcm_wait_nocheck(pcm, -1);
401                         snd_pcm_direct_clear_timer_queue(dshare); /* force poll to wait */
402                 }
403         } while (dshare->state == SND_PCM_STATE_DRAINING);
404         pcm->stop_threshold = stop_threshold;
405         return 0;
406 }
407
408 static int snd_pcm_dshare_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
409 {
410         return -EIO;
411 }
412
413 static snd_pcm_sframes_t snd_pcm_dshare_rewindable(snd_pcm_t *pcm)
414 {
415         return snd_pcm_mmap_playback_hw_avail(pcm);
416 }
417
418 static snd_pcm_sframes_t snd_pcm_dshare_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
419 {
420         snd_pcm_sframes_t avail;
421
422         avail = snd_pcm_mmap_playback_hw_avail(pcm);
423         if (avail < 0)
424                 return 0;
425         if (frames > (snd_pcm_uframes_t)avail)
426                 frames = avail;
427         snd_pcm_mmap_appl_backward(pcm, frames);
428         return frames;
429 }
430
431 static snd_pcm_sframes_t snd_pcm_dshare_forwardable(snd_pcm_t *pcm)
432 {
433         return snd_pcm_mmap_playback_avail(pcm);
434 }
435
436 static snd_pcm_sframes_t snd_pcm_dshare_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
437 {
438         snd_pcm_sframes_t avail;
439
440         avail = snd_pcm_mmap_playback_avail(pcm);
441         if (avail < 0)
442                 return 0;
443         if (frames > (snd_pcm_uframes_t)avail)
444                 frames = avail;
445         snd_pcm_mmap_appl_forward(pcm, frames);
446         return frames;
447 }
448
449 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)
450 {
451         return -ENODEV;
452 }
453
454 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)
455 {
456         return -ENODEV;
457 }
458
459 static int snd_pcm_dshare_close(snd_pcm_t *pcm)
460 {
461         snd_pcm_direct_t *dshare = pcm->private_data;
462
463         if (dshare->timer)
464                 snd_timer_close(dshare->timer);
465         do_silence(pcm);
466         snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT);
467         dshare->shmptr->u.dshare.chn_mask &= ~dshare->u.dshare.chn_mask;
468         snd_pcm_close(dshare->spcm);
469         if (dshare->server)
470                 snd_pcm_direct_server_discard(dshare);
471         if (dshare->client)
472                 snd_pcm_direct_client_discard(dshare);
473         if (snd_pcm_direct_shm_discard(dshare))
474                 snd_pcm_direct_semaphore_discard(dshare);
475         else
476                 snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
477         free(dshare->bindings);
478         pcm->private_data = NULL;
479         free(dshare);
480         return 0;
481 }
482
483 static snd_pcm_sframes_t snd_pcm_dshare_mmap_commit(snd_pcm_t *pcm,
484                                                   snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
485                                                   snd_pcm_uframes_t size)
486 {
487         snd_pcm_direct_t *dshare = pcm->private_data;
488         int err;
489
490         switch (snd_pcm_state(dshare->spcm)) {
491         case SND_PCM_STATE_XRUN:
492                 return -EPIPE;
493         case SND_PCM_STATE_SUSPENDED:
494                 return -ESTRPIPE;
495         default:
496                 break;
497         }
498         if (! size)
499                 return 0;
500         snd_pcm_mmap_appl_forward(pcm, size);
501         if (dshare->state == STATE_RUN_PENDING) {
502                 if ((err = snd_pcm_dshare_start_timer(dshare)) < 0)
503                         return err;
504         } else if (dshare->state == SND_PCM_STATE_RUNNING ||
505                    dshare->state == SND_PCM_STATE_DRAINING)
506                 snd_pcm_dshare_sync_ptr(pcm);
507         if (dshare->state == SND_PCM_STATE_RUNNING ||
508             dshare->state == SND_PCM_STATE_DRAINING) {
509                 /* ok, we commit the changes after the validation of area */
510                 /* it's intended, although the result might be crappy */
511                 snd_pcm_dshare_sync_area(pcm);
512                 /* clear timer queue to avoid a bogus return from poll */
513                 if (snd_pcm_mmap_playback_avail(pcm) < pcm->avail_min)
514                         snd_pcm_direct_clear_timer_queue(dshare);
515         }
516         return size;
517 }
518
519 static snd_pcm_sframes_t snd_pcm_dshare_avail_update(snd_pcm_t *pcm)
520 {
521         snd_pcm_direct_t *dshare = pcm->private_data;
522         
523         if (dshare->state == SND_PCM_STATE_RUNNING ||
524             dshare->state == SND_PCM_STATE_DRAINING)
525                 snd_pcm_dshare_sync_ptr(pcm);
526         return snd_pcm_mmap_playback_avail(pcm);
527 }
528
529 static int snd_pcm_dshare_htimestamp(snd_pcm_t *pcm,
530                                      snd_pcm_uframes_t *avail,
531                                      snd_htimestamp_t *tstamp)
532 {
533         snd_pcm_direct_t *dshare = pcm->private_data;
534         snd_pcm_uframes_t avail1;
535         int ok = 0;
536         
537         while (1) {
538                 if (dshare->state == SND_PCM_STATE_RUNNING ||
539                     dshare->state == SND_PCM_STATE_DRAINING)
540                         snd_pcm_dshare_sync_ptr(pcm);
541                 avail1 = snd_pcm_mmap_playback_avail(pcm);
542                 if (ok && *avail == avail1)
543                         break;
544                 *avail = avail1;
545                 *tstamp = snd_pcm_hw_fast_tstamp(dshare->spcm);
546         }
547         return 0;
548 }
549
550 static void snd_pcm_dshare_dump(snd_pcm_t *pcm, snd_output_t *out)
551 {
552         snd_pcm_direct_t *dshare = pcm->private_data;
553
554         snd_output_printf(out, "Direct Share PCM\n");
555         if (pcm->setup) {
556                 snd_output_printf(out, "Its setup is:\n");
557                 snd_pcm_dump_setup(pcm, out);
558         }
559         if (dshare->spcm)
560                 snd_pcm_dump(dshare->spcm, out);
561 }
562
563 static const snd_pcm_ops_t snd_pcm_dshare_ops = {
564         .close = snd_pcm_dshare_close,
565         .info = snd_pcm_direct_info,
566         .hw_refine = snd_pcm_direct_hw_refine,
567         .hw_params = snd_pcm_direct_hw_params,
568         .hw_free = snd_pcm_direct_hw_free,
569         .sw_params = snd_pcm_direct_sw_params,
570         .channel_info = snd_pcm_direct_channel_info,
571         .dump = snd_pcm_dshare_dump,
572         .nonblock = snd_pcm_direct_nonblock,
573         .async = snd_pcm_direct_async,
574         .mmap = snd_pcm_direct_mmap,
575         .munmap = snd_pcm_direct_munmap,
576 };
577
578 static const snd_pcm_fast_ops_t snd_pcm_dshare_fast_ops = {
579         .status = snd_pcm_dshare_status,
580         .state = snd_pcm_dshare_state,
581         .hwsync = snd_pcm_dshare_hwsync,
582         .delay = snd_pcm_dshare_delay,
583         .prepare = snd_pcm_dshare_prepare,
584         .reset = snd_pcm_dshare_reset,
585         .start = snd_pcm_dshare_start,
586         .drop = snd_pcm_dshare_drop,
587         .drain = snd_pcm_dshare_drain,
588         .pause = snd_pcm_dshare_pause,
589         .rewindable = snd_pcm_dshare_rewindable,
590         .rewind = snd_pcm_dshare_rewind,
591         .forwardable = snd_pcm_dshare_forwardable,
592         .forward = snd_pcm_dshare_forward,
593         .resume = snd_pcm_direct_resume,
594         .link = NULL,
595         .link_slaves = NULL,
596         .unlink = NULL,
597         .writei = snd_pcm_mmap_writei,
598         .writen = snd_pcm_mmap_writen,
599         .readi = snd_pcm_dshare_readi,
600         .readn = snd_pcm_dshare_readn,
601         .avail_update = snd_pcm_dshare_avail_update,
602         .mmap_commit = snd_pcm_dshare_mmap_commit,
603         .htimestamp = snd_pcm_dshare_htimestamp,
604         .poll_descriptors = NULL,
605         .poll_descriptors_count = NULL,
606         .poll_revents = snd_pcm_direct_poll_revents,
607 };
608
609 /**
610  * \brief Creates a new dshare PCM
611  * \param pcmp Returns created PCM handle
612  * \param name Name of PCM
613  * \param opts Direct PCM configurations
614  * \param params Parameters for slave
615  * \param root Configuration root
616  * \param sconf Slave configuration
617  * \param stream PCM Direction (stream)
618  * \param mode PCM Mode
619  * \retval zero on success otherwise a negative error code
620  * \warning Using of this function might be dangerous in the sense
621  *          of compatibility reasons. The prototype might be freely
622  *          changed in future.
623  */
624 int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
625                         struct snd_pcm_direct_open_conf *opts,
626                         struct slave_params *params,
627                         snd_config_t *root, snd_config_t *sconf,
628                         snd_pcm_stream_t stream, int mode)
629 {
630         snd_pcm_t *pcm = NULL, *spcm = NULL;
631         snd_pcm_direct_t *dshare = NULL;
632         int ret, first_instance;
633         unsigned int chn;
634         int fail_sem_loop = 10;
635
636         assert(pcmp);
637
638         if (stream != SND_PCM_STREAM_PLAYBACK) {
639                 SNDERR("The dshare plugin supports only playback stream");
640                 return -EINVAL;
641         }
642
643         dshare = calloc(1, sizeof(snd_pcm_direct_t));
644         if (!dshare) {
645                 ret = -ENOMEM;
646                 goto _err_nosem;
647         }
648         
649         ret = snd_pcm_direct_parse_bindings(dshare, params, opts->bindings);
650         if (ret < 0)
651                 goto _err_nosem;
652                 
653         if (!dshare->bindings) {
654                 SNDERR("dshare: specify bindings!!!");
655                 ret = -EINVAL;
656                 goto _err_nosem;
657         }
658         
659         dshare->ipc_key = opts->ipc_key;
660         dshare->ipc_perm = opts->ipc_perm;
661         dshare->ipc_gid = opts->ipc_gid;
662         dshare->semid = -1;
663         dshare->shmid = -1;
664
665         ret = snd_pcm_new(&pcm, dshare->type = SND_PCM_TYPE_DSHARE, name, stream, mode);
666         if (ret < 0)
667                 goto _err_nosem;
668
669         while (1) {
670                 ret = snd_pcm_direct_semaphore_create_or_connect(dshare);
671                 if (ret < 0) {
672                         SNDERR("unable to create IPC semaphore");
673                         goto _err_nosem;
674                 }
675         
676                 ret = snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT);
677                 if (ret < 0) {
678                         snd_pcm_direct_semaphore_discard(dshare);
679                         if (--fail_sem_loop <= 0)
680                                 goto _err_nosem;
681                         continue;
682                 }
683                 break;
684         }
685
686         first_instance = ret = snd_pcm_direct_shm_create_or_connect(dshare);
687         if (ret < 0) {
688                 SNDERR("unable to create IPC shm instance");
689                 goto _err;
690         }
691                 
692         pcm->ops = &snd_pcm_dshare_ops;
693         pcm->fast_ops = &snd_pcm_dshare_fast_ops;
694         pcm->private_data = dshare;
695         dshare->state = SND_PCM_STATE_OPEN;
696         dshare->slowptr = opts->slowptr;
697         dshare->max_periods = opts->max_periods;
698         dshare->sync_ptr = snd_pcm_dshare_sync_ptr;
699
700         if (first_instance) {
701                 /* recursion is already checked in
702                    snd_pcm_direct_get_slave_ipc_offset() */
703                 ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
704                                          mode | SND_PCM_NONBLOCK, NULL);
705                 if (ret < 0) {
706                         SNDERR("unable to open slave");
707                         goto _err;
708                 }
709         
710                 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
711                         SNDERR("dshare plugin can be only connected to hw plugin");
712                         goto _err;
713                 }
714                 
715                 ret = snd_pcm_direct_initialize_slave(dshare, spcm, params);
716                 if (ret < 0) {
717                         SNDERR("unable to initialize slave");
718                         goto _err;
719                 }
720
721                 dshare->spcm = spcm;
722                 
723                 if (dshare->shmptr->use_server) {
724                         ret = snd_pcm_direct_server_create(dshare);
725                         if (ret < 0) {
726                                 SNDERR("unable to create server");
727                                 goto _err;
728                         }
729                 }
730
731                 dshare->shmptr->type = spcm->type;
732         } else {
733                 if (dshare->shmptr->use_server) {
734                         /* up semaphore to avoid deadlock */
735                         snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
736                         ret = snd_pcm_direct_client_connect(dshare);
737                         if (ret < 0) {
738                                 SNDERR("unable to connect client");
739                                 goto _err_nosem;
740                         }
741                         
742                         snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT);
743                         ret = snd_pcm_direct_open_secondary_client(&spcm, dshare, "dshare_client");
744                         if (ret < 0)
745                                 goto _err;
746
747                 } else {
748
749                         ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
750                                                  mode | SND_PCM_NONBLOCK |
751                                                  SND_PCM_APPEND,
752                                                  NULL);
753                         if (ret < 0) {
754                                 SNDERR("unable to open slave");
755                                 goto _err;
756                         }
757                         if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
758                                 SNDERR("dshare plugin can be only connected to hw plugin");
759                                 ret = -EINVAL;
760                                 goto _err;
761                         }
762                 
763                         ret = snd_pcm_direct_initialize_secondary_slave(dshare, spcm, params);
764                         if (ret < 0) {
765                                 SNDERR("unable to initialize slave");
766                                 goto _err;
767                         }
768                 }
769
770                 dshare->spcm = spcm;
771         }
772
773         for (chn = 0; chn < dshare->channels; chn++)
774                 dshare->u.dshare.chn_mask |= (1ULL<<dshare->bindings[chn]);
775         if (dshare->shmptr->u.dshare.chn_mask & dshare->u.dshare.chn_mask) {
776                 SNDERR("destination channel specified in bindings is already used");
777                 dshare->u.dshare.chn_mask = 0;
778                 ret = -EINVAL;
779                 goto _err;
780         }
781         dshare->shmptr->u.dshare.chn_mask |= dshare->u.dshare.chn_mask;
782                 
783         ret = snd_pcm_direct_initialize_poll_fd(dshare);
784         if (ret < 0) {
785                 SNDERR("unable to initialize poll_fd");
786                 goto _err;
787         }
788
789         pcm->poll_fd = dshare->poll_fd;
790         pcm->poll_events = POLLIN;      /* it's different than other plugins */
791                 
792         pcm->mmap_rw = 1;
793         snd_pcm_set_hw_ptr(pcm, &dshare->hw_ptr, -1, 0);
794         snd_pcm_set_appl_ptr(pcm, &dshare->appl_ptr, -1, 0);
795         
796         snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
797
798         *pcmp = pcm;
799         return 0;
800         
801  _err:
802         if (dshare->shmptr)
803                 dshare->shmptr->u.dshare.chn_mask &= ~dshare->u.dshare.chn_mask;
804         if (dshare->timer)
805                 snd_timer_close(dshare->timer);
806         if (dshare->server)
807                 snd_pcm_direct_server_discard(dshare);
808         if (dshare->client)
809                 snd_pcm_direct_client_discard(dshare);
810         if (spcm)
811                 snd_pcm_close(spcm);
812         if (dshare->shmid >= 0)
813                 snd_pcm_direct_shm_discard(dshare);
814         if (snd_pcm_direct_semaphore_discard(dshare) < 0)
815                 snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
816  _err_nosem:
817         if (dshare) {
818                 free(dshare->bindings);
819                 free(dshare);
820         }
821         if (pcm)
822                 snd_pcm_free(pcm);
823         return ret;
824 }
825
826 /*! \page pcm_plugins
827
828 \section pcm_plugins_dshare Plugin: dshare
829
830 This plugin provides sharing channels.
831 Unlike \ref pcm_plugins_share "share plugin", this plugin doesn't need
832 the explicit server program but accesses the shared buffer concurrently
833 from each client as well as \ref pcm_plugins_dmix "dmix" and
834 \ref pcm_plugins_dsnoop "dsnoop" plugins do.
835 The parameters below are almost identical with these plugins.
836
837 \code
838 pcm.name {
839         type dshare             # Direct sharing
840         ipc_key INT             # unique IPC key
841         ipc_key_add_uid BOOL    # add current uid to unique IPC key
842         ipc_perm INT            # IPC permissions (octal, default 0600)
843         slave STR
844         # or
845         slave {                 # Slave definition
846                 pcm STR         # slave PCM name
847                 # or
848                 pcm { }         # slave PCM definition
849                 format STR      # format definition
850                 rate INT        # rate definition
851                 channels INT
852                 period_time INT # in usec
853                 # or
854                 period_size INT # in bytes
855                 buffer_time INT # in usec
856                 # or
857                 buffer_size INT # in bytes
858                 periods INT     # when buffer_size or buffer_time is not specified
859         }
860         bindings {              # note: this is client independent!!!
861                 N INT           # maps slave channel to client channel N
862         }
863         slowptr BOOL            # slow but more precise pointer updates
864 }
865 \endcode
866
867 \subsection pcm_plugins_dshare_funcref Function reference
868
869 <UL>
870   <LI>snd_pcm_dshare_open()
871   <LI>_snd_pcm_dshare_open()
872 </UL>
873
874 */
875
876 /**
877  * \brief Creates a new dshare PCM
878  * \param pcmp Returns created PCM handle
879  * \param name Name of PCM
880  * \param root Root configuration node
881  * \param conf Configuration node with dshare PCM description
882  * \param stream PCM Stream
883  * \param mode PCM Mode
884  * \warning Using of this function might be dangerous in the sense
885  *          of compatibility reasons. The prototype might be freely
886  *          changed in future.
887  */
888 int _snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
889                        snd_config_t *root, snd_config_t *conf,
890                        snd_pcm_stream_t stream, int mode)
891 {
892         snd_config_t *sconf;
893         struct slave_params params;
894         struct snd_pcm_direct_open_conf dopen;
895         int bsize, psize;
896         int err;
897
898         err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
899         if (err < 0)
900                 return err;
901
902         /* the default settings, it might be invalid for some hardware */
903         params.format = SND_PCM_FORMAT_S16;
904         params.rate = 48000;
905         params.channels = 2;
906         params.period_time = -1;
907         params.buffer_time = -1;
908         bsize = psize = -1;
909         params.periods = 3;
910         err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8,
911                                  SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &params.format,
912                                  SND_PCM_HW_PARAM_RATE, 0, &params.rate,
913                                  SND_PCM_HW_PARAM_CHANNELS, 0, &params.channels,
914                                  SND_PCM_HW_PARAM_PERIOD_TIME, 0, &params.period_time,
915                                  SND_PCM_HW_PARAM_BUFFER_TIME, 0, &params.buffer_time,
916                                  SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
917                                  SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
918                                  SND_PCM_HW_PARAM_PERIODS, 0, &params.periods);
919         if (err < 0)
920                 return err;
921
922         /* set a reasonable default */
923         if (psize == -1 && params.period_time == -1)
924                 params.period_time = 125000;    /* 0.125 seconds */
925
926         if (params.format == -2)
927                 params.format = SND_PCM_FORMAT_UNKNOWN;
928
929         params.period_size = psize;
930         params.buffer_size = bsize;
931
932         err = snd_pcm_dshare_open(pcmp, name, &dopen, &params,
933                                   root, sconf, stream, mode);
934         snd_config_delete(sconf);
935         return err;
936 }
937 #ifndef DOC_HIDDEN
938 SND_DLSYM_BUILD_VERSION(_snd_pcm_dshare_open, SND_PCM_DLSYM_VERSION);
939 #endif