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