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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26  *
27  */
28 
29 #include "pcm_local.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stddef.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <string.h>
36 #include <fcntl.h>
37 #include <ctype.h>
38 #include <grp.h>
39 #include <sys/ioctl.h>
40 #include <sys/mman.h>
41 #include <sys/shm.h>
42 #include <sys/sem.h>
43 #include <sys/wait.h>
44 #include <sys/socket.h>
45 #include <sys/un.h>
46 #include <sys/mman.h>
47 #include "pcm_direct.h"
48 
49 #ifndef PIC
50 /* entry for static linking */
51 const char *_snd_module_pcm_dshare = "";
52 #endif
53 
54 #ifndef DOC_HIDDEN
55 /* start is pending - this state happens when rate plugin does a delayed commit */
56 #define STATE_RUN_PENDING	1024
57 #endif
58 
do_silence(snd_pcm_t *pcm)59 static void do_silence(snd_pcm_t *pcm)
60 {
61 	snd_pcm_direct_t *dshare = pcm->private_data;
62 	const snd_pcm_channel_area_t *dst_areas;
63 	unsigned int chn, dchn, channels;
64 	snd_pcm_format_t format;
65 
66 	dst_areas = snd_pcm_mmap_areas(dshare->spcm);
67 	channels = dshare->channels;
68 	format = dshare->shmptr->s.format;
69 	for (chn = 0; chn < channels; chn++) {
70 		dchn = dshare->bindings ? dshare->bindings[chn] : chn;
71 		if (dchn != UINT_MAX)
72 			snd_pcm_area_silence(&dst_areas[dchn], 0,
73 					     dshare->shmptr->s.buffer_size, format);
74 	}
75 }
76 
share_areas(snd_pcm_direct_t *dshare, const snd_pcm_channel_area_t *src_areas, const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t src_ofs, snd_pcm_uframes_t dst_ofs, snd_pcm_uframes_t size)77 static void share_areas(snd_pcm_direct_t *dshare,
78 		      const snd_pcm_channel_area_t *src_areas,
79 		      const snd_pcm_channel_area_t *dst_areas,
80 		      snd_pcm_uframes_t src_ofs,
81 		      snd_pcm_uframes_t dst_ofs,
82 		      snd_pcm_uframes_t size)
83 {
84 	unsigned int chn, dchn, channels;
85 	snd_pcm_format_t format;
86 
87 	channels = dshare->channels;
88 	format = dshare->shmptr->s.format;
89 	if (dshare->interleaved) {
90 		unsigned int fbytes = snd_pcm_format_physical_width(format) / 8;
91 		memcpy(((char *)dst_areas[0].addr) + (dst_ofs * channels * fbytes),
92 		       ((char *)src_areas[0].addr) + (src_ofs * channels * fbytes),
93 		       size * channels * fbytes);
94 	} else {
95 		for (chn = 0; chn < channels; chn++) {
96 			dchn = dshare->bindings ? dshare->bindings[chn] : chn;
97 			if (dchn != UINT_MAX)
98 				snd_pcm_area_copy(&dst_areas[dchn], dst_ofs,
99 						  &src_areas[chn], src_ofs, size, format);
100 
101 		}
102 	}
103 }
104 
105 /*
106  *  synchronize shm ring buffer with hardware
107  */
snd_pcm_dshare_sync_area(snd_pcm_t *pcm)108 static void snd_pcm_dshare_sync_area(snd_pcm_t *pcm)
109 {
110 	snd_pcm_direct_t *dshare = pcm->private_data;
111 	snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size;
112 	snd_pcm_uframes_t appl_ptr, size;
113 	const snd_pcm_channel_area_t *src_areas, *dst_areas;
114 
115 	/* calculate the size to transfer */
116 	size = pcm_frame_diff(dshare->appl_ptr, dshare->last_appl_ptr, pcm->boundary);
117 	if (! size)
118 		return;
119 	slave_hw_ptr = dshare->slave_hw_ptr;
120 	/* don't write on the last active period - this area may be cleared
121 	 * by the driver during write operation...
122 	 */
123 	slave_hw_ptr -= slave_hw_ptr % dshare->slave_period_size;
124 	slave_hw_ptr += dshare->slave_buffer_size;
125 	if (slave_hw_ptr >= dshare->slave_boundary)
126 		slave_hw_ptr -= dshare->slave_boundary;
127 	slave_size = pcm_frame_diff(slave_hw_ptr, dshare->slave_appl_ptr, dshare->slave_boundary);
128 	if (slave_size < size)
129 		size = slave_size;
130 	if (! size)
131 		return;
132 
133 	/* add sample areas here */
134 	src_areas = snd_pcm_mmap_areas(pcm);
135 	dst_areas = snd_pcm_mmap_areas(dshare->spcm);
136 	appl_ptr = dshare->last_appl_ptr % pcm->buffer_size;
137 	dshare->last_appl_ptr += size;
138 	dshare->last_appl_ptr %= pcm->boundary;
139 	slave_appl_ptr = dshare->slave_appl_ptr % dshare->slave_buffer_size;
140 	dshare->slave_appl_ptr += size;
141 	dshare->slave_appl_ptr %= dshare->slave_boundary;
142 	for (;;) {
143 		snd_pcm_uframes_t transfer = size;
144 		if (appl_ptr + transfer > pcm->buffer_size)
145 			transfer = pcm->buffer_size - appl_ptr;
146 		if (slave_appl_ptr + transfer > dshare->slave_buffer_size)
147 			transfer = dshare->slave_buffer_size - slave_appl_ptr;
148 		share_areas(dshare, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
149 		size -= transfer;
150 		if (! size)
151 			break;
152 		slave_appl_ptr += transfer;
153 		slave_appl_ptr %= dshare->slave_buffer_size;
154 		appl_ptr += transfer;
155 		appl_ptr %= pcm->buffer_size;
156 	}
157 }
158 
159 /*
160  *  synchronize hardware pointer (hw_ptr) with ours
161  */
snd_pcm_dshare_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)162 static int snd_pcm_dshare_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)
163 {
164 	snd_pcm_direct_t *dshare = pcm->private_data;
165 	snd_pcm_uframes_t old_slave_hw_ptr, avail;
166 	snd_pcm_sframes_t diff;
167 
168 	old_slave_hw_ptr = dshare->slave_hw_ptr;
169 	dshare->slave_hw_ptr = slave_hw_ptr;
170 	diff = pcm_frame_diff(slave_hw_ptr, old_slave_hw_ptr, dshare->slave_boundary);
171 	if (diff == 0)		/* fast path */
172 		return 0;
173 	if (dshare->state != SND_PCM_STATE_RUNNING &&
174 	    dshare->state != SND_PCM_STATE_DRAINING)
175 		/* not really started yet - don't update hw_ptr */
176 		return 0;
177 	dshare->hw_ptr += diff;
178 	dshare->hw_ptr %= pcm->boundary;
179 	// printf("sync ptr diff = %li\n", diff);
180 	if (pcm->stop_threshold >= pcm->boundary)	/* don't care */
181 		return 0;
182 	avail = snd_pcm_mmap_playback_avail(pcm);
183 	if (avail > dshare->avail_max)
184 		dshare->avail_max = avail;
185 	if (avail >= pcm->stop_threshold) {
186 		snd_timer_stop(dshare->timer);
187 		do_silence(pcm);
188 		gettimestamp(&dshare->trigger_tstamp, pcm->tstamp_type);
189 		if (dshare->state == SND_PCM_STATE_RUNNING) {
190 			dshare->state = SND_PCM_STATE_XRUN;
191 			return -EPIPE;
192 		}
193 		dshare->state = SND_PCM_STATE_SETUP;
194 		/* clear queue to remove pending poll events */
195 		snd_pcm_direct_clear_timer_queue(dshare);
196 	}
197 	return 0;
198 }
199 
snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm)200 static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm)
201 {
202 	snd_pcm_direct_t *dshare = pcm->private_data;
203 	snd_pcm_uframes_t slave_hw_ptr;
204 	int err;
205 
206 	if (dshare->slowptr)
207 		snd_pcm_hwsync(dshare->spcm);
208 	slave_hw_ptr = *dshare->spcm->hw.ptr;
209 	err = snd_pcm_direct_check_xrun(dshare, pcm);
210 	if (err < 0)
211 		return err;
212 
213 	return snd_pcm_dshare_sync_ptr0(pcm, slave_hw_ptr);
214 }
215 
216 /*
217  *  plugin implementation
218  */
219 
220 static snd_pcm_state_t snd_pcm_dshare_state(snd_pcm_t *pcm);
221 
snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status)222 static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
223 {
224 	snd_pcm_direct_t *dshare = pcm->private_data;
225 
226 	memset(status, 0, sizeof(*status));
227 	snd_pcm_status(dshare->spcm, status);
228 
229 	switch (dshare->state) {
230 	case SNDRV_PCM_STATE_DRAINING:
231 	case SNDRV_PCM_STATE_RUNNING:
232 		snd_pcm_dshare_sync_ptr0(pcm, status->hw_ptr);
233 		status->delay = snd_pcm_mmap_playback_delay(pcm);
234 		break;
235 	default:
236 		break;
237 	}
238 	status->state = snd_pcm_dshare_state(pcm);
239 	status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */
240 	status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */
241 	status->trigger_tstamp = dshare->trigger_tstamp;
242 	status->avail = snd_pcm_mmap_playback_avail(pcm);
243 	status->avail_max = status->avail > dshare->avail_max ? status->avail : dshare->avail_max;
244 	dshare->avail_max = 0;
245 	return 0;
246 }
247 
snd_pcm_dshare_state(snd_pcm_t *pcm)248 static snd_pcm_state_t snd_pcm_dshare_state(snd_pcm_t *pcm)
249 {
250 	snd_pcm_direct_t *dshare = pcm->private_data;
251 
252 	snd_pcm_direct_check_xrun(dshare, pcm);
253 	if (dshare->state == STATE_RUN_PENDING)
254 		return SNDRV_PCM_STATE_RUNNING;
255 	return dshare->state;
256 }
257 
snd_pcm_dshare_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)258 static int snd_pcm_dshare_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
259 {
260 	snd_pcm_direct_t *dshare = pcm->private_data;
261 	int err;
262 
263 	switch (dshare->state) {
264 	case SNDRV_PCM_STATE_DRAINING:
265 	case SNDRV_PCM_STATE_RUNNING:
266 		err = snd_pcm_dshare_sync_ptr(pcm);
267 		if (err < 0)
268 			return err;
269 		/* fallthru */
270 	case SNDRV_PCM_STATE_PREPARED:
271 	case SNDRV_PCM_STATE_SUSPENDED:
272 	case STATE_RUN_PENDING:
273 		*delayp = snd_pcm_mmap_playback_delay(pcm);
274 		return 0;
275 	case SNDRV_PCM_STATE_XRUN:
276 		return -EPIPE;
277 	case SNDRV_PCM_STATE_DISCONNECTED:
278 		return -ENODEV;
279 	default:
280 		return -EBADFD;
281 	}
282 }
283 
snd_pcm_dshare_hwsync(snd_pcm_t *pcm)284 static int snd_pcm_dshare_hwsync(snd_pcm_t *pcm)
285 {
286 	snd_pcm_direct_t *dshare = pcm->private_data;
287 
288 	switch(dshare->state) {
289 	case SNDRV_PCM_STATE_DRAINING:
290 	case SNDRV_PCM_STATE_RUNNING:
291 		return snd_pcm_dshare_sync_ptr(pcm);
292 	case SNDRV_PCM_STATE_PREPARED:
293 	case SNDRV_PCM_STATE_SUSPENDED:
294 		return 0;
295 	case SNDRV_PCM_STATE_XRUN:
296 		return -EPIPE;
297 	case SNDRV_PCM_STATE_DISCONNECTED:
298 		return -ENODEV;
299 	default:
300 		return -EBADFD;
301 	}
302 }
303 
snd_pcm_dshare_reset(snd_pcm_t *pcm)304 static int snd_pcm_dshare_reset(snd_pcm_t *pcm)
305 {
306 	snd_pcm_direct_t *dshare = pcm->private_data;
307 	dshare->hw_ptr %= pcm->period_size;
308 	dshare->appl_ptr = dshare->last_appl_ptr = dshare->hw_ptr;
309 	snd_pcm_direct_reset_slave_ptr(pcm, dshare, *dshare->spcm->hw.ptr);
310 	return 0;
311 }
312 
snd_pcm_dshare_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dshare)313 static int snd_pcm_dshare_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dshare)
314 {
315 	int err;
316 
317 	snd_pcm_hwsync(dshare->spcm);
318 	snd_pcm_direct_reset_slave_ptr(pcm, dshare, *dshare->spcm->hw.ptr);
319 	err = snd_timer_start(dshare->timer);
320 	if (err < 0)
321 		return err;
322 	dshare->state = SND_PCM_STATE_RUNNING;
323 	return 0;
324 }
325 
snd_pcm_dshare_start(snd_pcm_t *pcm)326 static int snd_pcm_dshare_start(snd_pcm_t *pcm)
327 {
328 	snd_pcm_direct_t *dshare = pcm->private_data;
329 	snd_pcm_sframes_t avail;
330 	int err;
331 
332 	if (dshare->state != SND_PCM_STATE_PREPARED)
333 		return -EBADFD;
334 	avail = snd_pcm_mmap_playback_hw_avail(pcm);
335 	if (avail == 0)
336 		dshare->state = STATE_RUN_PENDING;
337 	else if (avail < 0)
338 		return 0;
339 	else {
340 		err = snd_pcm_dshare_start_timer(pcm, dshare);
341 		if (err < 0)
342 			return err;
343 		snd_pcm_dshare_sync_area(pcm);
344 	}
345 	gettimestamp(&dshare->trigger_tstamp, pcm->tstamp_type);
346 	return 0;
347 }
348 
snd_pcm_dshare_drop(snd_pcm_t *pcm)349 static int snd_pcm_dshare_drop(snd_pcm_t *pcm)
350 {
351 	snd_pcm_direct_t *dshare = pcm->private_data;
352 	if (dshare->state == SND_PCM_STATE_OPEN)
353 		return -EBADFD;
354 	dshare->state = SND_PCM_STATE_SETUP;
355 	snd_pcm_direct_timer_stop(dshare);
356 	do_silence(pcm);
357 	return 0;
358 }
359 
360 /* locked version */
__snd_pcm_dshare_drain(snd_pcm_t *pcm)361 static int __snd_pcm_dshare_drain(snd_pcm_t *pcm)
362 {
363 	snd_pcm_direct_t *dshare = pcm->private_data;
364 	snd_pcm_uframes_t stop_threshold;
365 	int err;
366 
367 	switch (snd_pcm_state(dshare->spcm)) {
368 	case SND_PCM_STATE_SUSPENDED:
369 		return -ESTRPIPE;
370 	default:
371 		break;
372 	}
373 
374 	if (dshare->state == SND_PCM_STATE_OPEN)
375 		return -EBADFD;
376 	if (pcm->mode & SND_PCM_NONBLOCK)
377 		return -EAGAIN;
378 	if (dshare->state == SND_PCM_STATE_PREPARED) {
379 		if (snd_pcm_mmap_playback_hw_avail(pcm) > 0)
380 			snd_pcm_dshare_start(pcm);
381 		else {
382 			snd_pcm_dshare_drop(pcm);
383 			return 0;
384 		}
385 	}
386 
387 	if (dshare->state == SND_PCM_STATE_XRUN) {
388 		snd_pcm_dshare_drop(pcm);
389 		return 0;
390 	}
391 
392 	stop_threshold = pcm->stop_threshold;
393 	if (pcm->stop_threshold > pcm->buffer_size)
394 		pcm->stop_threshold = pcm->buffer_size;
395 	dshare->state = SND_PCM_STATE_DRAINING;
396 	do {
397 		err = snd_pcm_dshare_sync_ptr(pcm);
398 		if (err < 0) {
399 			snd_pcm_dshare_drop(pcm);
400 			break;
401 		}
402 		if (dshare->state == SND_PCM_STATE_DRAINING) {
403 			snd_pcm_dshare_sync_area(pcm);
404 			snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN);
405 			snd_pcm_direct_clear_timer_queue(dshare); /* force poll to wait */
406 
407 			switch (snd_pcm_state(dshare->spcm)) {
408 			case SND_PCM_STATE_SUSPENDED:
409 				return -ESTRPIPE;
410 			default:
411 				break;
412 			}
413 		}
414 	} while (dshare->state == SND_PCM_STATE_DRAINING);
415 	pcm->stop_threshold = stop_threshold;
416 	return 0;
417 }
418 
snd_pcm_dshare_drain(snd_pcm_t *pcm)419 static int snd_pcm_dshare_drain(snd_pcm_t *pcm)
420 {
421 	int err;
422 
423 	snd_pcm_lock(pcm);
424 	err = __snd_pcm_dshare_drain(pcm);
425 	snd_pcm_unlock(pcm);
426 	return err;
427 }
428 
snd_pcm_dshare_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)429 static int snd_pcm_dshare_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
430 {
431 	return -EIO;
432 }
433 
snd_pcm_dshare_rewindable(snd_pcm_t *pcm)434 static snd_pcm_sframes_t snd_pcm_dshare_rewindable(snd_pcm_t *pcm)
435 {
436 	return snd_pcm_mmap_playback_hw_rewindable(pcm);
437 }
438 
snd_pcm_dshare_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)439 static snd_pcm_sframes_t snd_pcm_dshare_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
440 {
441 	snd_pcm_sframes_t avail;
442 
443 	avail = snd_pcm_dshare_rewindable(pcm);
444 	if (frames > (snd_pcm_uframes_t)avail)
445 		frames = avail;
446 	snd_pcm_mmap_appl_backward(pcm, frames);
447 	return frames;
448 }
449 
snd_pcm_dshare_forwardable(snd_pcm_t *pcm)450 static snd_pcm_sframes_t snd_pcm_dshare_forwardable(snd_pcm_t *pcm)
451 {
452 	return snd_pcm_mmap_playback_avail(pcm);
453 }
454 
snd_pcm_dshare_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)455 static snd_pcm_sframes_t snd_pcm_dshare_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
456 {
457 	snd_pcm_sframes_t avail;
458 
459 	avail = snd_pcm_dshare_forwardable(pcm);
460 	if (frames > (snd_pcm_uframes_t)avail)
461 		frames = avail;
462 	snd_pcm_mmap_appl_forward(pcm, frames);
463 	return frames;
464 }
465 
snd_pcm_dshare_readi(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)466 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)
467 {
468 	return -ENODEV;
469 }
470 
snd_pcm_dshare_readn(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)471 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)
472 {
473 	return -ENODEV;
474 }
475 
snd_pcm_dshare_close(snd_pcm_t *pcm)476 static int snd_pcm_dshare_close(snd_pcm_t *pcm)
477 {
478 	snd_pcm_direct_t *dshare = pcm->private_data;
479 
480 	if (dshare->timer)
481 		snd_timer_close(dshare->timer);
482 	if (dshare->bindings)
483 		do_silence(pcm);
484 	snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT);
485 	dshare->shmptr->u.dshare.chn_mask &= ~dshare->u.dshare.chn_mask;
486 	snd_pcm_close(dshare->spcm);
487  	if (dshare->server)
488  		snd_pcm_direct_server_discard(dshare);
489  	if (dshare->client)
490  		snd_pcm_direct_client_discard(dshare);
491 	if (snd_pcm_direct_shm_discard(dshare)) {
492 		if (snd_pcm_direct_semaphore_discard(dshare))
493 			snd_pcm_direct_semaphore_final(dshare, DIRECT_IPC_SEM_CLIENT);
494 	} else
495 		snd_pcm_direct_semaphore_final(dshare, DIRECT_IPC_SEM_CLIENT);
496 	free(dshare->bindings);
497 	pcm->private_data = NULL;
498 	free(dshare);
499 	return 0;
500 }
501 
snd_pcm_dshare_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)502 static snd_pcm_sframes_t snd_pcm_dshare_mmap_commit(snd_pcm_t *pcm,
503 						  snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
504 						  snd_pcm_uframes_t size)
505 {
506 	snd_pcm_direct_t *dshare = pcm->private_data;
507 	int err;
508 
509 	err = snd_pcm_direct_check_xrun(dshare, pcm);
510 	if (err < 0)
511 		return err;
512 	if (! size)
513 		return 0;
514 	snd_pcm_mmap_appl_forward(pcm, size);
515 	if (dshare->state == STATE_RUN_PENDING) {
516 		err = snd_pcm_dshare_start_timer(pcm, dshare);
517 		if (err < 0)
518 			return err;
519 	} else if (dshare->state == SND_PCM_STATE_RUNNING ||
520 		   dshare->state == SND_PCM_STATE_DRAINING) {
521 		if ((err = snd_pcm_dshare_sync_ptr(pcm)) < 0)
522 			return err;
523 	}
524 	if (dshare->state == SND_PCM_STATE_RUNNING ||
525 	    dshare->state == SND_PCM_STATE_DRAINING) {
526 		/* ok, we commit the changes after the validation of area */
527 		/* it's intended, although the result might be crappy */
528 		snd_pcm_dshare_sync_area(pcm);
529 		/* clear timer queue to avoid a bogus return from poll */
530 		if (snd_pcm_mmap_playback_avail(pcm) < pcm->avail_min)
531 			snd_pcm_direct_clear_timer_queue(dshare);
532 	}
533 	return size;
534 }
535 
snd_pcm_dshare_avail_update(snd_pcm_t *pcm)536 static snd_pcm_sframes_t snd_pcm_dshare_avail_update(snd_pcm_t *pcm)
537 {
538 	snd_pcm_direct_t *dshare = pcm->private_data;
539 	int err;
540 
541 	if (dshare->state == SND_PCM_STATE_RUNNING ||
542 	    dshare->state == SND_PCM_STATE_DRAINING) {
543 		if ((err = snd_pcm_dshare_sync_ptr(pcm)) < 0)
544 			return err;
545 	}
546 	if (dshare->state == SND_PCM_STATE_XRUN)
547 		return -EPIPE;
548 
549 	return snd_pcm_mmap_playback_avail(pcm);
550 }
551 
snd_pcm_dshare_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail, snd_htimestamp_t *tstamp)552 static int snd_pcm_dshare_htimestamp(snd_pcm_t *pcm,
553 				     snd_pcm_uframes_t *avail,
554 				     snd_htimestamp_t *tstamp)
555 {
556 	snd_pcm_direct_t *dshare = pcm->private_data;
557 	snd_pcm_uframes_t avail1;
558 	int ok = 0;
559 
560 	while (1) {
561 		if (dshare->state == SND_PCM_STATE_RUNNING ||
562 		    dshare->state == SND_PCM_STATE_DRAINING)
563 			snd_pcm_dshare_sync_ptr(pcm);
564 		avail1 = snd_pcm_mmap_playback_avail(pcm);
565 		if (ok && *avail == avail1)
566 			break;
567 		*avail = avail1;
568 		*tstamp = snd_pcm_hw_fast_tstamp(dshare->spcm);
569 		ok = 1;
570 	}
571 	return 0;
572 }
573 
snd_pcm_dshare_dump(snd_pcm_t *pcm, snd_output_t *out)574 static void snd_pcm_dshare_dump(snd_pcm_t *pcm, snd_output_t *out)
575 {
576 	snd_pcm_direct_t *dshare = pcm->private_data;
577 
578 	snd_output_printf(out, "Direct Share PCM\n");
579 	if (pcm->setup) {
580 		snd_output_printf(out, "Its setup is:\n");
581 		snd_pcm_dump_setup(pcm, out);
582 	}
583 	if (dshare->spcm)
584 		snd_pcm_dump(dshare->spcm, out);
585 }
586 
587 static const snd_pcm_ops_t snd_pcm_dshare_dummy_ops = {
588 	.close = snd_pcm_dshare_close,
589 };
590 
591 static const snd_pcm_fast_ops_t snd_pcm_dshare_fast_dummy_ops;
592 
593 static const snd_pcm_ops_t snd_pcm_dshare_ops = {
594 	.close = snd_pcm_dshare_close,
595 	.info = snd_pcm_direct_info,
596 	.hw_refine = snd_pcm_direct_hw_refine,
597 	.hw_params = snd_pcm_direct_hw_params,
598 	.hw_free = snd_pcm_direct_hw_free,
599 	.sw_params = snd_pcm_direct_sw_params,
600 	.channel_info = snd_pcm_direct_channel_info,
601 	.dump = snd_pcm_dshare_dump,
602 	.nonblock = snd_pcm_direct_nonblock,
603 	.async = snd_pcm_direct_async,
604 	.mmap = snd_pcm_direct_mmap,
605 	.munmap = snd_pcm_direct_munmap,
606 	.get_chmap = snd_pcm_direct_get_chmap,
607 	.set_chmap = snd_pcm_direct_set_chmap,
608 };
609 
610 static const snd_pcm_fast_ops_t snd_pcm_dshare_fast_ops = {
611 	.status = snd_pcm_dshare_status,
612 	.state = snd_pcm_dshare_state,
613 	.hwsync = snd_pcm_dshare_hwsync,
614 	.delay = snd_pcm_dshare_delay,
615 	.prepare = snd_pcm_direct_prepare,
616 	.reset = snd_pcm_dshare_reset,
617 	.start = snd_pcm_dshare_start,
618 	.drop = snd_pcm_dshare_drop,
619 	.drain = snd_pcm_dshare_drain,
620 	.pause = snd_pcm_dshare_pause,
621 	.rewindable = snd_pcm_dshare_rewindable,
622 	.rewind = snd_pcm_dshare_rewind,
623 	.forwardable = snd_pcm_dshare_forwardable,
624 	.forward = snd_pcm_dshare_forward,
625 	.resume = snd_pcm_direct_resume,
626 	.link = NULL,
627 	.link_slaves = NULL,
628 	.unlink = NULL,
629 	.writei = snd_pcm_mmap_writei,
630 	.writen = snd_pcm_mmap_writen,
631 	.readi = snd_pcm_dshare_readi,
632 	.readn = snd_pcm_dshare_readn,
633 	.avail_update = snd_pcm_dshare_avail_update,
634 	.mmap_commit = snd_pcm_dshare_mmap_commit,
635 	.htimestamp = snd_pcm_dshare_htimestamp,
636 	.poll_descriptors = snd_pcm_direct_poll_descriptors,
637 	.poll_descriptors_count = NULL,
638 	.poll_revents = snd_pcm_direct_poll_revents,
639 };
640 
641 /**
642  * \brief Creates a new dshare PCM
643  * \param pcmp Returns created PCM handle
644  * \param name Name of PCM
645  * \param opts Direct PCM configurations
646  * \param params Parameters for slave
647  * \param root Configuration root
648  * \param sconf Slave configuration
649  * \param stream PCM Direction (stream)
650  * \param mode PCM Mode
651  * \retval zero on success otherwise a negative error code
652  * \warning Using of this function might be dangerous in the sense
653  *          of compatibility reasons. The prototype might be freely
654  *          changed in future.
655  */
snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name, struct snd_pcm_direct_open_conf *opts, struct slave_params *params, snd_config_t *root, snd_config_t *sconf, snd_pcm_stream_t stream, int mode)656 int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
657 			struct snd_pcm_direct_open_conf *opts,
658 			struct slave_params *params,
659 			snd_config_t *root, snd_config_t *sconf,
660 			snd_pcm_stream_t stream, int mode)
661 {
662 	snd_pcm_t *pcm, *spcm = NULL;
663 	snd_pcm_direct_t *dshare;
664 	int ret, first_instance;
665 	unsigned int chn;
666 
667 	assert(pcmp);
668 
669 	if (stream != SND_PCM_STREAM_PLAYBACK) {
670 		SNDERR("The dshare plugin supports only playback stream");
671 		return -EINVAL;
672 	}
673 
674 	ret = _snd_pcm_direct_new(&pcm, &dshare, SND_PCM_TYPE_DSHARE, name, opts, params, stream, mode);
675 	if (ret < 0)
676 		return ret;
677 	first_instance = ret;
678 
679 	if (!dshare->bindings) {
680 		pcm->ops = &snd_pcm_dshare_dummy_ops;
681 		pcm->fast_ops = &snd_pcm_dshare_fast_dummy_ops;
682 	} else {
683 		pcm->ops = &snd_pcm_dshare_ops;
684 		pcm->fast_ops = &snd_pcm_dshare_fast_ops;
685 	}
686 	pcm->private_data = dshare;
687 	dshare->state = SND_PCM_STATE_OPEN;
688 	dshare->slowptr = opts->slowptr;
689 	dshare->max_periods = opts->max_periods;
690 	dshare->var_periodsize = opts->var_periodsize;
691 	dshare->hw_ptr_alignment = opts->hw_ptr_alignment;
692 	dshare->sync_ptr = snd_pcm_dshare_sync_ptr;
693 
694  retry:
695 	if (first_instance) {
696 		/* recursion is already checked in
697 		   snd_pcm_direct_get_slave_ipc_offset() */
698 		ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
699 					 mode | SND_PCM_NONBLOCK, NULL);
700 		if (ret < 0) {
701 			SNDERR("unable to open slave");
702 			goto _err;
703 		}
704 
705 		if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
706 			SNDERR("dshare plugin can be only connected to hw plugin");
707 			goto _err;
708 		}
709 
710 		ret = snd_pcm_direct_initialize_slave(dshare, spcm, params);
711 		if (ret < 0) {
712 			SNDERR("unable to initialize slave");
713 			goto _err;
714 		}
715 
716 		dshare->spcm = spcm;
717 
718 		if (dshare->shmptr->use_server) {
719 			ret = snd_pcm_direct_server_create(dshare);
720 			if (ret < 0) {
721 				SNDERR("unable to create server");
722 				goto _err;
723 			}
724 		}
725 
726 		dshare->shmptr->type = spcm->type;
727 	} else {
728 		if (dshare->shmptr->use_server) {
729 			/* up semaphore to avoid deadlock */
730 			snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
731 			ret = snd_pcm_direct_client_connect(dshare);
732 			if (ret < 0) {
733 				SNDERR("unable to connect client");
734 				goto _err_nosem;
735 			}
736 
737 			snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT);
738 			ret = snd_pcm_direct_open_secondary_client(&spcm, dshare, "dshare_client");
739 			if (ret < 0)
740 				goto _err;
741 
742 		} else {
743 
744 			ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
745 						 mode | SND_PCM_NONBLOCK |
746 						 SND_PCM_APPEND,
747 						 NULL);
748 			if (ret < 0) {
749 				/* all other streams have been closed;
750 				 * retry as the first instance
751 				 */
752 				if (ret == -EBADFD) {
753 					first_instance = 1;
754 					goto retry;
755 				}
756 				SNDERR("unable to open slave");
757 				goto _err;
758 			}
759 			if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
760 				SNDERR("dshare plugin can be only connected to hw plugin");
761 				ret = -EINVAL;
762 				goto _err;
763 			}
764 
765 			ret = snd_pcm_direct_initialize_secondary_slave(dshare, spcm, params);
766 			if (ret < 0) {
767 				SNDERR("unable to initialize slave");
768 				goto _err;
769 			}
770 		}
771 
772 		dshare->spcm = spcm;
773 	}
774 
775 	for (chn = 0; dshare->bindings && (chn < dshare->channels); chn++) {
776 		unsigned int dchn = dshare->bindings ? dshare->bindings[chn] : chn;
777 		if (dchn != UINT_MAX)
778 			dshare->u.dshare.chn_mask |= (1ULL << dchn);
779 	}
780 	if (dshare->shmptr->u.dshare.chn_mask & dshare->u.dshare.chn_mask) {
781 		SNDERR("destination channel specified in bindings is already used");
782 		dshare->u.dshare.chn_mask = 0;
783 		ret = -EINVAL;
784 		goto _err;
785 	}
786 	dshare->shmptr->u.dshare.chn_mask |= dshare->u.dshare.chn_mask;
787 
788 	ret = snd_pcm_direct_initialize_poll_fd(dshare);
789 	if (ret < 0) {
790 		SNDERR("unable to initialize poll_fd");
791 		goto _err;
792 	}
793 
794 	pcm->poll_fd = dshare->poll_fd;
795 	pcm->poll_events = POLLIN;	/* it's different than other plugins */
796 	pcm->tstamp_type = spcm->tstamp_type;
797 	pcm->mmap_rw = 1;
798 	snd_pcm_set_hw_ptr(pcm, &dshare->hw_ptr, -1, 0);
799 	snd_pcm_set_appl_ptr(pcm, &dshare->appl_ptr, -1, 0);
800 
801 	snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
802 
803 	*pcmp = pcm;
804 	return 0;
805 
806  _err:
807 	if (dshare->shmptr != (void *) -1)
808 		dshare->shmptr->u.dshare.chn_mask &= ~dshare->u.dshare.chn_mask;
809 	if (dshare->timer)
810 		snd_timer_close(dshare->timer);
811 	if (dshare->server)
812 		snd_pcm_direct_server_discard(dshare);
813 	if (dshare->client)
814 		snd_pcm_direct_client_discard(dshare);
815 	if (spcm)
816 		snd_pcm_close(spcm);
817 	if ((dshare->shmid >= 0) && (snd_pcm_direct_shm_discard(dshare))) {
818 		if (snd_pcm_direct_semaphore_discard(dshare))
819 			snd_pcm_direct_semaphore_final(dshare, DIRECT_IPC_SEM_CLIENT);
820 	} else
821 		snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT);
822  _err_nosem:
823 	free(dshare->bindings);
824 	free(dshare);
825 	snd_pcm_free(pcm);
826 	return ret;
827 }
828 
829 /*! \page pcm_plugins
830 
831 \section pcm_plugins_dshare Plugin: dshare
832 
833 This plugin provides sharing channels.
834 Unlike \ref pcm_plugins_share "share plugin", this plugin doesn't need
835 the explicit server program but accesses the shared buffer concurrently
836 from each client as well as \ref pcm_plugins_dmix "dmix" and
837 \ref pcm_plugins_dsnoop "dsnoop" plugins do.
838 The parameters below are almost identical with these plugins.
839 
840 \code
841 pcm.name {
842 	type dshare		# Direct sharing
843 	ipc_key INT		# unique IPC key
844 	ipc_key_add_uid BOOL	# add current uid to unique IPC key
845 	ipc_perm INT		# IPC permissions (octal, default 0600)
846 	hw_ptr_alignment STR	# Slave application and hw pointer alignment type
847 		# STR can be one of the below strings :
848 		# no (or off)
849 		# roundup
850 		# rounddown
851 		# auto (default)
852 	tstamp_type STR		# timestamp type
853 				# STR can be one of the below strings :
854 				# default, gettimeofday, monotonic, monotonic_raw
855 	slave STR
856 	# or
857 	slave {			# Slave definition
858 		pcm STR		# slave PCM name
859 		# or
860 		pcm { }		# slave PCM definition
861 		format STR	# format definition
862 		rate INT	# rate definition
863 		channels INT
864 		period_time INT	# in usec
865 		# or
866 		period_size INT	# in frames
867 		buffer_time INT	# in usec
868 		# or
869 		buffer_size INT # in frames
870 		periods INT	# when buffer_size or buffer_time is not specified
871 	}
872 	bindings {		# note: this is client independent!!!
873 		N INT		# maps slave channel to client channel N
874 	}
875 	slowptr BOOL		# slow but more precise pointer updates
876 }
877 \endcode
878 
879 <code>hw_ptr_alignment</code> specifies slave application and hw
880 pointer alignment type. By default hw_ptr_alignment is auto. Below are
881 the possible configurations:
882 - no: minimal latency with minimal frames dropped at startup. But
883   wakeup of application (return from snd_pcm_wait() or poll()) can
884   take up to 2 * period.
885 - roundup: It is guaranteed that all frames will be played at
886   startup. But the latency will increase upto period-1 frames.
887 - rounddown: It is guaranteed that a wakeup will happen for each
888   period and frames can be written from application. But on startup
889   upto period-1 frames will be dropped.
890 - auto: Selects the best approach depending on the used period and
891   buffer size.
892   If the application buffer size is < 2 * application period,
893   "roundup" will be selected to avoid under runs. If the slave_period
894   is < 10ms we could expect that there are low latency
895   requirements. Therefore "rounddown" will be chosen to avoid long
896   wakeup times. Such wakeup delay could otherwise end up with Xruns in
897   case of a dependency to another sound device (e.g. forwarding of
898   microphone to speaker). Else "no" will be chosen.
899 
900 \subsection pcm_plugins_dshare_funcref Function reference
901 
902 <UL>
903   <LI>snd_pcm_dshare_open()
904   <LI>_snd_pcm_dshare_open()
905 </UL>
906 
907 */
908 
909 /**
910  * \brief Creates a new dshare PCM
911  * \param pcmp Returns created PCM handle
912  * \param name Name of PCM
913  * \param root Root configuration node
914  * \param conf Configuration node with dshare PCM description
915  * \param stream PCM Stream
916  * \param mode PCM Mode
917  * \warning Using of this function might be dangerous in the sense
918  *          of compatibility reasons. The prototype might be freely
919  *          changed in future.
920  */
_snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name, snd_config_t *root, snd_config_t *conf, snd_pcm_stream_t stream, int mode)921 int _snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
922 		       snd_config_t *root, snd_config_t *conf,
923 		       snd_pcm_stream_t stream, int mode)
924 {
925 	snd_config_t *sconf;
926 	struct slave_params params;
927 	struct snd_pcm_direct_open_conf dopen;
928 	int bsize, psize;
929 	int err;
930 
931 	err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
932 	if (err < 0)
933 		return err;
934 
935 	/* the default settings, it might be invalid for some hardware */
936 	params.format = SND_PCM_FORMAT_S16;
937 	params.rate = 48000;
938 	params.channels = 2;
939 	params.period_time = -1;
940 	params.buffer_time = -1;
941 	bsize = psize = -1;
942 	params.periods = 3;
943 	err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8,
944 				 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &params.format,
945 				 SND_PCM_HW_PARAM_RATE, 0, &params.rate,
946 				 SND_PCM_HW_PARAM_CHANNELS, 0, &params.channels,
947 				 SND_PCM_HW_PARAM_PERIOD_TIME, 0, &params.period_time,
948 				 SND_PCM_HW_PARAM_BUFFER_TIME, 0, &params.buffer_time,
949 				 SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
950 				 SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
951 				 SND_PCM_HW_PARAM_PERIODS, 0, &params.periods);
952 	if (err < 0)
953 		return err;
954 
955 	/* set a reasonable default */
956 	if (psize == -1 && params.period_time == -1)
957 		params.period_time = 125000;	/* 0.125 seconds */
958 
959 	if (params.format == -2)
960 		params.format = SND_PCM_FORMAT_UNKNOWN;
961 
962 	params.period_size = psize;
963 	params.buffer_size = bsize;
964 
965 	err = snd_pcm_dshare_open(pcmp, name, &dopen, &params,
966 				  root, sconf, stream, mode);
967 	snd_config_delete(sconf);
968 	return err;
969 }
970 #ifndef DOC_HIDDEN
971 SND_DLSYM_BUILD_VERSION(_snd_pcm_dshare_open, SND_PCM_DLSYM_VERSION);
972 #endif
973