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