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