• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * \file pcm/pcm_dsnoop.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Capture Stream Snooping (dsnoop) Plugin Interface
5  * \author Jaroslav Kysela <perex@perex.cz>
6  * \date 2003
7  */
8 /*
9  *  PCM - Capture Stream Snooping
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_dsnoop = "";
51 #endif
52 
53 /*
54  *
55  */
56 
snoop_timestamp(snd_pcm_t * pcm)57 static int snoop_timestamp(snd_pcm_t *pcm)
58 {
59 	snd_pcm_direct_t *dsnoop = pcm->private_data;
60 	snd_pcm_uframes_t ptr1 = -2LL /* invalid value */, ptr2;
61 
62 	/* loop is required to sync hw.ptr with timestamp */
63 	while (1) {
64 		ptr2 = *dsnoop->spcm->hw.ptr;
65 		if (ptr1 == ptr2)
66 			break;
67 		ptr1 = ptr2;
68 		dsnoop->update_tstamp = snd_pcm_hw_fast_tstamp(dsnoop->spcm);
69 	}
70 	dsnoop->slave_hw_ptr = ptr1;
71 	return 0;
72 }
73 
snoop_areas(snd_pcm_direct_t * dsnoop,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)74 static void snoop_areas(snd_pcm_direct_t *dsnoop,
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, schn, channels;
82 	snd_pcm_format_t format;
83 
84 	channels = dsnoop->channels;
85 	format = dsnoop->shmptr->s.format;
86 	if (dsnoop->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 			schn = dsnoop->bindings ? dsnoop->bindings[chn] : chn;
94 			snd_pcm_area_copy(&dst_areas[chn], dst_ofs, &src_areas[schn], src_ofs, size, format);
95 		}
96 	}
97 }
98 
99 /*
100  *  synchronize shm ring buffer with hardware
101  */
snd_pcm_dsnoop_sync_area(snd_pcm_t * pcm,snd_pcm_uframes_t slave_hw_ptr,snd_pcm_uframes_t size)102 static void snd_pcm_dsnoop_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr, snd_pcm_uframes_t size)
103 {
104 	snd_pcm_direct_t *dsnoop = pcm->private_data;
105 	snd_pcm_uframes_t hw_ptr = dsnoop->hw_ptr;
106 	snd_pcm_uframes_t transfer;
107 	const snd_pcm_channel_area_t *src_areas, *dst_areas;
108 
109 	/* add sample areas here */
110 	dst_areas = snd_pcm_mmap_areas(pcm);
111 	src_areas = snd_pcm_mmap_areas(dsnoop->spcm);
112 	hw_ptr %= pcm->buffer_size;
113 	slave_hw_ptr %= dsnoop->slave_buffer_size;
114 	while (size > 0) {
115 		transfer = hw_ptr + size > pcm->buffer_size ? pcm->buffer_size - hw_ptr : size;
116 		transfer = slave_hw_ptr + transfer > dsnoop->slave_buffer_size ?
117 			dsnoop->slave_buffer_size - slave_hw_ptr : transfer;
118 		size -= transfer;
119 		snoop_areas(dsnoop, src_areas, dst_areas, slave_hw_ptr, hw_ptr, transfer);
120 		slave_hw_ptr += transfer;
121 	 	slave_hw_ptr %= dsnoop->slave_buffer_size;
122 		hw_ptr += transfer;
123 		hw_ptr %= pcm->buffer_size;
124 	}
125 }
126 
127 /*
128  *  synchronize hardware pointer (hw_ptr) with ours
129  */
snd_pcm_dsnoop_sync_ptr(snd_pcm_t * pcm)130 static int snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
131 {
132 	snd_pcm_direct_t *dsnoop = pcm->private_data;
133 	snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
134 	snd_pcm_sframes_t diff;
135 	int err;
136 
137 	switch (snd_pcm_state(dsnoop->spcm)) {
138 	case SND_PCM_STATE_DISCONNECTED:
139 		dsnoop->state = SNDRV_PCM_STATE_DISCONNECTED;
140 		return -ENODEV;
141 	case SND_PCM_STATE_XRUN:
142 		if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
143 			return err;
144 		break;
145 	default:
146 		break;
147 	}
148 	if (snd_pcm_direct_client_chk_xrun(dsnoop, pcm))
149 		return -EPIPE;
150 	if (dsnoop->slowptr)
151 		snd_pcm_hwsync(dsnoop->spcm);
152 	old_slave_hw_ptr = dsnoop->slave_hw_ptr;
153 	snoop_timestamp(pcm);
154 	slave_hw_ptr = dsnoop->slave_hw_ptr;
155 	diff = pcm_frame_diff(slave_hw_ptr, old_slave_hw_ptr, dsnoop->slave_boundary);
156 	if (diff == 0)		/* fast path */
157 		return 0;
158 	snd_pcm_dsnoop_sync_area(pcm, old_slave_hw_ptr, diff);
159 	dsnoop->hw_ptr += diff;
160 	dsnoop->hw_ptr %= pcm->boundary;
161 	// printf("sync ptr diff = %li\n", diff);
162 	if (pcm->stop_threshold >= pcm->boundary)	/* don't care */
163 		return 0;
164 	if ((avail = snd_pcm_mmap_capture_avail(pcm)) >= pcm->stop_threshold) {
165 		gettimestamp(&dsnoop->trigger_tstamp, pcm->tstamp_type);
166 		dsnoop->state = SND_PCM_STATE_XRUN;
167 		dsnoop->avail_max = avail;
168 		return -EPIPE;
169 	}
170 	if (avail > dsnoop->avail_max)
171 		dsnoop->avail_max = avail;
172 	return 0;
173 }
174 
175 /*
176  *  plugin implementation
177  */
178 
snd_pcm_dsnoop_status(snd_pcm_t * pcm,snd_pcm_status_t * status)179 static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
180 {
181 	snd_pcm_direct_t *dsnoop = pcm->private_data;
182 	snd_pcm_state_t state;
183 
184 	switch(dsnoop->state) {
185 	case SNDRV_PCM_STATE_DRAINING:
186 	case SNDRV_PCM_STATE_RUNNING:
187 		snd_pcm_dsnoop_sync_ptr(pcm);
188 		break;
189 	default:
190 		break;
191 	}
192 	memset(status, 0, sizeof(*status));
193 	snd_pcm_status(dsnoop->spcm, status);
194 	state = snd_pcm_state(dsnoop->spcm);
195 	status->state = state == SND_PCM_STATE_RUNNING ? dsnoop->state : state;
196 	status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */
197 	status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */
198 	status->trigger_tstamp = dsnoop->trigger_tstamp;
199 	status->avail = snd_pcm_mmap_capture_avail(pcm);
200 	status->avail_max = status->avail > dsnoop->avail_max ? status->avail : dsnoop->avail_max;
201 	dsnoop->avail_max = 0;
202 	status->delay = snd_pcm_mmap_capture_delay(pcm);
203 	return 0;
204 }
205 
snd_pcm_dsnoop_state(snd_pcm_t * pcm)206 static snd_pcm_state_t snd_pcm_dsnoop_state(snd_pcm_t *pcm)
207 {
208 	snd_pcm_direct_t *dsnoop = pcm->private_data;
209 	int err;
210 	snd_pcm_state_t state;
211 	state = snd_pcm_state(dsnoop->spcm);
212 	switch (state) {
213 	case SND_PCM_STATE_SUSPENDED:
214 	case SND_PCM_STATE_DISCONNECTED:
215 		dsnoop->state = state;
216 		return state;
217 	case SND_PCM_STATE_XRUN:
218 		if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
219 			return err;
220 		break;
221 	default:
222 		break;
223 	}
224 	snd_pcm_direct_client_chk_xrun(dsnoop, pcm);
225 	return dsnoop->state;
226 }
227 
snd_pcm_dsnoop_delay(snd_pcm_t * pcm,snd_pcm_sframes_t * delayp)228 static int snd_pcm_dsnoop_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
229 {
230 	snd_pcm_direct_t *dsnoop = pcm->private_data;
231 	int err;
232 
233 	switch(dsnoop->state) {
234 	case SNDRV_PCM_STATE_DRAINING:
235 	case SNDRV_PCM_STATE_RUNNING:
236 		err = snd_pcm_dsnoop_sync_ptr(pcm);
237 		if (err < 0)
238 			return err;
239 		/* Fall through */
240 	case SNDRV_PCM_STATE_PREPARED:
241 	case SNDRV_PCM_STATE_SUSPENDED:
242 		*delayp = snd_pcm_mmap_capture_avail(pcm);
243 		return 0;
244 	case SNDRV_PCM_STATE_XRUN:
245 		return -EPIPE;
246 	case SNDRV_PCM_STATE_DISCONNECTED:
247 		return -ENODEV;
248 	default:
249 		return -EBADFD;
250 	}
251 }
252 
snd_pcm_dsnoop_hwsync(snd_pcm_t * pcm)253 static int snd_pcm_dsnoop_hwsync(snd_pcm_t *pcm)
254 {
255 	snd_pcm_direct_t *dsnoop = pcm->private_data;
256 
257 	switch(dsnoop->state) {
258 	case SNDRV_PCM_STATE_DRAINING:
259 	case SNDRV_PCM_STATE_RUNNING:
260 		return snd_pcm_dsnoop_sync_ptr(pcm);
261 	case SNDRV_PCM_STATE_PREPARED:
262 	case SNDRV_PCM_STATE_SUSPENDED:
263 		return 0;
264 	case SNDRV_PCM_STATE_XRUN:
265 		return -EPIPE;
266 	case SNDRV_PCM_STATE_DISCONNECTED:
267 		return -ENODEV;
268 	default:
269 		return -EBADFD;
270 	}
271 }
272 
snd_pcm_dsnoop_reset(snd_pcm_t * pcm)273 static int snd_pcm_dsnoop_reset(snd_pcm_t *pcm)
274 {
275 	snd_pcm_direct_t *dsnoop = pcm->private_data;
276 	dsnoop->hw_ptr %= pcm->period_size;
277 	dsnoop->appl_ptr = dsnoop->hw_ptr;
278 	dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr;
279 	snd_pcm_direct_reset_slave_ptr(pcm, dsnoop);
280 	return 0;
281 }
282 
snd_pcm_dsnoop_start(snd_pcm_t * pcm)283 static int snd_pcm_dsnoop_start(snd_pcm_t *pcm)
284 {
285 	snd_pcm_direct_t *dsnoop = pcm->private_data;
286 	int err;
287 
288 	if (dsnoop->state != SND_PCM_STATE_PREPARED)
289 		return -EBADFD;
290 	snd_pcm_hwsync(dsnoop->spcm);
291 	snoop_timestamp(pcm);
292 	dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr;
293 	snd_pcm_direct_reset_slave_ptr(pcm, dsnoop);
294 	err = snd_timer_start(dsnoop->timer);
295 	if (err < 0)
296 		return err;
297 	dsnoop->state = SND_PCM_STATE_RUNNING;
298 	dsnoop->trigger_tstamp = dsnoop->update_tstamp;
299 	return 0;
300 }
301 
snd_pcm_dsnoop_drop(snd_pcm_t * pcm)302 static int snd_pcm_dsnoop_drop(snd_pcm_t *pcm)
303 {
304 	snd_pcm_direct_t *dsnoop = pcm->private_data;
305 	if (dsnoop->state == SND_PCM_STATE_OPEN)
306 		return -EBADFD;
307 	dsnoop->state = SND_PCM_STATE_SETUP;
308 	snd_timer_stop(dsnoop->timer);
309 	return 0;
310 }
311 
312 /* locked version */
__snd_pcm_dsnoop_drain(snd_pcm_t * pcm)313 static int __snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
314 {
315 	snd_pcm_direct_t *dsnoop = pcm->private_data;
316 	snd_pcm_uframes_t stop_threshold;
317 	int err;
318 
319 	if (dsnoop->state == SND_PCM_STATE_OPEN)
320 		return -EBADFD;
321 	stop_threshold = pcm->stop_threshold;
322 	if (pcm->stop_threshold > pcm->buffer_size)
323 		pcm->stop_threshold = pcm->buffer_size;
324 	while (dsnoop->state == SND_PCM_STATE_RUNNING) {
325 		err = snd_pcm_dsnoop_sync_ptr(pcm);
326 		if (err < 0)
327 			break;
328 		if (pcm->mode & SND_PCM_NONBLOCK)
329 			return -EAGAIN;
330 		__snd_pcm_wait_in_lock(pcm, -1);
331 	}
332 	pcm->stop_threshold = stop_threshold;
333 	return snd_pcm_dsnoop_drop(pcm);
334 }
335 
snd_pcm_dsnoop_drain(snd_pcm_t * pcm)336 static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
337 {
338 	int err;
339 
340 	snd_pcm_lock(pcm);
341 	err = __snd_pcm_dsnoop_drain(pcm);
342 	snd_pcm_unlock(pcm);
343 	return err;
344 }
345 
snd_pcm_dsnoop_pause(snd_pcm_t * pcm ATTRIBUTE_UNUSED,int enable ATTRIBUTE_UNUSED)346 static int snd_pcm_dsnoop_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
347 {
348 	return -EIO;
349 }
350 
snd_pcm_dsnoop_rewindable(snd_pcm_t * pcm)351 static snd_pcm_sframes_t snd_pcm_dsnoop_rewindable(snd_pcm_t *pcm)
352 {
353 	return snd_pcm_mmap_capture_hw_rewindable(pcm);
354 }
355 
snd_pcm_dsnoop_rewind(snd_pcm_t * pcm,snd_pcm_uframes_t frames)356 static snd_pcm_sframes_t snd_pcm_dsnoop_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
357 {
358 	snd_pcm_sframes_t avail;
359 
360 	avail = snd_pcm_dsnoop_rewindable(pcm);
361 	if (frames > (snd_pcm_uframes_t)avail)
362 		frames = avail;
363 	snd_pcm_mmap_appl_backward(pcm, frames);
364 	return frames;
365 }
366 
snd_pcm_dsnoop_forwardable(snd_pcm_t * pcm)367 static snd_pcm_sframes_t snd_pcm_dsnoop_forwardable(snd_pcm_t *pcm)
368 {
369 	return snd_pcm_mmap_capture_avail(pcm);
370 }
371 
snd_pcm_dsnoop_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)372 static snd_pcm_sframes_t snd_pcm_dsnoop_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
373 {
374 	snd_pcm_sframes_t avail;
375 
376 	avail = snd_pcm_dsnoop_forwardable(pcm);
377 	if (frames > (snd_pcm_uframes_t)avail)
378 		frames = avail;
379 	snd_pcm_mmap_appl_forward(pcm, frames);
380 	return frames;
381 }
382 
snd_pcm_dsnoop_writei(snd_pcm_t * pcm ATTRIBUTE_UNUSED,const void * buffer ATTRIBUTE_UNUSED,snd_pcm_uframes_t size ATTRIBUTE_UNUSED)383 static snd_pcm_sframes_t snd_pcm_dsnoop_writei(snd_pcm_t *pcm ATTRIBUTE_UNUSED, const void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
384 {
385 	return -ENODEV;
386 }
387 
snd_pcm_dsnoop_writen(snd_pcm_t * pcm ATTRIBUTE_UNUSED,void ** bufs ATTRIBUTE_UNUSED,snd_pcm_uframes_t size ATTRIBUTE_UNUSED)388 static snd_pcm_sframes_t snd_pcm_dsnoop_writen(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
389 {
390 	return -ENODEV;
391 }
392 
snd_pcm_dsnoop_close(snd_pcm_t * pcm)393 static int snd_pcm_dsnoop_close(snd_pcm_t *pcm)
394 {
395 	snd_pcm_direct_t *dsnoop = pcm->private_data;
396 
397 	if (dsnoop->timer)
398 		snd_timer_close(dsnoop->timer);
399 	snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
400 	snd_pcm_close(dsnoop->spcm);
401  	if (dsnoop->server)
402  		snd_pcm_direct_server_discard(dsnoop);
403  	if (dsnoop->client)
404  		snd_pcm_direct_client_discard(dsnoop);
405 	if (snd_pcm_direct_shm_discard(dsnoop)) {
406 		if (snd_pcm_direct_semaphore_discard(dsnoop))
407 			snd_pcm_direct_semaphore_final(dsnoop, DIRECT_IPC_SEM_CLIENT);
408 	} else
409 		snd_pcm_direct_semaphore_final(dsnoop, DIRECT_IPC_SEM_CLIENT);
410 	free(dsnoop->bindings);
411 	pcm->private_data = NULL;
412 	free(dsnoop);
413 	return 0;
414 }
415 
snd_pcm_dsnoop_mmap_commit(snd_pcm_t * pcm,snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)416 static snd_pcm_sframes_t snd_pcm_dsnoop_mmap_commit(snd_pcm_t *pcm,
417 						    snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
418 						    snd_pcm_uframes_t size)
419 {
420 	snd_pcm_direct_t *dsnoop = pcm->private_data;
421 	int err;
422 
423 	switch (snd_pcm_state(dsnoop->spcm)) {
424 	case SND_PCM_STATE_XRUN:
425 		if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
426 			return err;
427 		break;
428 	case SND_PCM_STATE_SUSPENDED:
429 		return -ESTRPIPE;
430 	default:
431 		break;
432 	}
433 	if (snd_pcm_direct_client_chk_xrun(dsnoop, pcm))
434 		return -EPIPE;
435 	if (dsnoop->state == SND_PCM_STATE_RUNNING) {
436 		err = snd_pcm_dsnoop_sync_ptr(pcm);
437 		if (err < 0)
438 			return err;
439 	}
440 	snd_pcm_mmap_appl_forward(pcm, size);
441 	/* clear timer queue to avoid a bogus return from poll */
442 	if (snd_pcm_mmap_capture_avail(pcm) < pcm->avail_min)
443 		snd_pcm_direct_clear_timer_queue(dsnoop);
444 	return size;
445 }
446 
snd_pcm_dsnoop_avail_update(snd_pcm_t * pcm)447 static snd_pcm_sframes_t snd_pcm_dsnoop_avail_update(snd_pcm_t *pcm)
448 {
449 	snd_pcm_direct_t *dsnoop = pcm->private_data;
450 	int err;
451 
452 	if (dsnoop->state == SND_PCM_STATE_RUNNING) {
453 		err = snd_pcm_dsnoop_sync_ptr(pcm);
454 		if (err < 0)
455 			return err;
456 	}
457 	if (dsnoop->state == SND_PCM_STATE_XRUN)
458 		return -EPIPE;
459 
460 	return snd_pcm_mmap_capture_avail(pcm);
461 }
462 
snd_pcm_dsnoop_htimestamp(snd_pcm_t * pcm,snd_pcm_uframes_t * avail,snd_htimestamp_t * tstamp)463 static int snd_pcm_dsnoop_htimestamp(snd_pcm_t *pcm,
464 				     snd_pcm_uframes_t *avail,
465 				     snd_htimestamp_t *tstamp)
466 {
467 	snd_pcm_direct_t *dsnoop = pcm->private_data;
468 	snd_pcm_uframes_t avail1;
469 	int ok = 0;
470 
471 	while (1) {
472 		if (dsnoop->state == SND_PCM_STATE_RUNNING ||
473 		    dsnoop->state == SND_PCM_STATE_DRAINING)
474 			snd_pcm_dsnoop_sync_ptr(pcm);
475 		avail1 = snd_pcm_mmap_capture_avail(pcm);
476 		if (ok && *avail == avail1)
477 			break;
478 		*avail = avail1;
479 		*tstamp = snd_pcm_hw_fast_tstamp(dsnoop->spcm);
480 		ok = 1;
481 	}
482 	return 0;
483 }
484 
snd_pcm_dsnoop_dump(snd_pcm_t * pcm,snd_output_t * out)485 static void snd_pcm_dsnoop_dump(snd_pcm_t *pcm, snd_output_t *out)
486 {
487 	snd_pcm_direct_t *dsnoop = pcm->private_data;
488 
489 	snd_output_printf(out, "Direct Snoop PCM\n");
490 	if (pcm->setup) {
491 		snd_output_printf(out, "Its setup is:\n");
492 		snd_pcm_dump_setup(pcm, out);
493 	}
494 	if (dsnoop->spcm)
495 		snd_pcm_dump(dsnoop->spcm, out);
496 }
497 
498 static const snd_pcm_ops_t snd_pcm_dsnoop_ops = {
499 	.close = snd_pcm_dsnoop_close,
500 	.info = snd_pcm_direct_info,
501 	.hw_refine = snd_pcm_direct_hw_refine,
502 	.hw_params = snd_pcm_direct_hw_params,
503 	.hw_free = snd_pcm_direct_hw_free,
504 	.sw_params = snd_pcm_direct_sw_params,
505 	.channel_info = snd_pcm_direct_channel_info,
506 	.dump = snd_pcm_dsnoop_dump,
507 	.nonblock = snd_pcm_direct_nonblock,
508 	.async = snd_pcm_direct_async,
509 	.mmap = snd_pcm_direct_mmap,
510 	.munmap = snd_pcm_direct_munmap,
511 	.query_chmaps = snd_pcm_direct_query_chmaps,
512 	.get_chmap = snd_pcm_direct_get_chmap,
513 	.set_chmap = snd_pcm_direct_set_chmap,
514 };
515 
516 static const snd_pcm_fast_ops_t snd_pcm_dsnoop_fast_ops = {
517 	.status = snd_pcm_dsnoop_status,
518 	.state = snd_pcm_dsnoop_state,
519 	.hwsync = snd_pcm_dsnoop_hwsync,
520 	.delay = snd_pcm_dsnoop_delay,
521 	.prepare = snd_pcm_direct_prepare,
522 	.reset = snd_pcm_dsnoop_reset,
523 	.start = snd_pcm_dsnoop_start,
524 	.drop = snd_pcm_dsnoop_drop,
525 	.drain = snd_pcm_dsnoop_drain,
526 	.pause = snd_pcm_dsnoop_pause,
527 	.rewindable = snd_pcm_dsnoop_rewindable,
528 	.rewind = snd_pcm_dsnoop_rewind,
529 	.forwardable = snd_pcm_dsnoop_forwardable,
530 	.forward = snd_pcm_dsnoop_forward,
531 	.resume = snd_pcm_direct_resume,
532 	.link = NULL,
533 	.link_slaves = NULL,
534 	.unlink = NULL,
535 	.writei = snd_pcm_dsnoop_writei,
536 	.writen = snd_pcm_dsnoop_writen,
537 	.readi = snd_pcm_mmap_readi,
538 	.readn = snd_pcm_mmap_readn,
539 	.avail_update = snd_pcm_dsnoop_avail_update,
540 	.mmap_commit = snd_pcm_dsnoop_mmap_commit,
541 	.htimestamp = snd_pcm_dsnoop_htimestamp,
542 	.poll_descriptors = snd_pcm_direct_poll_descriptors,
543 	.poll_descriptors_count = NULL,
544 	.poll_revents = snd_pcm_direct_poll_revents,
545 };
546 
547 /**
548  * \brief Creates a new dsnoop PCM
549  * \param pcmp Returns created PCM handle
550  * \param name Name of PCM
551  * \param opts Direct PCM configurations
552  * \param params Parameters for slave
553  * \param root Configuration root
554  * \param sconf Slave configuration
555  * \param stream PCM Direction (stream)
556  * \param mode PCM Mode
557  * \retval zero on success otherwise a negative error code
558  * \warning Using of this function might be dangerous in the sense
559  *          of compatibility reasons. The prototype might be freely
560  *          changed in future.
561  */
snd_pcm_dsnoop_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)562 int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
563 			struct snd_pcm_direct_open_conf *opts,
564 			struct slave_params *params,
565 			snd_config_t *root, snd_config_t *sconf,
566 			snd_pcm_stream_t stream, int mode)
567 {
568 	snd_pcm_t *pcm, *spcm = NULL;
569 	snd_pcm_direct_t *dsnoop;
570 	int ret, first_instance;
571 
572 	assert(pcmp);
573 
574 	if (stream != SND_PCM_STREAM_CAPTURE) {
575 		SNDERR("The dsnoop plugin supports only capture stream");
576 		return -EINVAL;
577 	}
578 
579 	ret = _snd_pcm_direct_new(&pcm, &dsnoop, SND_PCM_TYPE_DSNOOP, name, opts, params, stream, mode);
580 	if (ret < 0)
581 		return ret;
582 	first_instance = ret;
583 
584 	pcm->ops = &snd_pcm_dsnoop_ops;
585 	pcm->fast_ops = &snd_pcm_dsnoop_fast_ops;
586 	pcm->private_data = dsnoop;
587 	dsnoop->state = SND_PCM_STATE_OPEN;
588 	dsnoop->slowptr = opts->slowptr;
589 	dsnoop->max_periods = opts->max_periods;
590 	dsnoop->var_periodsize = opts->var_periodsize;
591 	dsnoop->sync_ptr = snd_pcm_dsnoop_sync_ptr;
592 	dsnoop->hw_ptr_alignment = opts->hw_ptr_alignment;
593 
594  retry:
595 	if (first_instance) {
596 		/* recursion is already checked in
597 		   snd_pcm_direct_get_slave_ipc_offset() */
598 		ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
599 					 mode | SND_PCM_NONBLOCK, NULL);
600 		if (ret < 0) {
601 			SNDERR("unable to open slave");
602 			goto _err;
603 		}
604 
605 		if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
606 			SNDERR("dsnoop plugin can be only connected to hw plugin");
607 			goto _err;
608 		}
609 
610 		ret = snd_pcm_direct_initialize_slave(dsnoop, spcm, params);
611 		if (ret < 0) {
612 			SNDERR("unable to initialize slave");
613 			goto _err;
614 		}
615 
616 		dsnoop->spcm = spcm;
617 
618 		if (dsnoop->shmptr->use_server) {
619 			ret = snd_pcm_direct_server_create(dsnoop);
620 			if (ret < 0) {
621 				SNDERR("unable to create server");
622 				goto _err;
623 			}
624 		}
625 
626 		dsnoop->shmptr->type = spcm->type;
627 	} else {
628 		if (dsnoop->shmptr->use_server) {
629 			/* up semaphore to avoid deadlock */
630 			snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
631 			ret = snd_pcm_direct_client_connect(dsnoop);
632 			if (ret < 0) {
633 				SNDERR("unable to connect client");
634 				goto _err_nosem;
635 			}
636 
637 			snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
638 
639 			ret = snd_pcm_direct_open_secondary_client(&spcm, dsnoop, "dsnoop_client");
640 			if (ret < 0)
641 				goto _err;
642 		} else {
643 
644 			ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
645 						 mode | SND_PCM_NONBLOCK |
646 						 SND_PCM_APPEND,
647 						 NULL);
648 			if (ret < 0) {
649 				/* all other streams have been closed;
650 				 * retry as the first instance
651 				 */
652 				if (ret == -EBADFD) {
653 					first_instance = 1;
654 					goto retry;
655 				}
656 				SNDERR("unable to open slave");
657 				goto _err;
658 			}
659 			if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
660 				SNDERR("dsnoop plugin can be only connected to hw plugin");
661 				ret = -EINVAL;
662 				goto _err;
663 			}
664 
665 			ret = snd_pcm_direct_initialize_secondary_slave(dsnoop, spcm, params);
666 			if (ret < 0) {
667 				SNDERR("unable to initialize slave");
668 				goto _err;
669 			}
670 		}
671 
672 		dsnoop->spcm = spcm;
673 	}
674 
675 	ret = snd_pcm_direct_initialize_poll_fd(dsnoop);
676 	if (ret < 0) {
677 		SNDERR("unable to initialize poll_fd");
678 		goto _err;
679 	}
680 
681 	pcm->poll_fd = dsnoop->poll_fd;
682 	pcm->poll_events = POLLIN;	/* it's different than other plugins */
683 	pcm->tstamp_type = spcm->tstamp_type;
684 	pcm->mmap_rw = 1;
685 	snd_pcm_set_hw_ptr(pcm, &dsnoop->hw_ptr, -1, 0);
686 	snd_pcm_set_appl_ptr(pcm, &dsnoop->appl_ptr, -1, 0);
687 
688 	if (dsnoop->channels == UINT_MAX)
689 		dsnoop->channels = dsnoop->shmptr->s.channels;
690 
691 	snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
692 
693 	*pcmp = pcm;
694 	return 0;
695 
696  _err:
697  	if (dsnoop->timer)
698 		snd_timer_close(dsnoop->timer);
699 	if (dsnoop->server)
700 		snd_pcm_direct_server_discard(dsnoop);
701 	if (dsnoop->client)
702 		snd_pcm_direct_client_discard(dsnoop);
703 	if (spcm)
704 		snd_pcm_close(spcm);
705 	if ((dsnoop->shmid >= 0) && (snd_pcm_direct_shm_discard(dsnoop))) {
706 		if (snd_pcm_direct_semaphore_discard(dsnoop))
707 			snd_pcm_direct_semaphore_final(dsnoop, DIRECT_IPC_SEM_CLIENT);
708 	} else
709 		snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
710 
711  _err_nosem:
712 	free(dsnoop->bindings);
713 	free(dsnoop);
714 	snd_pcm_free(pcm);
715 	return ret;
716 }
717 
718 /*! \page pcm_plugins
719 
720 \section pcm_plugins_dsnoop Plugin: dsnoop
721 
722 This plugin splits one capture stream to more.
723 It works the reverse way of \ref pcm_plugins_dmix "dmix plugin",
724 reading the shared capture buffer from many clients concurrently.
725 The meaning of parameters below are almost identical with
726 dmix plugin.
727 
728 \code
729 pcm.name {
730 	type dsnoop		# Direct snoop
731 	ipc_key INT		# unique IPC key
732 	ipc_key_add_uid BOOL	# add current uid to unique IPC key
733 	ipc_perm INT		# IPC permissions (octal, default 0600)
734 	hw_ptr_alignment STR	# Slave application and hw pointer alignment type
735 		# STR can be one of the below strings :
736 		# no
737 		# roundup
738 		# rounddown
739 		# auto (default)
740 	tstamp_type STR		# timestamp type
741 				# STR can be one of the below strings :
742 				# default, gettimeofday, monotonic, monotonic_raw
743 	slave STR
744 	# or
745 	slave {			# Slave definition
746 		pcm STR		# slave PCM name
747 		# or
748 		pcm { }		# slave PCM definition
749 		format STR	# format definition
750 		rate INT	# rate definition
751 		channels INT
752 		period_time INT	# in usec
753 		# or
754 		period_size INT	# in frames
755 		buffer_time INT	# in usec
756 		# or
757 		buffer_size INT # in frames
758 		periods INT	# when buffer_size or buffer_time is not specified
759 	}
760 	bindings {		# note: this is client independent!!!
761 		N INT		# maps slave channel to client channel N
762 	}
763 	slowptr BOOL		# slow but more precise pointer updates
764 }
765 \endcode
766 
767 <code>hw_ptr_alignment</code> specifies slave application and hw
768 pointer alignment type. By default hw_ptr_alignment is auto. Below are
769 the possible configurations:
770 - no: minimal latency with minimal frames dropped at startup. But
771   wakeup of application (return from snd_pcm_wait() or poll()) can
772   take up to 2 * period.
773 - roundup: It is guaranteed that all frames will be played at
774   startup. But the latency will increase upto period-1 frames.
775 - rounddown: It is guaranteed that a wakeup will happen for each
776   period and frames can be written from application. But on startup
777   upto period-1 frames will be dropped.
778 - auto: Selects the best approach depending on the used period and
779   buffer size.
780   If the application buffer size is < 2 * application period,
781   "roundup" will be selected to avoid over runs. If the slave_period
782   is < 10ms we could expect that there are low latency
783   requirements. Therefore "rounddown" will be chosen to avoid long
784   wakeup times. Else "no" will be chosen.
785 
786 \subsection pcm_plugins_dsnoop_funcref Function reference
787 
788 <UL>
789   <LI>snd_pcm_dsnoop_open()
790   <LI>_snd_pcm_dsnoop_open()
791 </UL>
792 
793 */
794 
795 /**
796  * \brief Creates a new dsnoop PCM
797  * \param pcmp Returns created PCM handle
798  * \param name Name of PCM
799  * \param root Root configuration node
800  * \param conf Configuration node with dsnoop PCM description
801  * \param stream PCM Stream
802  * \param mode PCM Mode
803  * \warning Using of this function might be dangerous in the sense
804  *          of compatibility reasons. The prototype might be freely
805  *          changed in future.
806  */
_snd_pcm_dsnoop_open(snd_pcm_t ** pcmp,const char * name,snd_config_t * root,snd_config_t * conf,snd_pcm_stream_t stream,int mode)807 int _snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
808 		       snd_config_t *root, snd_config_t *conf,
809 		       snd_pcm_stream_t stream, int mode)
810 {
811 	snd_config_t *sconf;
812 	struct slave_params params;
813 	struct snd_pcm_direct_open_conf dopen;
814 	int bsize, psize;
815 	int err;
816 
817 	err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
818 	if (err < 0)
819 		return err;
820 
821 	/* the default settings, it might be invalid for some hardware */
822 	params.format = SND_PCM_FORMAT_S16;
823 	params.rate = 48000;
824 	params.channels = 2;
825 	params.period_time = -1;
826 	params.buffer_time = -1;
827 	bsize = psize = -1;
828 	params.periods = 3;
829 	err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8,
830 				 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &params.format,
831 				 SND_PCM_HW_PARAM_RATE, 0, &params.rate,
832 				 SND_PCM_HW_PARAM_CHANNELS, 0, &params.channels,
833 				 SND_PCM_HW_PARAM_PERIOD_TIME, 0, &params.period_time,
834 				 SND_PCM_HW_PARAM_BUFFER_TIME, 0, &params.buffer_time,
835 				 SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
836 				 SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
837 				 SND_PCM_HW_PARAM_PERIODS, 0, &params.periods);
838 	if (err < 0)
839 		return err;
840 
841 	/* set a reasonable default */
842 	if (psize == -1 && params.period_time == -1)
843 		params.period_time = 125000;    /* 0.125 seconds */
844 
845 	if (params.format == -2)
846 		params.format = SND_PCM_FORMAT_UNKNOWN;
847 
848 	params.period_size = psize;
849 	params.buffer_size = bsize;
850 
851 	err = snd_pcm_dsnoop_open(pcmp, name, &dopen, &params,
852 				  root, sconf, stream, mode);
853 	snd_config_delete(sconf);
854 	return err;
855 }
856 #ifndef DOC_HIDDEN
857 SND_DLSYM_BUILD_VERSION(_snd_pcm_dsnoop_open, SND_PCM_DLSYM_VERSION);
858 #endif
859