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