1d5ac70f0Sopenharmony_ci/*
2d5ac70f0Sopenharmony_ci *  PCM - Direct Stream Mixing
3d5ac70f0Sopenharmony_ci *  Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
4d5ac70f0Sopenharmony_ci *
5d5ac70f0Sopenharmony_ci *
6d5ac70f0Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
7d5ac70f0Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as
8d5ac70f0Sopenharmony_ci *   published by the Free Software Foundation; either version 2.1 of
9d5ac70f0Sopenharmony_ci *   the License, or (at your option) any later version.
10d5ac70f0Sopenharmony_ci *
11d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
12d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14d5ac70f0Sopenharmony_ci *   GNU Lesser General Public License for more details.
15d5ac70f0Sopenharmony_ci *
16d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public
17d5ac70f0Sopenharmony_ci *   License along with this library; if not, write to the Free Software
18d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19d5ac70f0Sopenharmony_ci *
20d5ac70f0Sopenharmony_ci */
21d5ac70f0Sopenharmony_ci
22d5ac70f0Sopenharmony_ci#include "pcm_local.h"
23d5ac70f0Sopenharmony_ci#include <stdio.h>
24d5ac70f0Sopenharmony_ci#include <stdlib.h>
25d5ac70f0Sopenharmony_ci#include <stddef.h>
26d5ac70f0Sopenharmony_ci#include <unistd.h>
27d5ac70f0Sopenharmony_ci#include <signal.h>
28d5ac70f0Sopenharmony_ci#include <string.h>
29d5ac70f0Sopenharmony_ci#include <fcntl.h>
30d5ac70f0Sopenharmony_ci#include <ctype.h>
31d5ac70f0Sopenharmony_ci#include <grp.h>
32d5ac70f0Sopenharmony_ci#include <sys/ioctl.h>
33d5ac70f0Sopenharmony_ci#include <sys/mman.h>
34d5ac70f0Sopenharmony_ci#include <poll.h>
35d5ac70f0Sopenharmony_ci#include <sys/shm.h>
36d5ac70f0Sopenharmony_ci#include <sys/sem.h>
37d5ac70f0Sopenharmony_ci#include <sys/wait.h>
38d5ac70f0Sopenharmony_ci#include <sys/socket.h>
39d5ac70f0Sopenharmony_ci#include <sys/stat.h>
40d5ac70f0Sopenharmony_ci#include <sys/un.h>
41d5ac70f0Sopenharmony_ci#include <sys/mman.h>
42d5ac70f0Sopenharmony_ci#include "pcm_direct.h"
43d5ac70f0Sopenharmony_ci
44d5ac70f0Sopenharmony_ci/*
45d5ac70f0Sopenharmony_ci *
46d5ac70f0Sopenharmony_ci */
47d5ac70f0Sopenharmony_ci
48d5ac70f0Sopenharmony_ci#if !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__)
49d5ac70f0Sopenharmony_ciunion semun {
50d5ac70f0Sopenharmony_ci	int              val;    /* Value for SETVAL */
51d5ac70f0Sopenharmony_ci	struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
52d5ac70f0Sopenharmony_ci	unsigned short  *array;  /* Array for GETALL, SETALL */
53d5ac70f0Sopenharmony_ci#if defined(__linux__)
54d5ac70f0Sopenharmony_ci	struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux specific) */
55d5ac70f0Sopenharmony_ci#endif
56d5ac70f0Sopenharmony_ci};
57d5ac70f0Sopenharmony_ci#endif
58d5ac70f0Sopenharmony_ci
59d5ac70f0Sopenharmony_ci/*
60d5ac70f0Sopenharmony_ci * FIXME:
61d5ac70f0Sopenharmony_ci *  add possibility to use futexes here
62d5ac70f0Sopenharmony_ci */
63d5ac70f0Sopenharmony_ci
64d5ac70f0Sopenharmony_ciint snd_pcm_direct_semaphore_create_or_connect(snd_pcm_direct_t *dmix)
65d5ac70f0Sopenharmony_ci{
66d5ac70f0Sopenharmony_ci	union semun s;
67d5ac70f0Sopenharmony_ci	struct semid_ds buf;
68d5ac70f0Sopenharmony_ci	int i;
69d5ac70f0Sopenharmony_ci
70d5ac70f0Sopenharmony_ci	dmix->semid = semget(dmix->ipc_key, DIRECT_IPC_SEMS,
71d5ac70f0Sopenharmony_ci			     IPC_CREAT | dmix->ipc_perm);
72d5ac70f0Sopenharmony_ci	if (dmix->semid < 0)
73d5ac70f0Sopenharmony_ci		return -errno;
74d5ac70f0Sopenharmony_ci	if (dmix->ipc_gid < 0)
75d5ac70f0Sopenharmony_ci		return 0;
76d5ac70f0Sopenharmony_ci	for (i = 0; i < DIRECT_IPC_SEMS; i++) {
77d5ac70f0Sopenharmony_ci		s.buf = &buf;
78d5ac70f0Sopenharmony_ci		if (semctl(dmix->semid, i, IPC_STAT, s) < 0) {
79d5ac70f0Sopenharmony_ci			int err = -errno;
80d5ac70f0Sopenharmony_ci			snd_pcm_direct_semaphore_discard(dmix);
81d5ac70f0Sopenharmony_ci			return err;
82d5ac70f0Sopenharmony_ci		}
83d5ac70f0Sopenharmony_ci		buf.sem_perm.gid = dmix->ipc_gid;
84d5ac70f0Sopenharmony_ci		s.buf = &buf;
85d5ac70f0Sopenharmony_ci		semctl(dmix->semid, i, IPC_SET, s);
86d5ac70f0Sopenharmony_ci	}
87d5ac70f0Sopenharmony_ci	return 0;
88d5ac70f0Sopenharmony_ci}
89d5ac70f0Sopenharmony_ci
90d5ac70f0Sopenharmony_cistatic unsigned int snd_pcm_direct_magic(snd_pcm_direct_t *dmix)
91d5ac70f0Sopenharmony_ci{
92d5ac70f0Sopenharmony_ci	if (!dmix->direct_memory_access)
93d5ac70f0Sopenharmony_ci		return 0xa15ad300 + sizeof(snd_pcm_direct_share_t);
94d5ac70f0Sopenharmony_ci	else
95d5ac70f0Sopenharmony_ci		return 0xb15ad300 + sizeof(snd_pcm_direct_share_t);
96d5ac70f0Sopenharmony_ci}
97d5ac70f0Sopenharmony_ci
98d5ac70f0Sopenharmony_ci/*
99d5ac70f0Sopenharmony_ci *  global shared memory area
100d5ac70f0Sopenharmony_ci */
101d5ac70f0Sopenharmony_ci
102d5ac70f0Sopenharmony_ciint snd_pcm_direct_shm_create_or_connect(snd_pcm_direct_t *dmix)
103d5ac70f0Sopenharmony_ci{
104d5ac70f0Sopenharmony_ci	struct shmid_ds buf;
105d5ac70f0Sopenharmony_ci	int tmpid, err, first_instance = 0;
106d5ac70f0Sopenharmony_ci
107d5ac70f0Sopenharmony_ciretryget:
108d5ac70f0Sopenharmony_ci	dmix->shmid = shmget(dmix->ipc_key, sizeof(snd_pcm_direct_share_t),
109d5ac70f0Sopenharmony_ci			     dmix->ipc_perm);
110d5ac70f0Sopenharmony_ci	if (dmix->shmid < 0 && errno == ENOENT) {
111d5ac70f0Sopenharmony_ci		if ((dmix->shmid = shmget(dmix->ipc_key, sizeof(snd_pcm_direct_share_t),
112d5ac70f0Sopenharmony_ci					     IPC_CREAT | IPC_EXCL | dmix->ipc_perm)) != -1)
113d5ac70f0Sopenharmony_ci			first_instance = 1;
114d5ac70f0Sopenharmony_ci		else if (errno == EEXIST)
115d5ac70f0Sopenharmony_ci			goto retryget;
116d5ac70f0Sopenharmony_ci	}
117d5ac70f0Sopenharmony_ci	err = -errno;
118d5ac70f0Sopenharmony_ci	if (dmix->shmid < 0) {
119d5ac70f0Sopenharmony_ci		if (errno == EINVAL)
120d5ac70f0Sopenharmony_ci		if ((tmpid = shmget(dmix->ipc_key, 0, dmix->ipc_perm)) != -1)
121d5ac70f0Sopenharmony_ci		if (!shmctl(tmpid, IPC_STAT, &buf))
122d5ac70f0Sopenharmony_ci		if (!buf.shm_nattch)
123d5ac70f0Sopenharmony_ci	    	/* no users so destroy the segment */
124d5ac70f0Sopenharmony_ci		if (!shmctl(tmpid, IPC_RMID, NULL))
125d5ac70f0Sopenharmony_ci		    goto retryget;
126d5ac70f0Sopenharmony_ci		return err;
127d5ac70f0Sopenharmony_ci	}
128d5ac70f0Sopenharmony_ci	dmix->shmptr = shmat(dmix->shmid, 0, 0);
129d5ac70f0Sopenharmony_ci	if (dmix->shmptr == (void *) -1) {
130d5ac70f0Sopenharmony_ci		err = -errno;
131d5ac70f0Sopenharmony_ci		snd_pcm_direct_shm_discard(dmix);
132d5ac70f0Sopenharmony_ci		return err;
133d5ac70f0Sopenharmony_ci	}
134d5ac70f0Sopenharmony_ci	mlock(dmix->shmptr, sizeof(snd_pcm_direct_share_t));
135d5ac70f0Sopenharmony_ci	if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) {
136d5ac70f0Sopenharmony_ci		err = -errno;
137d5ac70f0Sopenharmony_ci		snd_pcm_direct_shm_discard(dmix);
138d5ac70f0Sopenharmony_ci		return err;
139d5ac70f0Sopenharmony_ci	}
140d5ac70f0Sopenharmony_ci	if (first_instance) {	/* we're the first user, clear the segment */
141d5ac70f0Sopenharmony_ci		memset(dmix->shmptr, 0, sizeof(snd_pcm_direct_share_t));
142d5ac70f0Sopenharmony_ci		if (dmix->ipc_gid >= 0) {
143d5ac70f0Sopenharmony_ci			buf.shm_perm.gid = dmix->ipc_gid;
144d5ac70f0Sopenharmony_ci			shmctl(dmix->shmid, IPC_SET, &buf);
145d5ac70f0Sopenharmony_ci		}
146d5ac70f0Sopenharmony_ci		dmix->shmptr->magic = snd_pcm_direct_magic(dmix);
147d5ac70f0Sopenharmony_ci		return 1;
148d5ac70f0Sopenharmony_ci	} else {
149d5ac70f0Sopenharmony_ci		if (dmix->shmptr->magic != snd_pcm_direct_magic(dmix)) {
150d5ac70f0Sopenharmony_ci			snd_pcm_direct_shm_discard(dmix);
151d5ac70f0Sopenharmony_ci			return -EINVAL;
152d5ac70f0Sopenharmony_ci		}
153d5ac70f0Sopenharmony_ci	}
154d5ac70f0Sopenharmony_ci	return 0;
155d5ac70f0Sopenharmony_ci}
156d5ac70f0Sopenharmony_ci
157d5ac70f0Sopenharmony_ci/* discard shared memory */
158d5ac70f0Sopenharmony_ci/*
159d5ac70f0Sopenharmony_ci * Define snd_* functions to be used in server.
160d5ac70f0Sopenharmony_ci * Since objects referred in a plugin can be released dynamically, a forked
161d5ac70f0Sopenharmony_ci * server should have statically linked functions.
162d5ac70f0Sopenharmony_ci * (e.g. Novell bugzilla #105772)
163d5ac70f0Sopenharmony_ci */
164d5ac70f0Sopenharmony_cistatic int _snd_pcm_direct_shm_discard(snd_pcm_direct_t *dmix)
165d5ac70f0Sopenharmony_ci{
166d5ac70f0Sopenharmony_ci	struct shmid_ds buf;
167d5ac70f0Sopenharmony_ci	int ret = 0;
168d5ac70f0Sopenharmony_ci
169d5ac70f0Sopenharmony_ci	if (dmix->shmid < 0)
170d5ac70f0Sopenharmony_ci		return -EINVAL;
171d5ac70f0Sopenharmony_ci	if (dmix->shmptr != (void *) -1 && shmdt(dmix->shmptr) < 0)
172d5ac70f0Sopenharmony_ci		return -errno;
173d5ac70f0Sopenharmony_ci	dmix->shmptr = (void *) -1;
174d5ac70f0Sopenharmony_ci	if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0)
175d5ac70f0Sopenharmony_ci		return -errno;
176d5ac70f0Sopenharmony_ci	if (buf.shm_nattch == 0) {	/* we're the last user, destroy the segment */
177d5ac70f0Sopenharmony_ci		if (shmctl(dmix->shmid, IPC_RMID, NULL) < 0)
178d5ac70f0Sopenharmony_ci			return -errno;
179d5ac70f0Sopenharmony_ci		ret = 1;
180d5ac70f0Sopenharmony_ci	}
181d5ac70f0Sopenharmony_ci	dmix->shmid = -1;
182d5ac70f0Sopenharmony_ci	return ret;
183d5ac70f0Sopenharmony_ci}
184d5ac70f0Sopenharmony_ci
185d5ac70f0Sopenharmony_ci/* ... and an exported version */
186d5ac70f0Sopenharmony_ciint snd_pcm_direct_shm_discard(snd_pcm_direct_t *dmix)
187d5ac70f0Sopenharmony_ci{
188d5ac70f0Sopenharmony_ci	return _snd_pcm_direct_shm_discard(dmix);
189d5ac70f0Sopenharmony_ci}
190d5ac70f0Sopenharmony_ci
191d5ac70f0Sopenharmony_ci/*
192d5ac70f0Sopenharmony_ci *  server side
193d5ac70f0Sopenharmony_ci */
194d5ac70f0Sopenharmony_ci
195d5ac70f0Sopenharmony_cistatic int get_tmp_name(char *filename, size_t size)
196d5ac70f0Sopenharmony_ci{
197d5ac70f0Sopenharmony_ci	struct timeval tv;
198d5ac70f0Sopenharmony_ci
199d5ac70f0Sopenharmony_ci	gettimeofday(&tv, NULL);
200d5ac70f0Sopenharmony_ci	snprintf(filename, size, TMPDIR "/alsa-dmix-%i-%li-%li", (int)getpid(), (long)tv.tv_sec, (long)tv.tv_usec);
201d5ac70f0Sopenharmony_ci	filename[size-1] = '\0';
202d5ac70f0Sopenharmony_ci	return 0;
203d5ac70f0Sopenharmony_ci}
204d5ac70f0Sopenharmony_ci
205d5ac70f0Sopenharmony_cistatic int make_local_socket(const char *filename, int server, mode_t ipc_perm, int ipc_gid)
206d5ac70f0Sopenharmony_ci{
207d5ac70f0Sopenharmony_ci	size_t l = strlen(filename);
208d5ac70f0Sopenharmony_ci	size_t size = offsetof(struct sockaddr_un, sun_path) + l;
209d5ac70f0Sopenharmony_ci	struct sockaddr_un *addr = alloca(size);
210d5ac70f0Sopenharmony_ci	int sock;
211d5ac70f0Sopenharmony_ci
212d5ac70f0Sopenharmony_ci	sock = socket(PF_LOCAL, SOCK_STREAM, 0);
213d5ac70f0Sopenharmony_ci	if (sock < 0) {
214d5ac70f0Sopenharmony_ci		int result = -errno;
215d5ac70f0Sopenharmony_ci		SYSERR("socket failed");
216d5ac70f0Sopenharmony_ci		return result;
217d5ac70f0Sopenharmony_ci	}
218d5ac70f0Sopenharmony_ci
219d5ac70f0Sopenharmony_ci	if (server)
220d5ac70f0Sopenharmony_ci		unlink(filename);
221d5ac70f0Sopenharmony_ci	memset(addr, 0, size); /* make valgrind happy */
222d5ac70f0Sopenharmony_ci	addr->sun_family = AF_LOCAL;
223d5ac70f0Sopenharmony_ci	memcpy(addr->sun_path, filename, l);
224d5ac70f0Sopenharmony_ci
225d5ac70f0Sopenharmony_ci	if (server) {
226d5ac70f0Sopenharmony_ci		if (bind(sock, (struct sockaddr *) addr, size) < 0) {
227d5ac70f0Sopenharmony_ci			int result = -errno;
228d5ac70f0Sopenharmony_ci			SYSERR("bind failed: %s", filename);
229d5ac70f0Sopenharmony_ci			close(sock);
230d5ac70f0Sopenharmony_ci			return result;
231d5ac70f0Sopenharmony_ci		} else {
232d5ac70f0Sopenharmony_ci			if (chmod(filename, ipc_perm) < 0) {
233d5ac70f0Sopenharmony_ci				int result = -errno;
234d5ac70f0Sopenharmony_ci				SYSERR("chmod failed: %s", filename);
235d5ac70f0Sopenharmony_ci				close(sock);
236d5ac70f0Sopenharmony_ci				unlink(filename);
237d5ac70f0Sopenharmony_ci				return result;
238d5ac70f0Sopenharmony_ci			}
239d5ac70f0Sopenharmony_ci			if (chown(filename, -1, ipc_gid) < 0) {
240d5ac70f0Sopenharmony_ci#if 0 /* it's not fatal */
241d5ac70f0Sopenharmony_ci				int result = -errno;
242d5ac70f0Sopenharmony_ci				SYSERR("chown failed: %s", filename);
243d5ac70f0Sopenharmony_ci				close(sock);
244d5ac70f0Sopenharmony_ci				unlink(filename);
245d5ac70f0Sopenharmony_ci				return result;
246d5ac70f0Sopenharmony_ci#endif
247d5ac70f0Sopenharmony_ci			}
248d5ac70f0Sopenharmony_ci		}
249d5ac70f0Sopenharmony_ci	} else {
250d5ac70f0Sopenharmony_ci		if (connect(sock, (struct sockaddr *) addr, size) < 0) {
251d5ac70f0Sopenharmony_ci			int result = -errno;
252d5ac70f0Sopenharmony_ci			SYSERR("connect failed: %s", filename);
253d5ac70f0Sopenharmony_ci			close(sock);
254d5ac70f0Sopenharmony_ci			return result;
255d5ac70f0Sopenharmony_ci		}
256d5ac70f0Sopenharmony_ci	}
257d5ac70f0Sopenharmony_ci	return sock;
258d5ac70f0Sopenharmony_ci}
259d5ac70f0Sopenharmony_ci
260d5ac70f0Sopenharmony_ci#if 0
261d5ac70f0Sopenharmony_ci#define SERVER_JOB_DEBUG
262d5ac70f0Sopenharmony_ci#define server_printf(fmt, args...) printf(fmt, ##args)
263d5ac70f0Sopenharmony_ci#else
264d5ac70f0Sopenharmony_ci#undef SERVER_JOB_DEBUG
265d5ac70f0Sopenharmony_ci#define server_printf(fmt, args...) /* nothing */
266d5ac70f0Sopenharmony_ci#endif
267d5ac70f0Sopenharmony_ci
268d5ac70f0Sopenharmony_cistatic snd_pcm_direct_t *server_job_dmix;
269d5ac70f0Sopenharmony_ci
270d5ac70f0Sopenharmony_cistatic void server_cleanup(snd_pcm_direct_t *dmix)
271d5ac70f0Sopenharmony_ci{
272d5ac70f0Sopenharmony_ci	close(dmix->server_fd);
273d5ac70f0Sopenharmony_ci	close(dmix->hw_fd);
274d5ac70f0Sopenharmony_ci	if (dmix->server_free)
275d5ac70f0Sopenharmony_ci		dmix->server_free(dmix);
276d5ac70f0Sopenharmony_ci	unlink(dmix->shmptr->socket_name);
277d5ac70f0Sopenharmony_ci	_snd_pcm_direct_shm_discard(dmix);
278d5ac70f0Sopenharmony_ci	snd_pcm_direct_semaphore_discard(dmix);
279d5ac70f0Sopenharmony_ci}
280d5ac70f0Sopenharmony_ci
281d5ac70f0Sopenharmony_cistatic void server_job_signal(int sig ATTRIBUTE_UNUSED)
282d5ac70f0Sopenharmony_ci{
283d5ac70f0Sopenharmony_ci	snd_pcm_direct_semaphore_down(server_job_dmix, DIRECT_IPC_SEM_CLIENT);
284d5ac70f0Sopenharmony_ci	server_cleanup(server_job_dmix);
285d5ac70f0Sopenharmony_ci	server_printf("DIRECT SERVER EXIT - SIGNAL\n");
286d5ac70f0Sopenharmony_ci	_exit(EXIT_SUCCESS);
287d5ac70f0Sopenharmony_ci}
288d5ac70f0Sopenharmony_ci
289d5ac70f0Sopenharmony_ci/* This is a copy from ../socket.c, provided here only for a server job
290d5ac70f0Sopenharmony_ci * (see the comment above)
291d5ac70f0Sopenharmony_ci */
292d5ac70f0Sopenharmony_cistatic int _snd_send_fd(int sock, void *data, size_t len, int fd)
293d5ac70f0Sopenharmony_ci{
294d5ac70f0Sopenharmony_ci	int ret;
295d5ac70f0Sopenharmony_ci	size_t cmsg_len = CMSG_LEN(sizeof(int));
296d5ac70f0Sopenharmony_ci	struct cmsghdr *cmsg = alloca(cmsg_len);
297d5ac70f0Sopenharmony_ci	int *fds = (int *) CMSG_DATA(cmsg);
298d5ac70f0Sopenharmony_ci	struct msghdr msghdr;
299d5ac70f0Sopenharmony_ci	struct iovec vec;
300d5ac70f0Sopenharmony_ci
301d5ac70f0Sopenharmony_ci	vec.iov_base = (void *)&data;
302d5ac70f0Sopenharmony_ci	vec.iov_len = len;
303d5ac70f0Sopenharmony_ci
304d5ac70f0Sopenharmony_ci	cmsg->cmsg_len = cmsg_len;
305d5ac70f0Sopenharmony_ci	cmsg->cmsg_level = SOL_SOCKET;
306d5ac70f0Sopenharmony_ci	cmsg->cmsg_type = SCM_RIGHTS;
307d5ac70f0Sopenharmony_ci	*fds = fd;
308d5ac70f0Sopenharmony_ci
309d5ac70f0Sopenharmony_ci	msghdr.msg_name = NULL;
310d5ac70f0Sopenharmony_ci	msghdr.msg_namelen = 0;
311d5ac70f0Sopenharmony_ci	msghdr.msg_iov = &vec;
312d5ac70f0Sopenharmony_ci 	msghdr.msg_iovlen = 1;
313d5ac70f0Sopenharmony_ci	msghdr.msg_control = cmsg;
314d5ac70f0Sopenharmony_ci	msghdr.msg_controllen = cmsg_len;
315d5ac70f0Sopenharmony_ci	msghdr.msg_flags = 0;
316d5ac70f0Sopenharmony_ci
317d5ac70f0Sopenharmony_ci	ret = sendmsg(sock, &msghdr, 0 );
318d5ac70f0Sopenharmony_ci	if (ret < 0)
319d5ac70f0Sopenharmony_ci		return -errno;
320d5ac70f0Sopenharmony_ci	return ret;
321d5ac70f0Sopenharmony_ci}
322d5ac70f0Sopenharmony_ci
323d5ac70f0Sopenharmony_cistatic void server_job(snd_pcm_direct_t *dmix)
324d5ac70f0Sopenharmony_ci{
325d5ac70f0Sopenharmony_ci	int ret, sck, i;
326d5ac70f0Sopenharmony_ci	int max = 128, current = 0;
327d5ac70f0Sopenharmony_ci	struct pollfd pfds[max + 1];
328d5ac70f0Sopenharmony_ci
329d5ac70f0Sopenharmony_ci	server_job_dmix = dmix;
330d5ac70f0Sopenharmony_ci	/* don't allow to be killed */
331d5ac70f0Sopenharmony_ci	signal(SIGHUP, server_job_signal);
332d5ac70f0Sopenharmony_ci	signal(SIGQUIT, server_job_signal);
333d5ac70f0Sopenharmony_ci	signal(SIGTERM, server_job_signal);
334d5ac70f0Sopenharmony_ci	signal(SIGKILL, server_job_signal);
335d5ac70f0Sopenharmony_ci	/* close all files to free resources */
336d5ac70f0Sopenharmony_ci	i = sysconf(_SC_OPEN_MAX);
337d5ac70f0Sopenharmony_ci#ifdef SERVER_JOB_DEBUG
338d5ac70f0Sopenharmony_ci	while (--i >= 3) {
339d5ac70f0Sopenharmony_ci#else
340d5ac70f0Sopenharmony_ci	while (--i >= 0) {
341d5ac70f0Sopenharmony_ci#endif
342d5ac70f0Sopenharmony_ci		if (i != dmix->server_fd && i != dmix->hw_fd)
343d5ac70f0Sopenharmony_ci			close(i);
344d5ac70f0Sopenharmony_ci	}
345d5ac70f0Sopenharmony_ci
346d5ac70f0Sopenharmony_ci	/* detach from parent */
347d5ac70f0Sopenharmony_ci	setsid();
348d5ac70f0Sopenharmony_ci
349d5ac70f0Sopenharmony_ci	pfds[0].fd = dmix->server_fd;
350d5ac70f0Sopenharmony_ci	pfds[0].events = POLLIN | POLLERR | POLLHUP;
351d5ac70f0Sopenharmony_ci
352d5ac70f0Sopenharmony_ci	server_printf("DIRECT SERVER STARTED\n");
353d5ac70f0Sopenharmony_ci	while (1) {
354d5ac70f0Sopenharmony_ci		ret = poll(pfds, current + 1, 500);
355d5ac70f0Sopenharmony_ci		server_printf("DIRECT SERVER: poll ret = %i, revents[0] = 0x%x, errno = %i\n", ret, pfds[0].revents, errno);
356d5ac70f0Sopenharmony_ci		if (ret < 0) {
357d5ac70f0Sopenharmony_ci			if (errno == EINTR)
358d5ac70f0Sopenharmony_ci				continue;
359d5ac70f0Sopenharmony_ci			/* some error */
360d5ac70f0Sopenharmony_ci			break;
361d5ac70f0Sopenharmony_ci		}
362d5ac70f0Sopenharmony_ci		if (ret == 0 || (pfds[0].revents & (POLLERR | POLLHUP))) {	/* timeout or error? */
363d5ac70f0Sopenharmony_ci			struct shmid_ds buf;
364d5ac70f0Sopenharmony_ci			snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
365d5ac70f0Sopenharmony_ci			if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) {
366d5ac70f0Sopenharmony_ci				_snd_pcm_direct_shm_discard(dmix);
367d5ac70f0Sopenharmony_ci				snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
368d5ac70f0Sopenharmony_ci				continue;
369d5ac70f0Sopenharmony_ci			}
370d5ac70f0Sopenharmony_ci			server_printf("DIRECT SERVER: nattch = %i\n", (int)buf.shm_nattch);
371d5ac70f0Sopenharmony_ci			if (buf.shm_nattch == 1)	/* server is the last user, exit */
372d5ac70f0Sopenharmony_ci				break;
373d5ac70f0Sopenharmony_ci			snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
374d5ac70f0Sopenharmony_ci			continue;
375d5ac70f0Sopenharmony_ci		}
376d5ac70f0Sopenharmony_ci		if (pfds[0].revents & POLLIN) {
377d5ac70f0Sopenharmony_ci			ret--;
378d5ac70f0Sopenharmony_ci			sck = accept(dmix->server_fd, 0, 0);
379d5ac70f0Sopenharmony_ci			if (sck >= 0) {
380d5ac70f0Sopenharmony_ci				server_printf("DIRECT SERVER: new connection %i\n", sck);
381d5ac70f0Sopenharmony_ci				if (current == max) {
382d5ac70f0Sopenharmony_ci					close(sck);
383d5ac70f0Sopenharmony_ci				} else {
384d5ac70f0Sopenharmony_ci					unsigned char buf = 'A';
385d5ac70f0Sopenharmony_ci					pfds[current+1].fd = sck;
386d5ac70f0Sopenharmony_ci					pfds[current+1].events = POLLIN | POLLERR | POLLHUP;
387d5ac70f0Sopenharmony_ci					_snd_send_fd(sck, &buf, 1, dmix->hw_fd);
388d5ac70f0Sopenharmony_ci					server_printf("DIRECT SERVER: fd sent ok\n");
389d5ac70f0Sopenharmony_ci					current++;
390d5ac70f0Sopenharmony_ci				}
391d5ac70f0Sopenharmony_ci			}
392d5ac70f0Sopenharmony_ci		}
393d5ac70f0Sopenharmony_ci		for (i = 0; i < current && ret > 0; i++) {
394d5ac70f0Sopenharmony_ci			struct pollfd *pfd = &pfds[i+1];
395d5ac70f0Sopenharmony_ci			unsigned char cmd;
396d5ac70f0Sopenharmony_ci			server_printf("client %i revents = 0x%x\n", pfd->fd, pfd->revents);
397d5ac70f0Sopenharmony_ci			if (pfd->revents & (POLLERR | POLLHUP)) {
398d5ac70f0Sopenharmony_ci				ret--;
399d5ac70f0Sopenharmony_ci				close(pfd->fd);
400d5ac70f0Sopenharmony_ci				pfd->fd = -1;
401d5ac70f0Sopenharmony_ci				continue;
402d5ac70f0Sopenharmony_ci			}
403d5ac70f0Sopenharmony_ci			if (!(pfd->revents & POLLIN))
404d5ac70f0Sopenharmony_ci				continue;
405d5ac70f0Sopenharmony_ci			ret--;
406d5ac70f0Sopenharmony_ci			if (read(pfd->fd, &cmd, 1) == 1)
407d5ac70f0Sopenharmony_ci				cmd = 0 /*process command */;
408d5ac70f0Sopenharmony_ci		}
409d5ac70f0Sopenharmony_ci		for (i = 0; i < current; i++) {
410d5ac70f0Sopenharmony_ci			if (pfds[i+1].fd < 0) {
411d5ac70f0Sopenharmony_ci				if (i + 1 != max)
412d5ac70f0Sopenharmony_ci					memcpy(&pfds[i+1], &pfds[i+2], sizeof(struct pollfd) * (max - i - 1));
413d5ac70f0Sopenharmony_ci				current--;
414d5ac70f0Sopenharmony_ci			}
415d5ac70f0Sopenharmony_ci		}
416d5ac70f0Sopenharmony_ci	}
417d5ac70f0Sopenharmony_ci	server_cleanup(dmix);
418d5ac70f0Sopenharmony_ci	server_printf("DIRECT SERVER EXIT\n");
419d5ac70f0Sopenharmony_ci#ifdef SERVER_JOB_DEBUG
420d5ac70f0Sopenharmony_ci	close(0); close(1); close(2);
421d5ac70f0Sopenharmony_ci#endif
422d5ac70f0Sopenharmony_ci	_exit(EXIT_SUCCESS);
423d5ac70f0Sopenharmony_ci}
424d5ac70f0Sopenharmony_ci
425d5ac70f0Sopenharmony_ciint snd_pcm_direct_server_create(snd_pcm_direct_t *dmix)
426d5ac70f0Sopenharmony_ci{
427d5ac70f0Sopenharmony_ci	int ret;
428d5ac70f0Sopenharmony_ci
429d5ac70f0Sopenharmony_ci	dmix->server_fd = -1;
430d5ac70f0Sopenharmony_ci
431d5ac70f0Sopenharmony_ci	ret = get_tmp_name(dmix->shmptr->socket_name, sizeof(dmix->shmptr->socket_name));
432d5ac70f0Sopenharmony_ci	if (ret < 0)
433d5ac70f0Sopenharmony_ci		return ret;
434d5ac70f0Sopenharmony_ci
435d5ac70f0Sopenharmony_ci	ret = make_local_socket(dmix->shmptr->socket_name, 1, dmix->ipc_perm, dmix->ipc_gid);
436d5ac70f0Sopenharmony_ci	if (ret < 0)
437d5ac70f0Sopenharmony_ci		return ret;
438d5ac70f0Sopenharmony_ci	dmix->server_fd = ret;
439d5ac70f0Sopenharmony_ci
440d5ac70f0Sopenharmony_ci	ret = listen(dmix->server_fd, 4);
441d5ac70f0Sopenharmony_ci	if (ret < 0) {
442d5ac70f0Sopenharmony_ci		close(dmix->server_fd);
443d5ac70f0Sopenharmony_ci		return ret;
444d5ac70f0Sopenharmony_ci	}
445d5ac70f0Sopenharmony_ci
446d5ac70f0Sopenharmony_ci	ret = fork();
447d5ac70f0Sopenharmony_ci	if (ret < 0) {
448d5ac70f0Sopenharmony_ci		close(dmix->server_fd);
449d5ac70f0Sopenharmony_ci		return ret;
450d5ac70f0Sopenharmony_ci	} else if (ret == 0) {
451d5ac70f0Sopenharmony_ci		ret = fork();
452d5ac70f0Sopenharmony_ci		if (ret == 0)
453d5ac70f0Sopenharmony_ci			server_job(dmix);
454d5ac70f0Sopenharmony_ci		_exit(EXIT_SUCCESS);
455d5ac70f0Sopenharmony_ci	} else {
456d5ac70f0Sopenharmony_ci		waitpid(ret, NULL, 0);
457d5ac70f0Sopenharmony_ci	}
458d5ac70f0Sopenharmony_ci	dmix->server_pid = ret;
459d5ac70f0Sopenharmony_ci	dmix->server = 1;
460d5ac70f0Sopenharmony_ci	return 0;
461d5ac70f0Sopenharmony_ci}
462d5ac70f0Sopenharmony_ci
463d5ac70f0Sopenharmony_ciint snd_pcm_direct_server_discard(snd_pcm_direct_t *dmix)
464d5ac70f0Sopenharmony_ci{
465d5ac70f0Sopenharmony_ci	if (dmix->server) {
466d5ac70f0Sopenharmony_ci		//kill(dmix->server_pid, SIGTERM);
467d5ac70f0Sopenharmony_ci		//waitpid(dmix->server_pid, NULL, 0);
468d5ac70f0Sopenharmony_ci		dmix->server_pid = (pid_t)-1;
469d5ac70f0Sopenharmony_ci	}
470d5ac70f0Sopenharmony_ci	if (dmix->server_fd > 0) {
471d5ac70f0Sopenharmony_ci		close(dmix->server_fd);
472d5ac70f0Sopenharmony_ci		dmix->server_fd = -1;
473d5ac70f0Sopenharmony_ci	}
474d5ac70f0Sopenharmony_ci	dmix->server = 0;
475d5ac70f0Sopenharmony_ci	return 0;
476d5ac70f0Sopenharmony_ci}
477d5ac70f0Sopenharmony_ci
478d5ac70f0Sopenharmony_ci/*
479d5ac70f0Sopenharmony_ci *  client side
480d5ac70f0Sopenharmony_ci */
481d5ac70f0Sopenharmony_ci
482d5ac70f0Sopenharmony_ciint snd_pcm_direct_client_connect(snd_pcm_direct_t *dmix)
483d5ac70f0Sopenharmony_ci{
484d5ac70f0Sopenharmony_ci	int ret;
485d5ac70f0Sopenharmony_ci	unsigned char buf;
486d5ac70f0Sopenharmony_ci
487d5ac70f0Sopenharmony_ci	ret = make_local_socket(dmix->shmptr->socket_name, 0, -1, -1);
488d5ac70f0Sopenharmony_ci	if (ret < 0)
489d5ac70f0Sopenharmony_ci		return ret;
490d5ac70f0Sopenharmony_ci	dmix->comm_fd = ret;
491d5ac70f0Sopenharmony_ci
492d5ac70f0Sopenharmony_ci	ret = snd_receive_fd(dmix->comm_fd, &buf, 1, &dmix->hw_fd);
493d5ac70f0Sopenharmony_ci	if (ret < 1 || buf != 'A') {
494d5ac70f0Sopenharmony_ci		close(dmix->comm_fd);
495d5ac70f0Sopenharmony_ci		dmix->comm_fd = -1;
496d5ac70f0Sopenharmony_ci		return ret;
497d5ac70f0Sopenharmony_ci	}
498d5ac70f0Sopenharmony_ci
499d5ac70f0Sopenharmony_ci	dmix->client = 1;
500d5ac70f0Sopenharmony_ci	return 0;
501d5ac70f0Sopenharmony_ci}
502d5ac70f0Sopenharmony_ci
503d5ac70f0Sopenharmony_ciint snd_pcm_direct_client_discard(snd_pcm_direct_t *dmix)
504d5ac70f0Sopenharmony_ci{
505d5ac70f0Sopenharmony_ci	if (dmix->client) {
506d5ac70f0Sopenharmony_ci		close(dmix->comm_fd);
507d5ac70f0Sopenharmony_ci		dmix->comm_fd = -1;
508d5ac70f0Sopenharmony_ci	}
509d5ac70f0Sopenharmony_ci	return 0;
510d5ac70f0Sopenharmony_ci}
511d5ac70f0Sopenharmony_ci
512d5ac70f0Sopenharmony_ci/*
513d5ac70f0Sopenharmony_ci *  plugin helpers
514d5ac70f0Sopenharmony_ci */
515d5ac70f0Sopenharmony_ci
516d5ac70f0Sopenharmony_ciint snd_pcm_direct_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
517d5ac70f0Sopenharmony_ci{
518d5ac70f0Sopenharmony_ci	/* value is cached for us in pcm->mode (SND_PCM_NONBLOCK flag) */
519d5ac70f0Sopenharmony_ci	return 0;
520d5ac70f0Sopenharmony_ci}
521d5ac70f0Sopenharmony_ci
522d5ac70f0Sopenharmony_ciint snd_pcm_direct_async(snd_pcm_t *pcm, int sig, pid_t pid)
523d5ac70f0Sopenharmony_ci{
524d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dmix = pcm->private_data;
525d5ac70f0Sopenharmony_ci	return snd_timer_async(dmix->timer, sig, pid);
526d5ac70f0Sopenharmony_ci}
527d5ac70f0Sopenharmony_ci
528d5ac70f0Sopenharmony_ci/* empty the timer read queue */
529d5ac70f0Sopenharmony_ciint snd_pcm_direct_clear_timer_queue(snd_pcm_direct_t *dmix)
530d5ac70f0Sopenharmony_ci{
531d5ac70f0Sopenharmony_ci	int changed = 0;
532d5ac70f0Sopenharmony_ci	if (dmix->timer_need_poll) {
533d5ac70f0Sopenharmony_ci		while (poll(&dmix->timer_fd, 1, 0) > 0) {
534d5ac70f0Sopenharmony_ci			changed++;
535d5ac70f0Sopenharmony_ci			/* we don't need the value */
536d5ac70f0Sopenharmony_ci			if (dmix->tread) {
537d5ac70f0Sopenharmony_ci				snd_timer_tread_t rbuf[4];
538d5ac70f0Sopenharmony_ci				snd_timer_read(dmix->timer, rbuf, sizeof(rbuf));
539d5ac70f0Sopenharmony_ci			} else {
540d5ac70f0Sopenharmony_ci				snd_timer_read_t rbuf;
541d5ac70f0Sopenharmony_ci				snd_timer_read(dmix->timer, &rbuf, sizeof(rbuf));
542d5ac70f0Sopenharmony_ci			}
543d5ac70f0Sopenharmony_ci		}
544d5ac70f0Sopenharmony_ci	} else {
545d5ac70f0Sopenharmony_ci		if (dmix->tread) {
546d5ac70f0Sopenharmony_ci			snd_timer_tread_t rbuf[4];
547d5ac70f0Sopenharmony_ci			int len;
548d5ac70f0Sopenharmony_ci			while ((len = snd_timer_read(dmix->timer, rbuf,
549d5ac70f0Sopenharmony_ci						     sizeof(rbuf))) > 0
550d5ac70f0Sopenharmony_ci						     && (++changed) &&
551d5ac70f0Sopenharmony_ci			       len != sizeof(rbuf[0]))
552d5ac70f0Sopenharmony_ci				;
553d5ac70f0Sopenharmony_ci		} else {
554d5ac70f0Sopenharmony_ci			snd_timer_read_t rbuf;
555d5ac70f0Sopenharmony_ci			while (snd_timer_read(dmix->timer, &rbuf, sizeof(rbuf)) > 0)
556d5ac70f0Sopenharmony_ci				changed++;
557d5ac70f0Sopenharmony_ci		}
558d5ac70f0Sopenharmony_ci	}
559d5ac70f0Sopenharmony_ci	return changed;
560d5ac70f0Sopenharmony_ci}
561d5ac70f0Sopenharmony_ci
562d5ac70f0Sopenharmony_ciint snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix)
563d5ac70f0Sopenharmony_ci{
564d5ac70f0Sopenharmony_ci	snd_timer_stop(dmix->timer);
565d5ac70f0Sopenharmony_ci	return 0;
566d5ac70f0Sopenharmony_ci}
567d5ac70f0Sopenharmony_ci
568d5ac70f0Sopenharmony_ci#define RECOVERIES_FLAG_SUSPENDED	(1U << 31)
569d5ac70f0Sopenharmony_ci#define RECOVERIES_MASK			((1U << 31) - 1)
570d5ac70f0Sopenharmony_ci
571d5ac70f0Sopenharmony_ci/*
572d5ac70f0Sopenharmony_ci * Recover slave on XRUN or SUSPENDED.
573d5ac70f0Sopenharmony_ci * Even if direct plugins disable xrun detection, there might be an xrun
574d5ac70f0Sopenharmony_ci * raised directly by some drivers.
575d5ac70f0Sopenharmony_ci * The first client recovers slave pcm.
576d5ac70f0Sopenharmony_ci * Each client needs to execute sw xrun handling afterwards
577d5ac70f0Sopenharmony_ci */
578d5ac70f0Sopenharmony_ciint snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
579d5ac70f0Sopenharmony_ci{
580d5ac70f0Sopenharmony_ci	unsigned int recoveries;
581d5ac70f0Sopenharmony_ci	int state;
582d5ac70f0Sopenharmony_ci	int ret;
583d5ac70f0Sopenharmony_ci	int semerr;
584d5ac70f0Sopenharmony_ci
585d5ac70f0Sopenharmony_ci	semerr = snd_pcm_direct_semaphore_down(direct,
586d5ac70f0Sopenharmony_ci						   DIRECT_IPC_SEM_CLIENT);
587d5ac70f0Sopenharmony_ci	if (semerr < 0) {
588d5ac70f0Sopenharmony_ci		SNDERR("SEMDOWN FAILED with err %d", semerr);
589d5ac70f0Sopenharmony_ci		return semerr;
590d5ac70f0Sopenharmony_ci	}
591d5ac70f0Sopenharmony_ci
592d5ac70f0Sopenharmony_ci	state = snd_pcm_state(direct->spcm);
593d5ac70f0Sopenharmony_ci	if (state != SND_PCM_STATE_XRUN && state != SND_PCM_STATE_SUSPENDED) {
594d5ac70f0Sopenharmony_ci		/* ignore... someone else already did recovery */
595d5ac70f0Sopenharmony_ci		semerr = snd_pcm_direct_semaphore_up(direct,
596d5ac70f0Sopenharmony_ci						     DIRECT_IPC_SEM_CLIENT);
597d5ac70f0Sopenharmony_ci		if (semerr < 0) {
598d5ac70f0Sopenharmony_ci			SNDERR("SEMUP FAILED with err %d", semerr);
599d5ac70f0Sopenharmony_ci			return semerr;
600d5ac70f0Sopenharmony_ci		}
601d5ac70f0Sopenharmony_ci		return 0;
602d5ac70f0Sopenharmony_ci	}
603d5ac70f0Sopenharmony_ci
604d5ac70f0Sopenharmony_ci	recoveries = direct->shmptr->s.recoveries;
605d5ac70f0Sopenharmony_ci	recoveries = (recoveries + 1) & RECOVERIES_MASK;
606d5ac70f0Sopenharmony_ci	if (state == SND_PCM_STATE_SUSPENDED)
607d5ac70f0Sopenharmony_ci		recoveries |= RECOVERIES_FLAG_SUSPENDED;
608d5ac70f0Sopenharmony_ci	direct->shmptr->s.recoveries = recoveries;
609d5ac70f0Sopenharmony_ci
610d5ac70f0Sopenharmony_ci	/* some buggy drivers require the device resumed before prepared;
611d5ac70f0Sopenharmony_ci	 * when a device has RESUME flag and is in SUSPENDED state, resume
612d5ac70f0Sopenharmony_ci	 * here but immediately drop to bring it to a sane active state.
613d5ac70f0Sopenharmony_ci	 */
614d5ac70f0Sopenharmony_ci	if (state == SND_PCM_STATE_SUSPENDED &&
615d5ac70f0Sopenharmony_ci	    (direct->spcm->info & SND_PCM_INFO_RESUME)) {
616d5ac70f0Sopenharmony_ci		snd_pcm_resume(direct->spcm);
617d5ac70f0Sopenharmony_ci		snd_pcm_drop(direct->spcm);
618d5ac70f0Sopenharmony_ci		snd_pcm_direct_timer_stop(direct);
619d5ac70f0Sopenharmony_ci		snd_pcm_direct_clear_timer_queue(direct);
620d5ac70f0Sopenharmony_ci	}
621d5ac70f0Sopenharmony_ci
622d5ac70f0Sopenharmony_ci	ret = snd_pcm_prepare(direct->spcm);
623d5ac70f0Sopenharmony_ci	if (ret < 0) {
624d5ac70f0Sopenharmony_ci		SNDERR("recover: unable to prepare slave");
625d5ac70f0Sopenharmony_ci		semerr = snd_pcm_direct_semaphore_up(direct,
626d5ac70f0Sopenharmony_ci						     DIRECT_IPC_SEM_CLIENT);
627d5ac70f0Sopenharmony_ci		if (semerr < 0) {
628d5ac70f0Sopenharmony_ci			SNDERR("SEMUP FAILED with err %d", semerr);
629d5ac70f0Sopenharmony_ci			return semerr;
630d5ac70f0Sopenharmony_ci		}
631d5ac70f0Sopenharmony_ci		return ret;
632d5ac70f0Sopenharmony_ci	}
633d5ac70f0Sopenharmony_ci
634d5ac70f0Sopenharmony_ci	if (direct->type == SND_PCM_TYPE_DSHARE) {
635d5ac70f0Sopenharmony_ci		const snd_pcm_channel_area_t *dst_areas;
636d5ac70f0Sopenharmony_ci		dst_areas = snd_pcm_mmap_areas(direct->spcm);
637d5ac70f0Sopenharmony_ci		snd_pcm_areas_silence(dst_areas, 0, direct->spcm->channels,
638d5ac70f0Sopenharmony_ci				      direct->spcm->buffer_size,
639d5ac70f0Sopenharmony_ci				      direct->spcm->format);
640d5ac70f0Sopenharmony_ci	}
641d5ac70f0Sopenharmony_ci
642d5ac70f0Sopenharmony_ci	ret = snd_pcm_start(direct->spcm);
643d5ac70f0Sopenharmony_ci	if (ret < 0) {
644d5ac70f0Sopenharmony_ci		SNDERR("recover: unable to start slave");
645d5ac70f0Sopenharmony_ci		semerr = snd_pcm_direct_semaphore_up(direct,
646d5ac70f0Sopenharmony_ci						     DIRECT_IPC_SEM_CLIENT);
647d5ac70f0Sopenharmony_ci		if (semerr < 0) {
648d5ac70f0Sopenharmony_ci			SNDERR("SEMUP FAILED with err %d", semerr);
649d5ac70f0Sopenharmony_ci			return semerr;
650d5ac70f0Sopenharmony_ci		}
651d5ac70f0Sopenharmony_ci		return ret;
652d5ac70f0Sopenharmony_ci	}
653d5ac70f0Sopenharmony_ci	semerr = snd_pcm_direct_semaphore_up(direct,
654d5ac70f0Sopenharmony_ci						 DIRECT_IPC_SEM_CLIENT);
655d5ac70f0Sopenharmony_ci	if (semerr < 0) {
656d5ac70f0Sopenharmony_ci		SNDERR("SEMUP FAILED with err %d", semerr);
657d5ac70f0Sopenharmony_ci		return semerr;
658d5ac70f0Sopenharmony_ci	}
659d5ac70f0Sopenharmony_ci	return 0;
660d5ac70f0Sopenharmony_ci}
661d5ac70f0Sopenharmony_ci
662d5ac70f0Sopenharmony_ci/*
663d5ac70f0Sopenharmony_ci * enter xrun or suspended state, if slave xrun occurred or suspended
664d5ac70f0Sopenharmony_ci * @return: 0 for no xrun/suspend or a negative error code for xrun/suspend
665d5ac70f0Sopenharmony_ci */
666d5ac70f0Sopenharmony_ciint snd_pcm_direct_check_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm)
667d5ac70f0Sopenharmony_ci{
668d5ac70f0Sopenharmony_ci	int err;
669d5ac70f0Sopenharmony_ci
670d5ac70f0Sopenharmony_ci	switch (snd_pcm_state(direct->spcm)) {
671d5ac70f0Sopenharmony_ci	case SND_PCM_STATE_DISCONNECTED:
672d5ac70f0Sopenharmony_ci		direct->state = SNDRV_PCM_STATE_DISCONNECTED;
673d5ac70f0Sopenharmony_ci		return -ENODEV;
674d5ac70f0Sopenharmony_ci	case SND_PCM_STATE_XRUN:
675d5ac70f0Sopenharmony_ci	case SND_PCM_STATE_SUSPENDED:
676d5ac70f0Sopenharmony_ci		if ((err = snd_pcm_direct_slave_recover(direct)) < 0)
677d5ac70f0Sopenharmony_ci			return err;
678d5ac70f0Sopenharmony_ci		break;
679d5ac70f0Sopenharmony_ci	default:
680d5ac70f0Sopenharmony_ci		break;
681d5ac70f0Sopenharmony_ci	}
682d5ac70f0Sopenharmony_ci
683d5ac70f0Sopenharmony_ci	if (direct->state == SND_PCM_STATE_XRUN)
684d5ac70f0Sopenharmony_ci		return -EPIPE;
685d5ac70f0Sopenharmony_ci	else if (direct->state == SND_PCM_STATE_SUSPENDED)
686d5ac70f0Sopenharmony_ci		return -ESTRPIPE;
687d5ac70f0Sopenharmony_ci	if (direct->shmptr->s.recoveries != direct->recoveries) {
688d5ac70f0Sopenharmony_ci		/* no matter how many xruns we missed -
689d5ac70f0Sopenharmony_ci		 * so don't increment but just update to actual counter
690d5ac70f0Sopenharmony_ci		 */
691d5ac70f0Sopenharmony_ci		direct->recoveries = direct->shmptr->s.recoveries;
692d5ac70f0Sopenharmony_ci		pcm->fast_ops->drop(pcm->fast_op_arg);
693d5ac70f0Sopenharmony_ci		/* trigger_tstamp update is missing in drop callbacks */
694d5ac70f0Sopenharmony_ci		gettimestamp(&direct->trigger_tstamp, pcm->tstamp_type);
695d5ac70f0Sopenharmony_ci		/* no timer clear:
696d5ac70f0Sopenharmony_ci		 * if slave already entered xrun again the event is lost.
697d5ac70f0Sopenharmony_ci		 * snd_pcm_direct_clear_timer_queue(direct);
698d5ac70f0Sopenharmony_ci		 */
699d5ac70f0Sopenharmony_ci		if (direct->recoveries & RECOVERIES_FLAG_SUSPENDED) {
700d5ac70f0Sopenharmony_ci			direct->state = SND_PCM_STATE_SUSPENDED;
701d5ac70f0Sopenharmony_ci			return -ESTRPIPE;
702d5ac70f0Sopenharmony_ci		} else {
703d5ac70f0Sopenharmony_ci			direct->state = SND_PCM_STATE_XRUN;
704d5ac70f0Sopenharmony_ci			return -EPIPE;
705d5ac70f0Sopenharmony_ci		}
706d5ac70f0Sopenharmony_ci	}
707d5ac70f0Sopenharmony_ci	return 0;
708d5ac70f0Sopenharmony_ci}
709d5ac70f0Sopenharmony_ci
710d5ac70f0Sopenharmony_ci/*
711d5ac70f0Sopenharmony_ci * This is the only operation guaranteed to be called before entering poll().
712d5ac70f0Sopenharmony_ci * Direct plugins use fd of snd_timer to poll on, these timers do NOT check
713d5ac70f0Sopenharmony_ci * state of substream in kernel by intention.
714d5ac70f0Sopenharmony_ci * Only the enter to xrun might be notified once (SND_TIMER_EVENT_MSTOP).
715d5ac70f0Sopenharmony_ci * If xrun event was not correctly handled or was ignored it will never be
716d5ac70f0Sopenharmony_ci * evaluated again afterwards.
717d5ac70f0Sopenharmony_ci * This will result in snd_pcm_wait() always returning timeout.
718d5ac70f0Sopenharmony_ci * In contrast poll() on pcm hardware checks ALSA state and will immediately
719d5ac70f0Sopenharmony_ci * return POLLERR on XRUN.
720d5ac70f0Sopenharmony_ci *
721d5ac70f0Sopenharmony_ci * To prevent timeout and applications endlessly spinning without xrun
722d5ac70f0Sopenharmony_ci * detected we add a state check here which may trigger the xrun sequence.
723d5ac70f0Sopenharmony_ci *
724d5ac70f0Sopenharmony_ci * return count of filled descriptors or negative error code
725d5ac70f0Sopenharmony_ci */
726d5ac70f0Sopenharmony_ciint snd_pcm_direct_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds,
727d5ac70f0Sopenharmony_ci				    unsigned int space)
728d5ac70f0Sopenharmony_ci{
729d5ac70f0Sopenharmony_ci	if (pcm->poll_fd < 0) {
730d5ac70f0Sopenharmony_ci		SNDMSG("poll_fd < 0");
731d5ac70f0Sopenharmony_ci		return -EIO;
732d5ac70f0Sopenharmony_ci	}
733d5ac70f0Sopenharmony_ci	if (space >= 1 && pfds) {
734d5ac70f0Sopenharmony_ci		pfds->fd = pcm->poll_fd;
735d5ac70f0Sopenharmony_ci		pfds->events = pcm->poll_events | POLLERR | POLLNVAL;
736d5ac70f0Sopenharmony_ci	} else {
737d5ac70f0Sopenharmony_ci		return 0;
738d5ac70f0Sopenharmony_ci	}
739d5ac70f0Sopenharmony_ci
740d5ac70f0Sopenharmony_ci	/* this will also evaluate slave state and enter xrun if necessary */
741d5ac70f0Sopenharmony_ci	/* using __snd_pcm_state() since this function is called inside lock */
742d5ac70f0Sopenharmony_ci	switch (__snd_pcm_state(pcm)) {
743d5ac70f0Sopenharmony_ci	case SND_PCM_STATE_XRUN:
744d5ac70f0Sopenharmony_ci		return -EPIPE;
745d5ac70f0Sopenharmony_ci	default:
746d5ac70f0Sopenharmony_ci		break;
747d5ac70f0Sopenharmony_ci	}
748d5ac70f0Sopenharmony_ci	return 1;
749d5ac70f0Sopenharmony_ci}
750d5ac70f0Sopenharmony_ci
751d5ac70f0Sopenharmony_ciint snd_pcm_direct_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
752d5ac70f0Sopenharmony_ci{
753d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dmix = pcm->private_data;
754d5ac70f0Sopenharmony_ci	unsigned short events;
755d5ac70f0Sopenharmony_ci	int empty = 0;
756d5ac70f0Sopenharmony_ci
757d5ac70f0Sopenharmony_ci	assert(pfds && nfds == 1 && revents);
758d5ac70f0Sopenharmony_ci
759d5ac70f0Sopenharmony_citimer_changed:
760d5ac70f0Sopenharmony_ci	events = pfds[0].revents;
761d5ac70f0Sopenharmony_ci	if (events & POLLIN) {
762d5ac70f0Sopenharmony_ci		snd_pcm_uframes_t avail;
763d5ac70f0Sopenharmony_ci		__snd_pcm_avail_update(pcm);
764d5ac70f0Sopenharmony_ci		if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
765d5ac70f0Sopenharmony_ci			events |= POLLOUT;
766d5ac70f0Sopenharmony_ci			events &= ~POLLIN;
767d5ac70f0Sopenharmony_ci			avail = snd_pcm_mmap_playback_avail(pcm);
768d5ac70f0Sopenharmony_ci		} else {
769d5ac70f0Sopenharmony_ci			avail = snd_pcm_mmap_capture_avail(pcm);
770d5ac70f0Sopenharmony_ci		}
771d5ac70f0Sopenharmony_ci		empty = avail < pcm->avail_min;
772d5ac70f0Sopenharmony_ci	}
773d5ac70f0Sopenharmony_ci
774d5ac70f0Sopenharmony_ci	if (snd_pcm_direct_check_xrun(dmix, pcm) < 0 ||
775d5ac70f0Sopenharmony_ci	    snd_pcm_state(dmix->spcm) == SND_PCM_STATE_SETUP) {
776d5ac70f0Sopenharmony_ci		events |= POLLERR;
777d5ac70f0Sopenharmony_ci	} else {
778d5ac70f0Sopenharmony_ci		if (empty) {
779d5ac70f0Sopenharmony_ci			/* here we have a race condition:
780d5ac70f0Sopenharmony_ci			 * if period event arrived after the avail_update call
781d5ac70f0Sopenharmony_ci			 * above we might clear this event with the following
782d5ac70f0Sopenharmony_ci			 * clear_timer_queue.
783d5ac70f0Sopenharmony_ci			 * There is no way to do this in atomic manner, so we
784d5ac70f0Sopenharmony_ci			 * need to recheck avail_update if we successfully
785d5ac70f0Sopenharmony_ci			 * cleared a poll event.
786d5ac70f0Sopenharmony_ci			 */
787d5ac70f0Sopenharmony_ci			if (snd_pcm_direct_clear_timer_queue(dmix))
788d5ac70f0Sopenharmony_ci				goto timer_changed;
789d5ac70f0Sopenharmony_ci			events &= ~(POLLOUT|POLLIN);
790d5ac70f0Sopenharmony_ci			/* additional check */
791d5ac70f0Sopenharmony_ci			switch (__snd_pcm_state(pcm)) {
792d5ac70f0Sopenharmony_ci			case SND_PCM_STATE_XRUN:
793d5ac70f0Sopenharmony_ci			case SND_PCM_STATE_SUSPENDED:
794d5ac70f0Sopenharmony_ci			case SND_PCM_STATE_SETUP:
795d5ac70f0Sopenharmony_ci				events |= POLLERR;
796d5ac70f0Sopenharmony_ci				break;
797d5ac70f0Sopenharmony_ci			default:
798d5ac70f0Sopenharmony_ci				break;
799d5ac70f0Sopenharmony_ci			}
800d5ac70f0Sopenharmony_ci		}
801d5ac70f0Sopenharmony_ci	}
802d5ac70f0Sopenharmony_ci	*revents = events;
803d5ac70f0Sopenharmony_ci	return 0;
804d5ac70f0Sopenharmony_ci}
805d5ac70f0Sopenharmony_ci
806d5ac70f0Sopenharmony_ciint snd_pcm_direct_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
807d5ac70f0Sopenharmony_ci{
808d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dmix = pcm->private_data;
809d5ac70f0Sopenharmony_ci
810d5ac70f0Sopenharmony_ci	if (dmix->spcm && !dmix->shmptr->use_server)
811d5ac70f0Sopenharmony_ci		return snd_pcm_info(dmix->spcm, info);
812d5ac70f0Sopenharmony_ci
813d5ac70f0Sopenharmony_ci	memset(info, 0, sizeof(*info));
814d5ac70f0Sopenharmony_ci	info->stream = pcm->stream;
815d5ac70f0Sopenharmony_ci	info->card = -1;
816d5ac70f0Sopenharmony_ci	/* FIXME: fill this with something more useful: we know the hardware name */
817d5ac70f0Sopenharmony_ci	if (pcm->name) {
818d5ac70f0Sopenharmony_ci		snd_strlcpy((char *)info->id, pcm->name, sizeof(info->id));
819d5ac70f0Sopenharmony_ci		snd_strlcpy((char *)info->name, pcm->name, sizeof(info->name));
820d5ac70f0Sopenharmony_ci		snd_strlcpy((char *)info->subname, pcm->name, sizeof(info->subname));
821d5ac70f0Sopenharmony_ci	}
822d5ac70f0Sopenharmony_ci	info->subdevices_count = 1;
823d5ac70f0Sopenharmony_ci	return 0;
824d5ac70f0Sopenharmony_ci}
825d5ac70f0Sopenharmony_ci
826d5ac70f0Sopenharmony_cistatic inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params,
827d5ac70f0Sopenharmony_ci					snd_pcm_hw_param_t var)
828d5ac70f0Sopenharmony_ci{
829d5ac70f0Sopenharmony_ci	return &params->masks[var - SND_PCM_HW_PARAM_FIRST_MASK];
830d5ac70f0Sopenharmony_ci}
831d5ac70f0Sopenharmony_ci
832d5ac70f0Sopenharmony_cistatic inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
833d5ac70f0Sopenharmony_ci						snd_pcm_hw_param_t var)
834d5ac70f0Sopenharmony_ci{
835d5ac70f0Sopenharmony_ci	return &params->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL];
836d5ac70f0Sopenharmony_ci}
837d5ac70f0Sopenharmony_ci
838d5ac70f0Sopenharmony_cistatic int hw_param_interval_refine_one(snd_pcm_hw_params_t *params,
839d5ac70f0Sopenharmony_ci					snd_pcm_hw_param_t var,
840d5ac70f0Sopenharmony_ci					snd_interval_t *src)
841d5ac70f0Sopenharmony_ci{
842d5ac70f0Sopenharmony_ci	snd_interval_t *i;
843d5ac70f0Sopenharmony_ci
844d5ac70f0Sopenharmony_ci	if (!(params->rmask & (1<<var)))	/* nothing to do? */
845d5ac70f0Sopenharmony_ci		return 0;
846d5ac70f0Sopenharmony_ci	i = hw_param_interval(params, var);
847d5ac70f0Sopenharmony_ci	if (snd_interval_empty(i)) {
848d5ac70f0Sopenharmony_ci		SNDERR("dshare interval %i empty?", (int)var);
849d5ac70f0Sopenharmony_ci		return -EINVAL;
850d5ac70f0Sopenharmony_ci	}
851d5ac70f0Sopenharmony_ci	if (snd_interval_refine(i, src))
852d5ac70f0Sopenharmony_ci		params->cmask |= 1<<var;
853d5ac70f0Sopenharmony_ci	return 0;
854d5ac70f0Sopenharmony_ci}
855d5ac70f0Sopenharmony_ci
856d5ac70f0Sopenharmony_cistatic int hw_param_interval_refine_minmax(snd_pcm_hw_params_t *params,
857d5ac70f0Sopenharmony_ci					   snd_pcm_hw_param_t var,
858d5ac70f0Sopenharmony_ci					   unsigned int imin,
859d5ac70f0Sopenharmony_ci					   unsigned int imax)
860d5ac70f0Sopenharmony_ci{
861d5ac70f0Sopenharmony_ci	snd_interval_t t;
862d5ac70f0Sopenharmony_ci
863d5ac70f0Sopenharmony_ci	memset(&t, 0, sizeof(t));
864d5ac70f0Sopenharmony_ci	snd_interval_set_minmax(&t, imin, imax);
865d5ac70f0Sopenharmony_ci	t.integer = 1;
866d5ac70f0Sopenharmony_ci	return hw_param_interval_refine_one(params, var, &t);
867d5ac70f0Sopenharmony_ci}
868d5ac70f0Sopenharmony_ci
869d5ac70f0Sopenharmony_ci/* this code is used 'as-is' from the alsa kernel code */
870d5ac70f0Sopenharmony_cistatic int snd_interval_step(struct snd_interval *i, unsigned int min,
871d5ac70f0Sopenharmony_ci			     unsigned int step)
872d5ac70f0Sopenharmony_ci{
873d5ac70f0Sopenharmony_ci	unsigned int n;
874d5ac70f0Sopenharmony_ci	int changed = 0;
875d5ac70f0Sopenharmony_ci	n = (i->min - min) % step;
876d5ac70f0Sopenharmony_ci	if (n != 0 || i->openmin) {
877d5ac70f0Sopenharmony_ci		i->min += step - n;
878d5ac70f0Sopenharmony_ci		changed = 1;
879d5ac70f0Sopenharmony_ci	}
880d5ac70f0Sopenharmony_ci	n = (i->max - min) % step;
881d5ac70f0Sopenharmony_ci	if (n != 0 || i->openmax) {
882d5ac70f0Sopenharmony_ci		i->max -= n;
883d5ac70f0Sopenharmony_ci		changed = 1;
884d5ac70f0Sopenharmony_ci	}
885d5ac70f0Sopenharmony_ci	if (snd_interval_checkempty(i)) {
886d5ac70f0Sopenharmony_ci		i->empty = 1;
887d5ac70f0Sopenharmony_ci		return -EINVAL;
888d5ac70f0Sopenharmony_ci	}
889d5ac70f0Sopenharmony_ci	return changed;
890d5ac70f0Sopenharmony_ci}
891d5ac70f0Sopenharmony_ci
892d5ac70f0Sopenharmony_ci#undef REFINE_DEBUG
893d5ac70f0Sopenharmony_ci
894d5ac70f0Sopenharmony_ciint snd_pcm_direct_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
895d5ac70f0Sopenharmony_ci{
896d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dshare = pcm->private_data;
897d5ac70f0Sopenharmony_ci	static const snd_mask_t access = { .bits = {
898d5ac70f0Sopenharmony_ci					(1<<SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) |
899d5ac70f0Sopenharmony_ci					(1<<SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) |
900d5ac70f0Sopenharmony_ci					(1<<SNDRV_PCM_ACCESS_RW_INTERLEAVED) |
901d5ac70f0Sopenharmony_ci					(1<<SNDRV_PCM_ACCESS_RW_NONINTERLEAVED),
902d5ac70f0Sopenharmony_ci					0, 0, 0 } };
903d5ac70f0Sopenharmony_ci	int err;
904d5ac70f0Sopenharmony_ci
905d5ac70f0Sopenharmony_ci#ifdef REFINE_DEBUG
906d5ac70f0Sopenharmony_ci	snd_output_t *log;
907d5ac70f0Sopenharmony_ci	snd_output_stdio_attach(&log, stderr, 0);
908d5ac70f0Sopenharmony_ci	snd_output_puts(log, "DMIX REFINE (begin):\n");
909d5ac70f0Sopenharmony_ci	snd_pcm_hw_params_dump(params, log);
910d5ac70f0Sopenharmony_ci#endif
911d5ac70f0Sopenharmony_ci	if (params->rmask & (1<<SND_PCM_HW_PARAM_ACCESS)) {
912d5ac70f0Sopenharmony_ci		if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS))) {
913d5ac70f0Sopenharmony_ci			SNDERR("dshare access mask empty?");
914d5ac70f0Sopenharmony_ci			return -EINVAL;
915d5ac70f0Sopenharmony_ci		}
916d5ac70f0Sopenharmony_ci		if (snd_mask_refine(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS), &access))
917d5ac70f0Sopenharmony_ci			params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
918d5ac70f0Sopenharmony_ci	}
919d5ac70f0Sopenharmony_ci	if (params->rmask & (1<<SND_PCM_HW_PARAM_FORMAT)) {
920d5ac70f0Sopenharmony_ci		if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT))) {
921d5ac70f0Sopenharmony_ci			SNDERR("dshare format mask empty?");
922d5ac70f0Sopenharmony_ci			return -EINVAL;
923d5ac70f0Sopenharmony_ci		}
924d5ac70f0Sopenharmony_ci		if (snd_mask_refine_set(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT),
925d5ac70f0Sopenharmony_ci					dshare->shmptr->hw.format))
926d5ac70f0Sopenharmony_ci			params->cmask |= 1<<SND_PCM_HW_PARAM_FORMAT;
927d5ac70f0Sopenharmony_ci	}
928d5ac70f0Sopenharmony_ci	//snd_mask_none(hw_param_mask(params, SND_PCM_HW_PARAM_SUBFORMAT));
929d5ac70f0Sopenharmony_ci	if (params->rmask & (1<<SND_PCM_HW_PARAM_CHANNELS)) {
930d5ac70f0Sopenharmony_ci		if (snd_interval_empty(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS))) {
931d5ac70f0Sopenharmony_ci			SNDERR("dshare channels mask empty?");
932d5ac70f0Sopenharmony_ci			return -EINVAL;
933d5ac70f0Sopenharmony_ci		}
934d5ac70f0Sopenharmony_ci		err = snd_interval_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS), dshare->channels);
935d5ac70f0Sopenharmony_ci		if (err < 0)
936d5ac70f0Sopenharmony_ci			return err;
937d5ac70f0Sopenharmony_ci	}
938d5ac70f0Sopenharmony_ci	err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_RATE,
939d5ac70f0Sopenharmony_ci					   &dshare->shmptr->hw.rate);
940d5ac70f0Sopenharmony_ci	if (err < 0)
941d5ac70f0Sopenharmony_ci		return err;
942d5ac70f0Sopenharmony_ci
943d5ac70f0Sopenharmony_ci	if (dshare->max_periods < 0) {
944d5ac70f0Sopenharmony_ci		err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE,
945d5ac70f0Sopenharmony_ci						   &dshare->shmptr->hw.period_size);
946d5ac70f0Sopenharmony_ci		if (err < 0)
947d5ac70f0Sopenharmony_ci			return err;
948d5ac70f0Sopenharmony_ci		err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME,
949d5ac70f0Sopenharmony_ci						   &dshare->shmptr->hw.period_time);
950d5ac70f0Sopenharmony_ci		if (err < 0)
951d5ac70f0Sopenharmony_ci			return err;
952d5ac70f0Sopenharmony_ci		err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE,
953d5ac70f0Sopenharmony_ci						   &dshare->shmptr->hw.buffer_size);
954d5ac70f0Sopenharmony_ci		if (err < 0)
955d5ac70f0Sopenharmony_ci			return err;
956d5ac70f0Sopenharmony_ci		err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_TIME,
957d5ac70f0Sopenharmony_ci						   &dshare->shmptr->hw.buffer_time);
958d5ac70f0Sopenharmony_ci		if (err < 0)
959d5ac70f0Sopenharmony_ci			return err;
960d5ac70f0Sopenharmony_ci	} else if (params->rmask & ((1<<SND_PCM_HW_PARAM_PERIODS)|
961d5ac70f0Sopenharmony_ci				    (1<<SND_PCM_HW_PARAM_BUFFER_BYTES)|
962d5ac70f0Sopenharmony_ci				    (1<<SND_PCM_HW_PARAM_BUFFER_SIZE)|
963d5ac70f0Sopenharmony_ci				    (1<<SND_PCM_HW_PARAM_BUFFER_TIME)|
964d5ac70f0Sopenharmony_ci				    (1<<SND_PCM_HW_PARAM_PERIOD_TIME)|
965d5ac70f0Sopenharmony_ci				    (1<<SND_PCM_HW_PARAM_PERIOD_SIZE)|
966d5ac70f0Sopenharmony_ci				    (1<<SND_PCM_HW_PARAM_PERIOD_BYTES))) {
967d5ac70f0Sopenharmony_ci		snd_interval_t period_size = dshare->shmptr->hw.period_size;
968d5ac70f0Sopenharmony_ci		snd_interval_t period_time = dshare->shmptr->hw.period_time;
969d5ac70f0Sopenharmony_ci		int changed;
970d5ac70f0Sopenharmony_ci		unsigned int max_periods = dshare->max_periods;
971d5ac70f0Sopenharmony_ci		if (max_periods < 2)
972d5ac70f0Sopenharmony_ci			max_periods = dshare->slave_buffer_size / dshare->slave_period_size;
973d5ac70f0Sopenharmony_ci
974d5ac70f0Sopenharmony_ci		/* make sure buffer size does not exceed slave buffer size */
975d5ac70f0Sopenharmony_ci		err = hw_param_interval_refine_minmax(params, SND_PCM_HW_PARAM_BUFFER_SIZE,
976d5ac70f0Sopenharmony_ci					2 * dshare->slave_period_size, dshare->slave_buffer_size);
977d5ac70f0Sopenharmony_ci		if (err < 0)
978d5ac70f0Sopenharmony_ci			return err;
979d5ac70f0Sopenharmony_ci		if (dshare->var_periodsize) {
980d5ac70f0Sopenharmony_ci			/* more tolerant settings... */
981d5ac70f0Sopenharmony_ci			if (dshare->shmptr->hw.buffer_size.max / 2 > period_size.max) {
982d5ac70f0Sopenharmony_ci				period_size.max = dshare->shmptr->hw.buffer_size.max / 2;
983d5ac70f0Sopenharmony_ci				period_size.openmax = dshare->shmptr->hw.buffer_size.openmax;
984d5ac70f0Sopenharmony_ci			}
985d5ac70f0Sopenharmony_ci			if (dshare->shmptr->hw.buffer_time.max / 2 > period_time.max) {
986d5ac70f0Sopenharmony_ci				period_time.max = dshare->shmptr->hw.buffer_time.max / 2;
987d5ac70f0Sopenharmony_ci				period_time.openmax = dshare->shmptr->hw.buffer_time.openmax;
988d5ac70f0Sopenharmony_ci			}
989d5ac70f0Sopenharmony_ci		}
990d5ac70f0Sopenharmony_ci
991d5ac70f0Sopenharmony_ci		err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE,
992d5ac70f0Sopenharmony_ci						   &period_size);
993d5ac70f0Sopenharmony_ci		if (err < 0)
994d5ac70f0Sopenharmony_ci			return err;
995d5ac70f0Sopenharmony_ci		err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME,
996d5ac70f0Sopenharmony_ci						   &period_time);
997d5ac70f0Sopenharmony_ci		if (err < 0)
998d5ac70f0Sopenharmony_ci			return err;
999d5ac70f0Sopenharmony_ci		do {
1000d5ac70f0Sopenharmony_ci			changed = 0;
1001d5ac70f0Sopenharmony_ci			err = hw_param_interval_refine_minmax(params, SND_PCM_HW_PARAM_PERIODS,
1002d5ac70f0Sopenharmony_ci							      2, max_periods);
1003d5ac70f0Sopenharmony_ci			if (err < 0)
1004d5ac70f0Sopenharmony_ci				return err;
1005d5ac70f0Sopenharmony_ci			changed |= err;
1006d5ac70f0Sopenharmony_ci			err = snd_pcm_hw_refine_soft(pcm, params);
1007d5ac70f0Sopenharmony_ci			if (err < 0)
1008d5ac70f0Sopenharmony_ci				return err;
1009d5ac70f0Sopenharmony_ci			changed |= err;
1010d5ac70f0Sopenharmony_ci			err = snd_interval_step(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE),
1011d5ac70f0Sopenharmony_ci								0, dshare->slave_period_size);
1012d5ac70f0Sopenharmony_ci			if (err < 0)
1013d5ac70f0Sopenharmony_ci				return err;
1014d5ac70f0Sopenharmony_ci			changed |= err;
1015d5ac70f0Sopenharmony_ci			if (err)
1016d5ac70f0Sopenharmony_ci				params->rmask |= (1 << SND_PCM_HW_PARAM_PERIOD_SIZE);
1017d5ac70f0Sopenharmony_ci		} while (changed);
1018d5ac70f0Sopenharmony_ci	}
1019d5ac70f0Sopenharmony_ci	dshare->timer_ticks = hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE)->max / dshare->slave_period_size;
1020d5ac70f0Sopenharmony_ci	params->info = dshare->shmptr->s.info;
1021d5ac70f0Sopenharmony_ci#ifdef REFINE_DEBUG
1022d5ac70f0Sopenharmony_ci	snd_output_puts(log, "DMIX REFINE (end):\n");
1023d5ac70f0Sopenharmony_ci	snd_pcm_hw_params_dump(params, log);
1024d5ac70f0Sopenharmony_ci	snd_output_close(log);
1025d5ac70f0Sopenharmony_ci#endif
1026d5ac70f0Sopenharmony_ci	return 0;
1027d5ac70f0Sopenharmony_ci}
1028d5ac70f0Sopenharmony_ci
1029d5ac70f0Sopenharmony_ciint snd_pcm_direct_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
1030d5ac70f0Sopenharmony_ci{
1031d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dmix = pcm->private_data;
1032d5ac70f0Sopenharmony_ci
1033d5ac70f0Sopenharmony_ci	params->info = dmix->shmptr->s.info;
1034d5ac70f0Sopenharmony_ci	params->rate_num = dmix->shmptr->s.rate;
1035d5ac70f0Sopenharmony_ci	params->rate_den = 1;
1036d5ac70f0Sopenharmony_ci	params->fifo_size = 0;
1037d5ac70f0Sopenharmony_ci	params->msbits = dmix->shmptr->s.msbits;
1038d5ac70f0Sopenharmony_ci	return 0;
1039d5ac70f0Sopenharmony_ci}
1040d5ac70f0Sopenharmony_ci
1041d5ac70f0Sopenharmony_ciint snd_pcm_direct_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
1042d5ac70f0Sopenharmony_ci{
1043d5ac70f0Sopenharmony_ci	/* values are cached in the pcm structure */
1044d5ac70f0Sopenharmony_ci	return 0;
1045d5ac70f0Sopenharmony_ci}
1046d5ac70f0Sopenharmony_ci
1047d5ac70f0Sopenharmony_ciint snd_pcm_direct_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
1048d5ac70f0Sopenharmony_ci{
1049d5ac70f0Sopenharmony_ci	if (params->tstamp_type != pcm->tstamp_type)
1050d5ac70f0Sopenharmony_ci		return -EINVAL;
1051d5ac70f0Sopenharmony_ci
1052d5ac70f0Sopenharmony_ci	/* values are cached in the pcm structure */
1053d5ac70f0Sopenharmony_ci	return 0;
1054d5ac70f0Sopenharmony_ci}
1055d5ac70f0Sopenharmony_ci
1056d5ac70f0Sopenharmony_ciint snd_pcm_direct_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
1057d5ac70f0Sopenharmony_ci{
1058d5ac70f0Sopenharmony_ci        return snd_pcm_channel_info_shm(pcm, info, -1);
1059d5ac70f0Sopenharmony_ci}
1060d5ac70f0Sopenharmony_ci
1061d5ac70f0Sopenharmony_ciint snd_pcm_direct_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
1062d5ac70f0Sopenharmony_ci{
1063d5ac70f0Sopenharmony_ci	return 0;
1064d5ac70f0Sopenharmony_ci}
1065d5ac70f0Sopenharmony_ci
1066d5ac70f0Sopenharmony_ciint snd_pcm_direct_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
1067d5ac70f0Sopenharmony_ci{
1068d5ac70f0Sopenharmony_ci	return 0;
1069d5ac70f0Sopenharmony_ci}
1070d5ac70f0Sopenharmony_ci
1071d5ac70f0Sopenharmony_cisnd_pcm_chmap_query_t **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm)
1072d5ac70f0Sopenharmony_ci{
1073d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dmix = pcm->private_data;
1074d5ac70f0Sopenharmony_ci	snd_pcm_chmap_query_t **smaps, **maps;
1075d5ac70f0Sopenharmony_ci	unsigned int i, j;
1076d5ac70f0Sopenharmony_ci
1077d5ac70f0Sopenharmony_ci	if (dmix->bindings == NULL)
1078d5ac70f0Sopenharmony_ci		return snd_pcm_query_chmaps(dmix->spcm);
1079d5ac70f0Sopenharmony_ci
1080d5ac70f0Sopenharmony_ci	maps = calloc(2, sizeof(*maps));
1081d5ac70f0Sopenharmony_ci	if (!maps)
1082d5ac70f0Sopenharmony_ci		return NULL;
1083d5ac70f0Sopenharmony_ci	maps[0] = calloc(dmix->channels + 2, sizeof(int *));
1084d5ac70f0Sopenharmony_ci	if (!maps[0]) {
1085d5ac70f0Sopenharmony_ci		free(maps);
1086d5ac70f0Sopenharmony_ci		return NULL;
1087d5ac70f0Sopenharmony_ci	}
1088d5ac70f0Sopenharmony_ci	smaps = snd_pcm_query_chmaps(dmix->spcm);
1089d5ac70f0Sopenharmony_ci	if (smaps == NULL) {
1090d5ac70f0Sopenharmony_ci		snd_pcm_free_chmaps(maps);
1091d5ac70f0Sopenharmony_ci		return NULL;
1092d5ac70f0Sopenharmony_ci	}
1093d5ac70f0Sopenharmony_ci	maps[0]->type = SND_CHMAP_TYPE_FIXED;
1094d5ac70f0Sopenharmony_ci	maps[0]->map.channels = dmix->channels;
1095d5ac70f0Sopenharmony_ci	for (i = 0; i < dmix->channels; i++) {
1096d5ac70f0Sopenharmony_ci		j = dmix->bindings[i];
1097d5ac70f0Sopenharmony_ci		if (j == UINT_MAX || smaps[0]->map.channels < j)
1098d5ac70f0Sopenharmony_ci			continue;
1099d5ac70f0Sopenharmony_ci		maps[0]->map.pos[i] = smaps[0]->map.pos[j];
1100d5ac70f0Sopenharmony_ci	}
1101d5ac70f0Sopenharmony_ci	return maps;
1102d5ac70f0Sopenharmony_ci}
1103d5ac70f0Sopenharmony_ci
1104d5ac70f0Sopenharmony_cisnd_pcm_chmap_t *snd_pcm_direct_get_chmap(snd_pcm_t *pcm)
1105d5ac70f0Sopenharmony_ci{
1106d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dmix = pcm->private_data;
1107d5ac70f0Sopenharmony_ci	return snd_pcm_get_chmap(dmix->spcm);
1108d5ac70f0Sopenharmony_ci}
1109d5ac70f0Sopenharmony_ci
1110d5ac70f0Sopenharmony_ciint snd_pcm_direct_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
1111d5ac70f0Sopenharmony_ci{
1112d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dmix = pcm->private_data;
1113d5ac70f0Sopenharmony_ci	return snd_pcm_set_chmap(dmix->spcm, map);
1114d5ac70f0Sopenharmony_ci}
1115d5ac70f0Sopenharmony_ci
1116d5ac70f0Sopenharmony_ciint snd_pcm_direct_prepare(snd_pcm_t *pcm)
1117d5ac70f0Sopenharmony_ci{
1118d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dmix = pcm->private_data;
1119d5ac70f0Sopenharmony_ci	int err;
1120d5ac70f0Sopenharmony_ci
1121d5ac70f0Sopenharmony_ci	switch (snd_pcm_state(dmix->spcm)) {
1122d5ac70f0Sopenharmony_ci	case SND_PCM_STATE_SETUP:
1123d5ac70f0Sopenharmony_ci	case SND_PCM_STATE_XRUN:
1124d5ac70f0Sopenharmony_ci	case SND_PCM_STATE_SUSPENDED:
1125d5ac70f0Sopenharmony_ci		err = snd_pcm_prepare(dmix->spcm);
1126d5ac70f0Sopenharmony_ci		if (err < 0)
1127d5ac70f0Sopenharmony_ci			return err;
1128d5ac70f0Sopenharmony_ci		snd_pcm_start(dmix->spcm);
1129d5ac70f0Sopenharmony_ci		break;
1130d5ac70f0Sopenharmony_ci	case SND_PCM_STATE_OPEN:
1131d5ac70f0Sopenharmony_ci	case SND_PCM_STATE_DISCONNECTED:
1132d5ac70f0Sopenharmony_ci		return -EBADFD;
1133d5ac70f0Sopenharmony_ci	default:
1134d5ac70f0Sopenharmony_ci		break;
1135d5ac70f0Sopenharmony_ci	}
1136d5ac70f0Sopenharmony_ci	snd_pcm_direct_check_interleave(dmix, pcm);
1137d5ac70f0Sopenharmony_ci	dmix->state = SND_PCM_STATE_PREPARED;
1138d5ac70f0Sopenharmony_ci	dmix->appl_ptr = dmix->last_appl_ptr = 0;
1139d5ac70f0Sopenharmony_ci	dmix->hw_ptr = 0;
1140d5ac70f0Sopenharmony_ci	return snd_pcm_direct_set_timer_params(dmix);
1141d5ac70f0Sopenharmony_ci}
1142d5ac70f0Sopenharmony_ci
1143d5ac70f0Sopenharmony_ciint snd_pcm_direct_resume(snd_pcm_t *pcm)
1144d5ac70f0Sopenharmony_ci{
1145d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dmix = pcm->private_data;
1146d5ac70f0Sopenharmony_ci	int err;
1147d5ac70f0Sopenharmony_ci
1148d5ac70f0Sopenharmony_ci	err = snd_pcm_direct_slave_recover(dmix);
1149d5ac70f0Sopenharmony_ci	return err < 0 ? err : -ENOSYS;
1150d5ac70f0Sopenharmony_ci}
1151d5ac70f0Sopenharmony_ci
1152d5ac70f0Sopenharmony_ci#define COPY_SLAVE(field) (dmix->shmptr->s.field = spcm->field)
1153d5ac70f0Sopenharmony_ci
1154d5ac70f0Sopenharmony_ci/* copy the slave setting */
1155d5ac70f0Sopenharmony_cistatic void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm)
1156d5ac70f0Sopenharmony_ci{
1157d5ac70f0Sopenharmony_ci	spcm->info &= ~SND_PCM_INFO_PAUSE;
1158d5ac70f0Sopenharmony_ci
1159d5ac70f0Sopenharmony_ci	COPY_SLAVE(access);
1160d5ac70f0Sopenharmony_ci	COPY_SLAVE(format);
1161d5ac70f0Sopenharmony_ci	COPY_SLAVE(subformat);
1162d5ac70f0Sopenharmony_ci	COPY_SLAVE(channels);
1163d5ac70f0Sopenharmony_ci	COPY_SLAVE(rate);
1164d5ac70f0Sopenharmony_ci	COPY_SLAVE(period_size);
1165d5ac70f0Sopenharmony_ci	COPY_SLAVE(period_time);
1166d5ac70f0Sopenharmony_ci	COPY_SLAVE(periods);
1167d5ac70f0Sopenharmony_ci	COPY_SLAVE(tstamp_mode);
1168d5ac70f0Sopenharmony_ci	COPY_SLAVE(tstamp_type);
1169d5ac70f0Sopenharmony_ci	COPY_SLAVE(period_step);
1170d5ac70f0Sopenharmony_ci	COPY_SLAVE(avail_min);
1171d5ac70f0Sopenharmony_ci	COPY_SLAVE(start_threshold);
1172d5ac70f0Sopenharmony_ci	COPY_SLAVE(stop_threshold);
1173d5ac70f0Sopenharmony_ci	COPY_SLAVE(silence_threshold);
1174d5ac70f0Sopenharmony_ci	COPY_SLAVE(silence_size);
1175d5ac70f0Sopenharmony_ci	COPY_SLAVE(boundary);
1176d5ac70f0Sopenharmony_ci	COPY_SLAVE(info);
1177d5ac70f0Sopenharmony_ci	COPY_SLAVE(msbits);
1178d5ac70f0Sopenharmony_ci	COPY_SLAVE(rate_num);
1179d5ac70f0Sopenharmony_ci	COPY_SLAVE(rate_den);
1180d5ac70f0Sopenharmony_ci	COPY_SLAVE(hw_flags);
1181d5ac70f0Sopenharmony_ci	COPY_SLAVE(fifo_size);
1182d5ac70f0Sopenharmony_ci	COPY_SLAVE(buffer_size);
1183d5ac70f0Sopenharmony_ci	COPY_SLAVE(buffer_time);
1184d5ac70f0Sopenharmony_ci	COPY_SLAVE(sample_bits);
1185d5ac70f0Sopenharmony_ci	COPY_SLAVE(frame_bits);
1186d5ac70f0Sopenharmony_ci
1187d5ac70f0Sopenharmony_ci	dmix->shmptr->s.info &= ~SND_PCM_INFO_RESUME;
1188d5ac70f0Sopenharmony_ci}
1189d5ac70f0Sopenharmony_ci
1190d5ac70f0Sopenharmony_ci#undef COPY_SLAVE
1191d5ac70f0Sopenharmony_ci
1192d5ac70f0Sopenharmony_ci/*
1193d5ac70f0Sopenharmony_ci * this function initializes hardware and starts playback operation with
1194d5ac70f0Sopenharmony_ci * no stop threshold (it operates all time without xrun checking)
1195d5ac70f0Sopenharmony_ci * also, the driver silences the unused ring buffer areas for us
1196d5ac70f0Sopenharmony_ci */
1197d5ac70f0Sopenharmony_ciint snd_pcm_direct_initialize_slave(snd_pcm_direct_t *dmix, snd_pcm_t *spcm, struct slave_params *params)
1198d5ac70f0Sopenharmony_ci{
1199d5ac70f0Sopenharmony_ci	snd_pcm_hw_params_t hw_params = {0};
1200d5ac70f0Sopenharmony_ci	snd_pcm_sw_params_t sw_params = {0};
1201d5ac70f0Sopenharmony_ci	int ret, buffer_is_not_initialized;
1202d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t boundary;
1203d5ac70f0Sopenharmony_ci	struct pollfd fd;
1204d5ac70f0Sopenharmony_ci	int loops = 10;
1205d5ac70f0Sopenharmony_ci
1206d5ac70f0Sopenharmony_ci      __again:
1207d5ac70f0Sopenharmony_ci      	if (loops-- <= 0) {
1208d5ac70f0Sopenharmony_ci      		SNDERR("unable to find a valid configuration for slave");
1209d5ac70f0Sopenharmony_ci      		return -EINVAL;
1210d5ac70f0Sopenharmony_ci      	}
1211d5ac70f0Sopenharmony_ci	ret = snd_pcm_hw_params_any(spcm, &hw_params);
1212d5ac70f0Sopenharmony_ci	if (ret < 0) {
1213d5ac70f0Sopenharmony_ci		SNDERR("snd_pcm_hw_params_any failed");
1214d5ac70f0Sopenharmony_ci		return ret;
1215d5ac70f0Sopenharmony_ci	}
1216d5ac70f0Sopenharmony_ci	ret = snd_pcm_hw_params_set_access(spcm, &hw_params,
1217d5ac70f0Sopenharmony_ci					   SND_PCM_ACCESS_MMAP_INTERLEAVED);
1218d5ac70f0Sopenharmony_ci	if (ret < 0) {
1219d5ac70f0Sopenharmony_ci		ret = snd_pcm_hw_params_set_access(spcm, &hw_params,
1220d5ac70f0Sopenharmony_ci					SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
1221d5ac70f0Sopenharmony_ci		if (ret < 0) {
1222d5ac70f0Sopenharmony_ci			SNDERR("slave plugin does not support mmap interleaved or mmap noninterleaved access");
1223d5ac70f0Sopenharmony_ci			return ret;
1224d5ac70f0Sopenharmony_ci		}
1225d5ac70f0Sopenharmony_ci	}
1226d5ac70f0Sopenharmony_ci	if (params->format == SND_PCM_FORMAT_UNKNOWN)
1227d5ac70f0Sopenharmony_ci		ret = -EINVAL;
1228d5ac70f0Sopenharmony_ci	else
1229d5ac70f0Sopenharmony_ci		ret = snd_pcm_hw_params_set_format(spcm, &hw_params,
1230d5ac70f0Sopenharmony_ci						   params->format);
1231d5ac70f0Sopenharmony_ci	if (ret < 0) {
1232d5ac70f0Sopenharmony_ci		static const snd_pcm_format_t dmix_formats[] = {
1233d5ac70f0Sopenharmony_ci			SND_PCM_FORMAT_S32,
1234d5ac70f0Sopenharmony_ci			SND_PCM_FORMAT_S32 ^ SND_PCM_FORMAT_S32_LE ^
1235d5ac70f0Sopenharmony_ci							SND_PCM_FORMAT_S32_BE,
1236d5ac70f0Sopenharmony_ci			SND_PCM_FORMAT_S16,
1237d5ac70f0Sopenharmony_ci			SND_PCM_FORMAT_S16 ^ SND_PCM_FORMAT_S16_LE ^
1238d5ac70f0Sopenharmony_ci							SND_PCM_FORMAT_S16_BE,
1239d5ac70f0Sopenharmony_ci			SND_PCM_FORMAT_S24_LE,
1240d5ac70f0Sopenharmony_ci			SND_PCM_FORMAT_S24_3LE,
1241d5ac70f0Sopenharmony_ci			SND_PCM_FORMAT_U8,
1242d5ac70f0Sopenharmony_ci		};
1243d5ac70f0Sopenharmony_ci		snd_pcm_format_t format;
1244d5ac70f0Sopenharmony_ci		unsigned int i;
1245d5ac70f0Sopenharmony_ci
1246d5ac70f0Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(dmix_formats); ++i) {
1247d5ac70f0Sopenharmony_ci			format = dmix_formats[i];
1248d5ac70f0Sopenharmony_ci			ret = snd_pcm_hw_params_set_format(spcm, &hw_params,
1249d5ac70f0Sopenharmony_ci							   format);
1250d5ac70f0Sopenharmony_ci			if (ret >= 0)
1251d5ac70f0Sopenharmony_ci				break;
1252d5ac70f0Sopenharmony_ci		}
1253d5ac70f0Sopenharmony_ci		if (ret < 0 && dmix->type != SND_PCM_TYPE_DMIX) {
1254d5ac70f0Sopenharmony_ci			/* TODO: try to choose a good format */
1255d5ac70f0Sopenharmony_ci			ret = INTERNAL(snd_pcm_hw_params_set_format_first)(spcm,
1256d5ac70f0Sopenharmony_ci							&hw_params, &format);
1257d5ac70f0Sopenharmony_ci		}
1258d5ac70f0Sopenharmony_ci		if (ret < 0) {
1259d5ac70f0Sopenharmony_ci			SNDERR("requested or auto-format is not available");
1260d5ac70f0Sopenharmony_ci			return ret;
1261d5ac70f0Sopenharmony_ci		}
1262d5ac70f0Sopenharmony_ci		params->format = format;
1263d5ac70f0Sopenharmony_ci	}
1264d5ac70f0Sopenharmony_ci	ret = INTERNAL(snd_pcm_hw_params_set_channels_near)(spcm, &hw_params,
1265d5ac70f0Sopenharmony_ci					(unsigned int *)&params->channels);
1266d5ac70f0Sopenharmony_ci	if (ret < 0) {
1267d5ac70f0Sopenharmony_ci		SNDERR("requested count of channels is not available");
1268d5ac70f0Sopenharmony_ci		return ret;
1269d5ac70f0Sopenharmony_ci	}
1270d5ac70f0Sopenharmony_ci	ret = INTERNAL(snd_pcm_hw_params_set_rate_near)(spcm, &hw_params,
1271d5ac70f0Sopenharmony_ci					(unsigned int *)&params->rate, 0);
1272d5ac70f0Sopenharmony_ci	if (ret < 0) {
1273d5ac70f0Sopenharmony_ci		SNDERR("requested rate is not available");
1274d5ac70f0Sopenharmony_ci		return ret;
1275d5ac70f0Sopenharmony_ci	}
1276d5ac70f0Sopenharmony_ci
1277d5ac70f0Sopenharmony_ci	buffer_is_not_initialized = 0;
1278d5ac70f0Sopenharmony_ci	if (params->buffer_time > 0) {
1279d5ac70f0Sopenharmony_ci		ret = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(spcm,
1280d5ac70f0Sopenharmony_ci			&hw_params, (unsigned int *)&params->buffer_time, 0);
1281d5ac70f0Sopenharmony_ci		if (ret < 0) {
1282d5ac70f0Sopenharmony_ci			SNDERR("unable to set buffer time");
1283d5ac70f0Sopenharmony_ci			return ret;
1284d5ac70f0Sopenharmony_ci		}
1285d5ac70f0Sopenharmony_ci	} else if (params->buffer_size > 0) {
1286d5ac70f0Sopenharmony_ci		ret = INTERNAL(snd_pcm_hw_params_set_buffer_size_near)(spcm,
1287d5ac70f0Sopenharmony_ci			&hw_params, (snd_pcm_uframes_t *)&params->buffer_size);
1288d5ac70f0Sopenharmony_ci		if (ret < 0) {
1289d5ac70f0Sopenharmony_ci			SNDERR("unable to set buffer size");
1290d5ac70f0Sopenharmony_ci			return ret;
1291d5ac70f0Sopenharmony_ci		}
1292d5ac70f0Sopenharmony_ci	} else {
1293d5ac70f0Sopenharmony_ci		buffer_is_not_initialized = 1;
1294d5ac70f0Sopenharmony_ci	}
1295d5ac70f0Sopenharmony_ci
1296d5ac70f0Sopenharmony_ci	if (params->period_time > 0) {
1297d5ac70f0Sopenharmony_ci		ret = INTERNAL(snd_pcm_hw_params_set_period_time_near)(spcm,
1298d5ac70f0Sopenharmony_ci			&hw_params, (unsigned int *)&params->period_time, 0);
1299d5ac70f0Sopenharmony_ci		if (ret < 0) {
1300d5ac70f0Sopenharmony_ci			SNDERR("unable to set period_time");
1301d5ac70f0Sopenharmony_ci			return ret;
1302d5ac70f0Sopenharmony_ci		}
1303d5ac70f0Sopenharmony_ci	} else if (params->period_size > 0) {
1304d5ac70f0Sopenharmony_ci		ret = INTERNAL(snd_pcm_hw_params_set_period_size_near)(spcm,
1305d5ac70f0Sopenharmony_ci			&hw_params, (snd_pcm_uframes_t *)&params->period_size,
1306d5ac70f0Sopenharmony_ci			0);
1307d5ac70f0Sopenharmony_ci		if (ret < 0) {
1308d5ac70f0Sopenharmony_ci			SNDERR("unable to set period_size");
1309d5ac70f0Sopenharmony_ci			return ret;
1310d5ac70f0Sopenharmony_ci		}
1311d5ac70f0Sopenharmony_ci	}
1312d5ac70f0Sopenharmony_ci
1313d5ac70f0Sopenharmony_ci	if (buffer_is_not_initialized && params->periods > 0) {
1314d5ac70f0Sopenharmony_ci		unsigned int periods = params->periods;
1315d5ac70f0Sopenharmony_ci		ret = INTERNAL(snd_pcm_hw_params_set_periods_near)(spcm,
1316d5ac70f0Sopenharmony_ci					&hw_params, &params->periods, 0);
1317d5ac70f0Sopenharmony_ci		if (ret < 0) {
1318d5ac70f0Sopenharmony_ci			SNDERR("unable to set requested periods");
1319d5ac70f0Sopenharmony_ci			return ret;
1320d5ac70f0Sopenharmony_ci		}
1321d5ac70f0Sopenharmony_ci		if (params->periods == 1) {
1322d5ac70f0Sopenharmony_ci			params->periods = periods;
1323d5ac70f0Sopenharmony_ci			if (params->period_time > 0) {
1324d5ac70f0Sopenharmony_ci				params->period_time /= 2;
1325d5ac70f0Sopenharmony_ci				goto __again;
1326d5ac70f0Sopenharmony_ci			} else if (params->period_size > 0) {
1327d5ac70f0Sopenharmony_ci				params->period_size /= 2;
1328d5ac70f0Sopenharmony_ci				goto __again;
1329d5ac70f0Sopenharmony_ci			}
1330d5ac70f0Sopenharmony_ci			SNDERR("unable to use stream with periods == 1");
1331d5ac70f0Sopenharmony_ci			return ret;
1332d5ac70f0Sopenharmony_ci		}
1333d5ac70f0Sopenharmony_ci	}
1334d5ac70f0Sopenharmony_ci
1335d5ac70f0Sopenharmony_ci	ret = snd_pcm_hw_params(spcm, &hw_params);
1336d5ac70f0Sopenharmony_ci	if (ret < 0) {
1337d5ac70f0Sopenharmony_ci		SNDERR("unable to install hw params");
1338d5ac70f0Sopenharmony_ci		return ret;
1339d5ac70f0Sopenharmony_ci	}
1340d5ac70f0Sopenharmony_ci
1341d5ac70f0Sopenharmony_ci	/* store some hw_params values to shared info */
1342d5ac70f0Sopenharmony_ci	dmix->shmptr->hw.format =
1343d5ac70f0Sopenharmony_ci		snd_mask_value(hw_param_mask(&hw_params,
1344d5ac70f0Sopenharmony_ci					     SND_PCM_HW_PARAM_FORMAT));
1345d5ac70f0Sopenharmony_ci	dmix->shmptr->hw.rate =
1346d5ac70f0Sopenharmony_ci		*hw_param_interval(&hw_params, SND_PCM_HW_PARAM_RATE);
1347d5ac70f0Sopenharmony_ci	dmix->shmptr->hw.buffer_size =
1348d5ac70f0Sopenharmony_ci		*hw_param_interval(&hw_params, SND_PCM_HW_PARAM_BUFFER_SIZE);
1349d5ac70f0Sopenharmony_ci	dmix->shmptr->hw.buffer_time =
1350d5ac70f0Sopenharmony_ci		*hw_param_interval(&hw_params, SND_PCM_HW_PARAM_BUFFER_TIME);
1351d5ac70f0Sopenharmony_ci	dmix->shmptr->hw.period_size =
1352d5ac70f0Sopenharmony_ci		*hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIOD_SIZE);
1353d5ac70f0Sopenharmony_ci	dmix->shmptr->hw.period_time =
1354d5ac70f0Sopenharmony_ci		*hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIOD_TIME);
1355d5ac70f0Sopenharmony_ci	dmix->shmptr->hw.periods =
1356d5ac70f0Sopenharmony_ci		*hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIODS);
1357d5ac70f0Sopenharmony_ci
1358d5ac70f0Sopenharmony_ci
1359d5ac70f0Sopenharmony_ci	ret = snd_pcm_sw_params_current(spcm, &sw_params);
1360d5ac70f0Sopenharmony_ci	if (ret < 0) {
1361d5ac70f0Sopenharmony_ci		SNDERR("unable to get current sw_params");
1362d5ac70f0Sopenharmony_ci		return ret;
1363d5ac70f0Sopenharmony_ci	}
1364d5ac70f0Sopenharmony_ci
1365d5ac70f0Sopenharmony_ci	ret = snd_pcm_sw_params_get_boundary(&sw_params, &boundary);
1366d5ac70f0Sopenharmony_ci	if (ret < 0) {
1367d5ac70f0Sopenharmony_ci		SNDERR("unable to get boundary");
1368d5ac70f0Sopenharmony_ci		return ret;
1369d5ac70f0Sopenharmony_ci	}
1370d5ac70f0Sopenharmony_ci	ret = snd_pcm_sw_params_set_stop_threshold(spcm, &sw_params, boundary);
1371d5ac70f0Sopenharmony_ci	if (ret < 0) {
1372d5ac70f0Sopenharmony_ci		SNDERR("unable to set stop threshold");
1373d5ac70f0Sopenharmony_ci		return ret;
1374d5ac70f0Sopenharmony_ci	}
1375d5ac70f0Sopenharmony_ci
1376d5ac70f0Sopenharmony_ci	/* set timestamp mode to MMAP
1377d5ac70f0Sopenharmony_ci	 * the slave timestamp is copied appropriately in dsnoop/dmix/dshare
1378d5ac70f0Sopenharmony_ci	 * based on the tstamp_mode of each client
1379d5ac70f0Sopenharmony_ci	 */
1380d5ac70f0Sopenharmony_ci	ret = snd_pcm_sw_params_set_tstamp_mode(spcm, &sw_params,
1381d5ac70f0Sopenharmony_ci						SND_PCM_TSTAMP_ENABLE);
1382d5ac70f0Sopenharmony_ci	if (ret < 0) {
1383d5ac70f0Sopenharmony_ci		SNDERR("unable to tstamp mode MMAP");
1384d5ac70f0Sopenharmony_ci		return ret;
1385d5ac70f0Sopenharmony_ci	}
1386d5ac70f0Sopenharmony_ci
1387d5ac70f0Sopenharmony_ci	if (dmix->tstamp_type != -1) {
1388d5ac70f0Sopenharmony_ci		ret = snd_pcm_sw_params_set_tstamp_type(spcm, &sw_params,
1389d5ac70f0Sopenharmony_ci							dmix->tstamp_type);
1390d5ac70f0Sopenharmony_ci		if (ret < 0) {
1391d5ac70f0Sopenharmony_ci			SNDERR("unable to set tstamp type");
1392d5ac70f0Sopenharmony_ci			return ret;
1393d5ac70f0Sopenharmony_ci		}
1394d5ac70f0Sopenharmony_ci	}
1395d5ac70f0Sopenharmony_ci
1396d5ac70f0Sopenharmony_ci	if (dmix->type != SND_PCM_TYPE_DMIX &&
1397d5ac70f0Sopenharmony_ci	    dmix->type != SND_PCM_TYPE_DSHARE)
1398d5ac70f0Sopenharmony_ci		goto __skip_silencing;
1399d5ac70f0Sopenharmony_ci
1400d5ac70f0Sopenharmony_ci	ret = snd_pcm_sw_params_set_silence_threshold(spcm, &sw_params, 0);
1401d5ac70f0Sopenharmony_ci	if (ret < 0) {
1402d5ac70f0Sopenharmony_ci		SNDERR("unable to set silence threshold");
1403d5ac70f0Sopenharmony_ci		return ret;
1404d5ac70f0Sopenharmony_ci	}
1405d5ac70f0Sopenharmony_ci	ret = snd_pcm_sw_params_set_silence_size(spcm, &sw_params, boundary);
1406d5ac70f0Sopenharmony_ci	if (ret < 0) {
1407d5ac70f0Sopenharmony_ci		SNDERR("unable to set silence threshold (please upgrade to 0.9.0rc8+ driver)");
1408d5ac70f0Sopenharmony_ci		return ret;
1409d5ac70f0Sopenharmony_ci	}
1410d5ac70f0Sopenharmony_ci
1411d5ac70f0Sopenharmony_ci      __skip_silencing:
1412d5ac70f0Sopenharmony_ci
1413d5ac70f0Sopenharmony_ci	ret = snd_pcm_sw_params(spcm, &sw_params);
1414d5ac70f0Sopenharmony_ci	if (ret < 0) {
1415d5ac70f0Sopenharmony_ci		SNDERR("unable to install sw params (please upgrade to 0.9.0rc8+ driver)");
1416d5ac70f0Sopenharmony_ci		return ret;
1417d5ac70f0Sopenharmony_ci	}
1418d5ac70f0Sopenharmony_ci
1419d5ac70f0Sopenharmony_ci	if (dmix->type == SND_PCM_TYPE_DSHARE) {
1420d5ac70f0Sopenharmony_ci		const snd_pcm_channel_area_t *dst_areas;
1421d5ac70f0Sopenharmony_ci		dst_areas = snd_pcm_mmap_areas(spcm);
1422d5ac70f0Sopenharmony_ci		snd_pcm_areas_silence(dst_areas, 0, spcm->channels,
1423d5ac70f0Sopenharmony_ci				      spcm->buffer_size, spcm->format);
1424d5ac70f0Sopenharmony_ci	}
1425d5ac70f0Sopenharmony_ci
1426d5ac70f0Sopenharmony_ci	ret = snd_pcm_start(spcm);
1427d5ac70f0Sopenharmony_ci	if (ret < 0) {
1428d5ac70f0Sopenharmony_ci		SNDERR("unable to start PCM stream");
1429d5ac70f0Sopenharmony_ci		return ret;
1430d5ac70f0Sopenharmony_ci	}
1431d5ac70f0Sopenharmony_ci
1432d5ac70f0Sopenharmony_ci	if (snd_pcm_poll_descriptors_count(spcm) != 1) {
1433d5ac70f0Sopenharmony_ci		SNDERR("unable to use hardware pcm with fd more than one!!!");
1434d5ac70f0Sopenharmony_ci		return ret;
1435d5ac70f0Sopenharmony_ci	}
1436d5ac70f0Sopenharmony_ci	snd_pcm_poll_descriptors(spcm, &fd, 1);
1437d5ac70f0Sopenharmony_ci	dmix->hw_fd = fd.fd;
1438d5ac70f0Sopenharmony_ci
1439d5ac70f0Sopenharmony_ci	save_slave_setting(dmix, spcm);
1440d5ac70f0Sopenharmony_ci
1441d5ac70f0Sopenharmony_ci	/* Currently, we assume that each dmix client has the same
1442d5ac70f0Sopenharmony_ci	 * hw_params setting.
1443d5ac70f0Sopenharmony_ci	 * If the arbitrary hw_parmas is supported in future,
1444d5ac70f0Sopenharmony_ci	 * boundary has to be taken from the slave config but
1445d5ac70f0Sopenharmony_ci	 * recalculated for the native boundary size (for 32bit
1446d5ac70f0Sopenharmony_ci	 * emulation on 64bit arch).
1447d5ac70f0Sopenharmony_ci	 */
1448d5ac70f0Sopenharmony_ci	dmix->slave_buffer_size = spcm->buffer_size;
1449d5ac70f0Sopenharmony_ci	dmix->slave_period_size = spcm->period_size;
1450d5ac70f0Sopenharmony_ci	dmix->slave_boundary = spcm->boundary;
1451d5ac70f0Sopenharmony_ci
1452d5ac70f0Sopenharmony_ci	spcm->donot_close = 1;
1453d5ac70f0Sopenharmony_ci
1454d5ac70f0Sopenharmony_ci	{
1455d5ac70f0Sopenharmony_ci		int ver = 0;
1456d5ac70f0Sopenharmony_ci		ioctl(spcm->poll_fd, SNDRV_PCM_IOCTL_PVERSION, &ver);
1457d5ac70f0Sopenharmony_ci		if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 8))
1458d5ac70f0Sopenharmony_ci			dmix->shmptr->use_server = 1;
1459d5ac70f0Sopenharmony_ci	}
1460d5ac70f0Sopenharmony_ci
1461d5ac70f0Sopenharmony_ci	return 0;
1462d5ac70f0Sopenharmony_ci}
1463d5ac70f0Sopenharmony_ci
1464d5ac70f0Sopenharmony_ci/*
1465d5ac70f0Sopenharmony_ci * the trick is used here; we cannot use effectively the hardware handle because
1466d5ac70f0Sopenharmony_ci * we cannot drive multiple accesses to appl_ptr; so we use slave timer of given
1467d5ac70f0Sopenharmony_ci * PCM hardware handle; it's not this easy and cheap?
1468d5ac70f0Sopenharmony_ci */
1469d5ac70f0Sopenharmony_ciint snd_pcm_direct_initialize_poll_fd(snd_pcm_direct_t *dmix)
1470d5ac70f0Sopenharmony_ci{
1471d5ac70f0Sopenharmony_ci	int ret;
1472d5ac70f0Sopenharmony_ci	snd_pcm_info_t info = {0};
1473d5ac70f0Sopenharmony_ci	char name[128];
1474d5ac70f0Sopenharmony_ci	int capture = dmix->type == SND_PCM_TYPE_DSNOOP ? 1 : 0;
1475d5ac70f0Sopenharmony_ci
1476d5ac70f0Sopenharmony_ci	dmix->tread = 1;
1477d5ac70f0Sopenharmony_ci	dmix->timer_need_poll = 0;
1478d5ac70f0Sopenharmony_ci	dmix->timer_ticks = 1;
1479d5ac70f0Sopenharmony_ci	ret = snd_pcm_info(dmix->spcm, &info);
1480d5ac70f0Sopenharmony_ci	if (ret < 0) {
1481d5ac70f0Sopenharmony_ci		SNDERR("unable to info for slave pcm");
1482d5ac70f0Sopenharmony_ci		return ret;
1483d5ac70f0Sopenharmony_ci	}
1484d5ac70f0Sopenharmony_ci	sprintf(name, "hw:CLASS=%i,SCLASS=0,CARD=%i,DEV=%i,SUBDEV=%i",
1485d5ac70f0Sopenharmony_ci		(int)SND_TIMER_CLASS_PCM,
1486d5ac70f0Sopenharmony_ci		snd_pcm_info_get_card(&info),
1487d5ac70f0Sopenharmony_ci		snd_pcm_info_get_device(&info),
1488d5ac70f0Sopenharmony_ci		snd_pcm_info_get_subdevice(&info) * 2 + capture);
1489d5ac70f0Sopenharmony_ci	ret = snd_timer_open(&dmix->timer, name,
1490d5ac70f0Sopenharmony_ci			     SND_TIMER_OPEN_NONBLOCK | SND_TIMER_OPEN_TREAD);
1491d5ac70f0Sopenharmony_ci	if (ret < 0) {
1492d5ac70f0Sopenharmony_ci		dmix->tread = 0;
1493d5ac70f0Sopenharmony_ci		ret = snd_timer_open(&dmix->timer, name,
1494d5ac70f0Sopenharmony_ci				     SND_TIMER_OPEN_NONBLOCK);
1495d5ac70f0Sopenharmony_ci		if (ret < 0) {
1496d5ac70f0Sopenharmony_ci			SNDERR("unable to open timer '%s'", name);
1497d5ac70f0Sopenharmony_ci			return ret;
1498d5ac70f0Sopenharmony_ci		}
1499d5ac70f0Sopenharmony_ci	}
1500d5ac70f0Sopenharmony_ci
1501d5ac70f0Sopenharmony_ci	if (snd_timer_poll_descriptors_count(dmix->timer) != 1) {
1502d5ac70f0Sopenharmony_ci		SNDERR("unable to use timer '%s' with more than one fd!", name);
1503d5ac70f0Sopenharmony_ci		return ret;
1504d5ac70f0Sopenharmony_ci	}
1505d5ac70f0Sopenharmony_ci	snd_timer_poll_descriptors(dmix->timer, &dmix->timer_fd, 1);
1506d5ac70f0Sopenharmony_ci	dmix->poll_fd = dmix->timer_fd.fd;
1507d5ac70f0Sopenharmony_ci
1508d5ac70f0Sopenharmony_ci	dmix->timer_events = (1<<SND_TIMER_EVENT_MSUSPEND) |
1509d5ac70f0Sopenharmony_ci			     (1<<SND_TIMER_EVENT_MRESUME) |
1510d5ac70f0Sopenharmony_ci			     (1<<SND_TIMER_EVENT_MSTOP) |
1511d5ac70f0Sopenharmony_ci			     (1<<SND_TIMER_EVENT_STOP);
1512d5ac70f0Sopenharmony_ci
1513d5ac70f0Sopenharmony_ci	/*
1514d5ac70f0Sopenharmony_ci	 * Some hacks for older kernel drivers
1515d5ac70f0Sopenharmony_ci	 */
1516d5ac70f0Sopenharmony_ci	{
1517d5ac70f0Sopenharmony_ci		int ver = 0;
1518d5ac70f0Sopenharmony_ci		ioctl(dmix->poll_fd, SNDRV_TIMER_IOCTL_PVERSION, &ver);
1519d5ac70f0Sopenharmony_ci		/* In older versions, check via poll before read() is needed
1520d5ac70f0Sopenharmony_ci		 * because of the confliction between TIMER_START and
1521d5ac70f0Sopenharmony_ci		 * FIONBIO ioctls.
1522d5ac70f0Sopenharmony_ci		 */
1523d5ac70f0Sopenharmony_ci		if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 4))
1524d5ac70f0Sopenharmony_ci			dmix->timer_need_poll = 1;
1525d5ac70f0Sopenharmony_ci		/*
1526d5ac70f0Sopenharmony_ci		 * In older versions, timer uses pause events instead
1527d5ac70f0Sopenharmony_ci		 * suspend/resume events.
1528d5ac70f0Sopenharmony_ci		 */
1529d5ac70f0Sopenharmony_ci		if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 5)) {
1530d5ac70f0Sopenharmony_ci			dmix->timer_events &= ~((1<<SND_TIMER_EVENT_MSUSPEND) |
1531d5ac70f0Sopenharmony_ci						(1<<SND_TIMER_EVENT_MRESUME));
1532d5ac70f0Sopenharmony_ci			dmix->timer_events |= (1<<SND_TIMER_EVENT_MPAUSE) |
1533d5ac70f0Sopenharmony_ci					      (1<<SND_TIMER_EVENT_MCONTINUE);
1534d5ac70f0Sopenharmony_ci		}
1535d5ac70f0Sopenharmony_ci		/* In older versions, use SND_TIMER_EVENT_START too.
1536d5ac70f0Sopenharmony_ci		 */
1537d5ac70f0Sopenharmony_ci		if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 6))
1538d5ac70f0Sopenharmony_ci			dmix->timer_events |= 1<<SND_TIMER_EVENT_START;
1539d5ac70f0Sopenharmony_ci	}
1540d5ac70f0Sopenharmony_ci	return 0;
1541d5ac70f0Sopenharmony_ci}
1542d5ac70f0Sopenharmony_ci
1543d5ac70f0Sopenharmony_cistatic snd_pcm_uframes_t recalc_boundary_size(unsigned long long bsize, snd_pcm_uframes_t buffer_size)
1544d5ac70f0Sopenharmony_ci{
1545d5ac70f0Sopenharmony_ci	if (bsize > LONG_MAX) {
1546d5ac70f0Sopenharmony_ci		bsize = buffer_size;
1547d5ac70f0Sopenharmony_ci		while (bsize * 2 <= LONG_MAX - buffer_size)
1548d5ac70f0Sopenharmony_ci			bsize *= 2;
1549d5ac70f0Sopenharmony_ci	}
1550d5ac70f0Sopenharmony_ci	return (snd_pcm_uframes_t)bsize;
1551d5ac70f0Sopenharmony_ci}
1552d5ac70f0Sopenharmony_ci
1553d5ac70f0Sopenharmony_ci#define COPY_SLAVE(field) (spcm->field = dmix->shmptr->s.field)
1554d5ac70f0Sopenharmony_ci
1555d5ac70f0Sopenharmony_ci/* copy the slave setting */
1556d5ac70f0Sopenharmony_cistatic void copy_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm)
1557d5ac70f0Sopenharmony_ci{
1558d5ac70f0Sopenharmony_ci	COPY_SLAVE(access);
1559d5ac70f0Sopenharmony_ci	COPY_SLAVE(format);
1560d5ac70f0Sopenharmony_ci	COPY_SLAVE(subformat);
1561d5ac70f0Sopenharmony_ci	COPY_SLAVE(channels);
1562d5ac70f0Sopenharmony_ci	COPY_SLAVE(rate);
1563d5ac70f0Sopenharmony_ci	COPY_SLAVE(period_size);
1564d5ac70f0Sopenharmony_ci	COPY_SLAVE(period_time);
1565d5ac70f0Sopenharmony_ci	COPY_SLAVE(periods);
1566d5ac70f0Sopenharmony_ci	COPY_SLAVE(tstamp_mode);
1567d5ac70f0Sopenharmony_ci	COPY_SLAVE(tstamp_type);
1568d5ac70f0Sopenharmony_ci	COPY_SLAVE(period_step);
1569d5ac70f0Sopenharmony_ci	COPY_SLAVE(avail_min);
1570d5ac70f0Sopenharmony_ci	COPY_SLAVE(start_threshold);
1571d5ac70f0Sopenharmony_ci	COPY_SLAVE(stop_threshold);
1572d5ac70f0Sopenharmony_ci	COPY_SLAVE(silence_threshold);
1573d5ac70f0Sopenharmony_ci	COPY_SLAVE(silence_size);
1574d5ac70f0Sopenharmony_ci	COPY_SLAVE(boundary);
1575d5ac70f0Sopenharmony_ci	COPY_SLAVE(info);
1576d5ac70f0Sopenharmony_ci	COPY_SLAVE(msbits);
1577d5ac70f0Sopenharmony_ci	COPY_SLAVE(rate_num);
1578d5ac70f0Sopenharmony_ci	COPY_SLAVE(rate_den);
1579d5ac70f0Sopenharmony_ci	COPY_SLAVE(hw_flags);
1580d5ac70f0Sopenharmony_ci	COPY_SLAVE(fifo_size);
1581d5ac70f0Sopenharmony_ci	COPY_SLAVE(buffer_size);
1582d5ac70f0Sopenharmony_ci	COPY_SLAVE(buffer_time);
1583d5ac70f0Sopenharmony_ci	COPY_SLAVE(sample_bits);
1584d5ac70f0Sopenharmony_ci	COPY_SLAVE(frame_bits);
1585d5ac70f0Sopenharmony_ci
1586d5ac70f0Sopenharmony_ci	spcm->info &= ~SND_PCM_INFO_PAUSE;
1587d5ac70f0Sopenharmony_ci	spcm->boundary = recalc_boundary_size(dmix->shmptr->s.boundary, spcm->buffer_size);
1588d5ac70f0Sopenharmony_ci}
1589d5ac70f0Sopenharmony_ci
1590d5ac70f0Sopenharmony_ci#undef COPY_SLAVE
1591d5ac70f0Sopenharmony_ci
1592d5ac70f0Sopenharmony_ci
1593d5ac70f0Sopenharmony_ci/*
1594d5ac70f0Sopenharmony_ci * open a slave PCM as secondary client (dup'ed fd)
1595d5ac70f0Sopenharmony_ci */
1596d5ac70f0Sopenharmony_ciint snd_pcm_direct_open_secondary_client(snd_pcm_t **spcmp, snd_pcm_direct_t *dmix, const char *client_name)
1597d5ac70f0Sopenharmony_ci{
1598d5ac70f0Sopenharmony_ci	int ret;
1599d5ac70f0Sopenharmony_ci	snd_pcm_t *spcm;
1600d5ac70f0Sopenharmony_ci
1601d5ac70f0Sopenharmony_ci	ret = snd_pcm_hw_open_fd(spcmp, client_name, dmix->hw_fd, 0);
1602d5ac70f0Sopenharmony_ci	if (ret < 0) {
1603d5ac70f0Sopenharmony_ci		SNDERR("unable to open hardware");
1604d5ac70f0Sopenharmony_ci		return ret;
1605d5ac70f0Sopenharmony_ci	}
1606d5ac70f0Sopenharmony_ci
1607d5ac70f0Sopenharmony_ci	spcm = *spcmp;
1608d5ac70f0Sopenharmony_ci	spcm->donot_close = 1;
1609d5ac70f0Sopenharmony_ci	spcm->setup = 1;
1610d5ac70f0Sopenharmony_ci
1611d5ac70f0Sopenharmony_ci	copy_slave_setting(dmix, spcm);
1612d5ac70f0Sopenharmony_ci
1613d5ac70f0Sopenharmony_ci	/* Use the slave setting as SPCM, so far */
1614d5ac70f0Sopenharmony_ci	dmix->slave_buffer_size = spcm->buffer_size;
1615d5ac70f0Sopenharmony_ci	dmix->slave_period_size = dmix->shmptr->s.period_size;
1616d5ac70f0Sopenharmony_ci	dmix->slave_boundary = spcm->boundary;
1617d5ac70f0Sopenharmony_ci	dmix->recoveries = dmix->shmptr->s.recoveries;
1618d5ac70f0Sopenharmony_ci
1619d5ac70f0Sopenharmony_ci	ret = snd_pcm_mmap(spcm);
1620d5ac70f0Sopenharmony_ci	if (ret < 0) {
1621d5ac70f0Sopenharmony_ci		SNDERR("unable to mmap channels");
1622d5ac70f0Sopenharmony_ci		return ret;
1623d5ac70f0Sopenharmony_ci	}
1624d5ac70f0Sopenharmony_ci	return 0;
1625d5ac70f0Sopenharmony_ci}
1626d5ac70f0Sopenharmony_ci
1627d5ac70f0Sopenharmony_ci/*
1628d5ac70f0Sopenharmony_ci * open a slave PCM as secondary client (dup'ed fd)
1629d5ac70f0Sopenharmony_ci */
1630d5ac70f0Sopenharmony_ciint snd_pcm_direct_initialize_secondary_slave(snd_pcm_direct_t *dmix,
1631d5ac70f0Sopenharmony_ci					      snd_pcm_t *spcm,
1632d5ac70f0Sopenharmony_ci					      struct slave_params *params ATTRIBUTE_UNUSED)
1633d5ac70f0Sopenharmony_ci{
1634d5ac70f0Sopenharmony_ci	int ret;
1635d5ac70f0Sopenharmony_ci
1636d5ac70f0Sopenharmony_ci	spcm->donot_close = 1;
1637d5ac70f0Sopenharmony_ci	spcm->setup = 1;
1638d5ac70f0Sopenharmony_ci
1639d5ac70f0Sopenharmony_ci	copy_slave_setting(dmix, spcm);
1640d5ac70f0Sopenharmony_ci
1641d5ac70f0Sopenharmony_ci	/* Use the slave setting as SPCM, so far */
1642d5ac70f0Sopenharmony_ci	dmix->slave_buffer_size = spcm->buffer_size;
1643d5ac70f0Sopenharmony_ci	dmix->slave_period_size = dmix->shmptr->s.period_size;
1644d5ac70f0Sopenharmony_ci	dmix->slave_boundary = spcm->boundary;
1645d5ac70f0Sopenharmony_ci
1646d5ac70f0Sopenharmony_ci	ret = snd_pcm_mmap(spcm);
1647d5ac70f0Sopenharmony_ci	if (ret < 0) {
1648d5ac70f0Sopenharmony_ci		SNDERR("unable to mmap channels");
1649d5ac70f0Sopenharmony_ci		return ret;
1650d5ac70f0Sopenharmony_ci	}
1651d5ac70f0Sopenharmony_ci	return 0;
1652d5ac70f0Sopenharmony_ci}
1653d5ac70f0Sopenharmony_ci
1654d5ac70f0Sopenharmony_ciint snd_pcm_direct_set_timer_params(snd_pcm_direct_t *dmix)
1655d5ac70f0Sopenharmony_ci{
1656d5ac70f0Sopenharmony_ci	snd_timer_params_t params = {0};
1657d5ac70f0Sopenharmony_ci	unsigned int filter;
1658d5ac70f0Sopenharmony_ci	int ret;
1659d5ac70f0Sopenharmony_ci
1660d5ac70f0Sopenharmony_ci	snd_timer_params_set_auto_start(&params, 1);
1661d5ac70f0Sopenharmony_ci	if (dmix->type != SND_PCM_TYPE_DSNOOP)
1662d5ac70f0Sopenharmony_ci		snd_timer_params_set_early_event(&params, 1);
1663d5ac70f0Sopenharmony_ci	snd_timer_params_set_ticks(&params, dmix->timer_ticks);
1664d5ac70f0Sopenharmony_ci	if (dmix->tread) {
1665d5ac70f0Sopenharmony_ci		filter = (1<<SND_TIMER_EVENT_TICK) |
1666d5ac70f0Sopenharmony_ci			 dmix->timer_events;
1667d5ac70f0Sopenharmony_ci		INTERNAL(snd_timer_params_set_filter)(&params, filter);
1668d5ac70f0Sopenharmony_ci	}
1669d5ac70f0Sopenharmony_ci	ret = snd_timer_params(dmix->timer, &params);
1670d5ac70f0Sopenharmony_ci	if (ret < 0) {
1671d5ac70f0Sopenharmony_ci		SNDERR("unable to set timer parameters");
1672d5ac70f0Sopenharmony_ci		return ret;
1673d5ac70f0Sopenharmony_ci	}
1674d5ac70f0Sopenharmony_ci	return 0;
1675d5ac70f0Sopenharmony_ci}
1676d5ac70f0Sopenharmony_ci
1677d5ac70f0Sopenharmony_ci/*
1678d5ac70f0Sopenharmony_ci *  ring buffer operation
1679d5ac70f0Sopenharmony_ci */
1680d5ac70f0Sopenharmony_ciint snd_pcm_direct_check_interleave(snd_pcm_direct_t *dmix, snd_pcm_t *pcm)
1681d5ac70f0Sopenharmony_ci{
1682d5ac70f0Sopenharmony_ci	unsigned int chn, channels;
1683d5ac70f0Sopenharmony_ci	int bits;
1684d5ac70f0Sopenharmony_ci	const snd_pcm_channel_area_t *dst_areas;
1685d5ac70f0Sopenharmony_ci	const snd_pcm_channel_area_t *src_areas;
1686d5ac70f0Sopenharmony_ci
1687d5ac70f0Sopenharmony_ci	bits = snd_pcm_format_physical_width(pcm->format);
1688d5ac70f0Sopenharmony_ci	if ((bits % 8) != 0)
1689d5ac70f0Sopenharmony_ci		goto __nointerleaved;
1690d5ac70f0Sopenharmony_ci	channels = dmix->channels;
1691d5ac70f0Sopenharmony_ci	if (channels != dmix->spcm->channels)
1692d5ac70f0Sopenharmony_ci		goto __nointerleaved;
1693d5ac70f0Sopenharmony_ci	dst_areas = snd_pcm_mmap_areas(dmix->spcm);
1694d5ac70f0Sopenharmony_ci	src_areas = snd_pcm_mmap_areas(pcm);
1695d5ac70f0Sopenharmony_ci	for (chn = 1; chn < channels; chn++) {
1696d5ac70f0Sopenharmony_ci		if (dst_areas[chn-1].addr != dst_areas[chn].addr)
1697d5ac70f0Sopenharmony_ci			goto __nointerleaved;
1698d5ac70f0Sopenharmony_ci		if (src_areas[chn-1].addr != src_areas[chn].addr)
1699d5ac70f0Sopenharmony_ci			goto __nointerleaved;
1700d5ac70f0Sopenharmony_ci	}
1701d5ac70f0Sopenharmony_ci	for (chn = 0; chn < channels; chn++) {
1702d5ac70f0Sopenharmony_ci		if (dmix->bindings && dmix->bindings[chn] != chn)
1703d5ac70f0Sopenharmony_ci			goto __nointerleaved;
1704d5ac70f0Sopenharmony_ci		if (dst_areas[chn].first != chn * bits ||
1705d5ac70f0Sopenharmony_ci		    dst_areas[chn].step != channels * bits)
1706d5ac70f0Sopenharmony_ci			goto __nointerleaved;
1707d5ac70f0Sopenharmony_ci		if (src_areas[chn].first != chn * bits ||
1708d5ac70f0Sopenharmony_ci		    src_areas[chn].step != channels * bits)
1709d5ac70f0Sopenharmony_ci			goto __nointerleaved;
1710d5ac70f0Sopenharmony_ci	}
1711d5ac70f0Sopenharmony_ci	return dmix->interleaved = 1;
1712d5ac70f0Sopenharmony_ci__nointerleaved:
1713d5ac70f0Sopenharmony_ci	return dmix->interleaved = 0;
1714d5ac70f0Sopenharmony_ci}
1715d5ac70f0Sopenharmony_ci
1716d5ac70f0Sopenharmony_ci/*
1717d5ac70f0Sopenharmony_ci * parse the channel map
1718d5ac70f0Sopenharmony_ci * id == client channel
1719d5ac70f0Sopenharmony_ci * value == slave's channel
1720d5ac70f0Sopenharmony_ci */
1721d5ac70f0Sopenharmony_ciint snd_pcm_direct_parse_bindings(snd_pcm_direct_t *dmix,
1722d5ac70f0Sopenharmony_ci				  struct slave_params *params,
1723d5ac70f0Sopenharmony_ci				  snd_config_t *cfg)
1724d5ac70f0Sopenharmony_ci{
1725d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
1726d5ac70f0Sopenharmony_ci	unsigned int chn, chn1, count = 0;
1727d5ac70f0Sopenharmony_ci	unsigned int *bindings;
1728d5ac70f0Sopenharmony_ci	int err;
1729d5ac70f0Sopenharmony_ci
1730d5ac70f0Sopenharmony_ci	dmix->channels = UINT_MAX;
1731d5ac70f0Sopenharmony_ci	if (cfg == NULL)
1732d5ac70f0Sopenharmony_ci		return 0;
1733d5ac70f0Sopenharmony_ci	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1734d5ac70f0Sopenharmony_ci		SNDERR("invalid type for bindings");
1735d5ac70f0Sopenharmony_ci		return -EINVAL;
1736d5ac70f0Sopenharmony_ci	}
1737d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, cfg) {
1738d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
1739d5ac70f0Sopenharmony_ci		const char *id;
1740d5ac70f0Sopenharmony_ci		long cchannel;
1741d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
1742d5ac70f0Sopenharmony_ci			continue;
1743d5ac70f0Sopenharmony_ci		err = safe_strtol(id, &cchannel);
1744d5ac70f0Sopenharmony_ci		if (err < 0 || cchannel < 0) {
1745d5ac70f0Sopenharmony_ci			SNDERR("invalid client channel in binding: %s", id);
1746d5ac70f0Sopenharmony_ci			return -EINVAL;
1747d5ac70f0Sopenharmony_ci		}
1748d5ac70f0Sopenharmony_ci		if ((unsigned)cchannel >= count)
1749d5ac70f0Sopenharmony_ci			count = cchannel + 1;
1750d5ac70f0Sopenharmony_ci	}
1751d5ac70f0Sopenharmony_ci	if (count == 0)
1752d5ac70f0Sopenharmony_ci		return 0;
1753d5ac70f0Sopenharmony_ci	if (count > 1024) {
1754d5ac70f0Sopenharmony_ci		SNDERR("client channel out of range");
1755d5ac70f0Sopenharmony_ci		return -EINVAL;
1756d5ac70f0Sopenharmony_ci	}
1757d5ac70f0Sopenharmony_ci	bindings = malloc(count * sizeof(unsigned int));
1758d5ac70f0Sopenharmony_ci	if (bindings == NULL)
1759d5ac70f0Sopenharmony_ci		return -ENOMEM;
1760d5ac70f0Sopenharmony_ci	for (chn = 0; chn < count; chn++)
1761d5ac70f0Sopenharmony_ci		bindings[chn] = UINT_MAX;		/* don't route */
1762d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, cfg) {
1763d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
1764d5ac70f0Sopenharmony_ci		const char *id;
1765d5ac70f0Sopenharmony_ci		long cchannel, schannel;
1766d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
1767d5ac70f0Sopenharmony_ci			continue;
1768d5ac70f0Sopenharmony_ci		safe_strtol(id, &cchannel);
1769d5ac70f0Sopenharmony_ci		if (snd_config_get_integer(n, &schannel) < 0) {
1770d5ac70f0Sopenharmony_ci			SNDERR("unable to get slave channel (should be integer type) in binding: %s", id);
1771d5ac70f0Sopenharmony_ci			free(bindings);
1772d5ac70f0Sopenharmony_ci			return -EINVAL;
1773d5ac70f0Sopenharmony_ci		}
1774d5ac70f0Sopenharmony_ci		if (schannel < 0 || schannel >= params->channels) {
1775d5ac70f0Sopenharmony_ci			SNDERR("invalid slave channel number %ld in binding to %ld",
1776d5ac70f0Sopenharmony_ci			       schannel, cchannel);
1777d5ac70f0Sopenharmony_ci			free(bindings);
1778d5ac70f0Sopenharmony_ci			return -EINVAL;
1779d5ac70f0Sopenharmony_ci		}
1780d5ac70f0Sopenharmony_ci		bindings[cchannel] = schannel;
1781d5ac70f0Sopenharmony_ci	}
1782d5ac70f0Sopenharmony_ci	if (dmix->type == SND_PCM_TYPE_DSNOOP ||
1783d5ac70f0Sopenharmony_ci	    ! dmix->bindings)
1784d5ac70f0Sopenharmony_ci		goto __skip_same_dst;
1785d5ac70f0Sopenharmony_ci	for (chn = 0; chn < count; chn++) {
1786d5ac70f0Sopenharmony_ci		for (chn1 = 0; chn1 < count; chn1++) {
1787d5ac70f0Sopenharmony_ci			if (chn == chn1)
1788d5ac70f0Sopenharmony_ci				continue;
1789d5ac70f0Sopenharmony_ci			if (bindings[chn] == dmix->bindings[chn1]) {
1790d5ac70f0Sopenharmony_ci				SNDERR("unable to route channels %d,%d to same destination %d", chn, chn1, bindings[chn]);
1791d5ac70f0Sopenharmony_ci				free(bindings);
1792d5ac70f0Sopenharmony_ci				return -EINVAL;
1793d5ac70f0Sopenharmony_ci			}
1794d5ac70f0Sopenharmony_ci		}
1795d5ac70f0Sopenharmony_ci	}
1796d5ac70f0Sopenharmony_ci      __skip_same_dst:
1797d5ac70f0Sopenharmony_ci	dmix->bindings = bindings;
1798d5ac70f0Sopenharmony_ci	dmix->channels = count;
1799d5ac70f0Sopenharmony_ci	return 0;
1800d5ac70f0Sopenharmony_ci}
1801d5ac70f0Sopenharmony_ci
1802d5ac70f0Sopenharmony_ci/*
1803d5ac70f0Sopenharmony_ci * parse slave config and calculate the ipc_key offset
1804d5ac70f0Sopenharmony_ci */
1805d5ac70f0Sopenharmony_ci
1806d5ac70f0Sopenharmony_cistatic int _snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root,
1807d5ac70f0Sopenharmony_ci						snd_config_t *sconf,
1808d5ac70f0Sopenharmony_ci						int direction,
1809d5ac70f0Sopenharmony_ci						int hop)
1810d5ac70f0Sopenharmony_ci{
1811d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
1812d5ac70f0Sopenharmony_ci	snd_config_t *pcm_conf, *pcm_conf2;
1813d5ac70f0Sopenharmony_ci	int err;
1814d5ac70f0Sopenharmony_ci	long card = 0, device = 0, subdevice = 0;
1815d5ac70f0Sopenharmony_ci	const char *str;
1816d5ac70f0Sopenharmony_ci
1817d5ac70f0Sopenharmony_ci	if (snd_config_get_string(sconf, &str) >= 0) {
1818d5ac70f0Sopenharmony_ci		if (hop > SND_CONF_MAX_HOPS) {
1819d5ac70f0Sopenharmony_ci			SNDERR("Too many definition levels (looped?)");
1820d5ac70f0Sopenharmony_ci			return -EINVAL;
1821d5ac70f0Sopenharmony_ci		}
1822d5ac70f0Sopenharmony_ci		err = snd_config_search_definition(root, "pcm", str, &pcm_conf);
1823d5ac70f0Sopenharmony_ci		if (err < 0) {
1824d5ac70f0Sopenharmony_ci			SNDERR("Unknown slave PCM %s", str);
1825d5ac70f0Sopenharmony_ci			return err;
1826d5ac70f0Sopenharmony_ci		}
1827d5ac70f0Sopenharmony_ci		err = _snd_pcm_direct_get_slave_ipc_offset(root, pcm_conf,
1828d5ac70f0Sopenharmony_ci							   direction,
1829d5ac70f0Sopenharmony_ci							   hop + 1);
1830d5ac70f0Sopenharmony_ci		snd_config_delete(pcm_conf);
1831d5ac70f0Sopenharmony_ci		return err;
1832d5ac70f0Sopenharmony_ci	}
1833d5ac70f0Sopenharmony_ci
1834d5ac70f0Sopenharmony_ci#if 0	/* for debug purposes */
1835d5ac70f0Sopenharmony_ci	{
1836d5ac70f0Sopenharmony_ci		snd_output_t *out;
1837d5ac70f0Sopenharmony_ci		snd_output_stdio_attach(&out, stderr, 0);
1838d5ac70f0Sopenharmony_ci		snd_config_save(sconf, out);
1839d5ac70f0Sopenharmony_ci		snd_output_close(out);
1840d5ac70f0Sopenharmony_ci	}
1841d5ac70f0Sopenharmony_ci#endif
1842d5ac70f0Sopenharmony_ci
1843d5ac70f0Sopenharmony_ci	if (snd_config_search(sconf, "slave", &pcm_conf) >= 0) {
1844d5ac70f0Sopenharmony_ci		if (snd_config_search(pcm_conf, "pcm", &pcm_conf) >= 0) {
1845d5ac70f0Sopenharmony_ci			return _snd_pcm_direct_get_slave_ipc_offset(root,
1846d5ac70f0Sopenharmony_ci								   pcm_conf,
1847d5ac70f0Sopenharmony_ci								   direction,
1848d5ac70f0Sopenharmony_ci								   hop + 1);
1849d5ac70f0Sopenharmony_ci		} else {
1850d5ac70f0Sopenharmony_ci			if (snd_config_get_string(pcm_conf, &str) >= 0 &&
1851d5ac70f0Sopenharmony_ci			    snd_config_search_definition(root, "pcm_slave",
1852d5ac70f0Sopenharmony_ci						    str, &pcm_conf) >= 0) {
1853d5ac70f0Sopenharmony_ci				if (snd_config_search(pcm_conf, "pcm",
1854d5ac70f0Sopenharmony_ci							&pcm_conf2) >= 0) {
1855d5ac70f0Sopenharmony_ci					err =
1856d5ac70f0Sopenharmony_ci					 _snd_pcm_direct_get_slave_ipc_offset(
1857d5ac70f0Sopenharmony_ci					     root, pcm_conf2, direction, hop + 1);
1858d5ac70f0Sopenharmony_ci					snd_config_delete(pcm_conf);
1859d5ac70f0Sopenharmony_ci					return err;
1860d5ac70f0Sopenharmony_ci				}
1861d5ac70f0Sopenharmony_ci				snd_config_delete(pcm_conf);
1862d5ac70f0Sopenharmony_ci			}
1863d5ac70f0Sopenharmony_ci		}
1864d5ac70f0Sopenharmony_ci	}
1865d5ac70f0Sopenharmony_ci
1866d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, sconf) {
1867d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
1868d5ac70f0Sopenharmony_ci		const char *id, *str;
1869d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
1870d5ac70f0Sopenharmony_ci			continue;
1871d5ac70f0Sopenharmony_ci		if (strcmp(id, "type") == 0) {
1872d5ac70f0Sopenharmony_ci			err = snd_config_get_string(n, &str);
1873d5ac70f0Sopenharmony_ci			if (err < 0) {
1874d5ac70f0Sopenharmony_ci				SNDERR("Invalid value for PCM type definition");
1875d5ac70f0Sopenharmony_ci				return -EINVAL;
1876d5ac70f0Sopenharmony_ci			}
1877d5ac70f0Sopenharmony_ci			if (strcmp(str, "hw")) {
1878d5ac70f0Sopenharmony_ci				SNDERR("Invalid type '%s' for slave PCM", str);
1879d5ac70f0Sopenharmony_ci				return -EINVAL;
1880d5ac70f0Sopenharmony_ci			}
1881d5ac70f0Sopenharmony_ci			continue;
1882d5ac70f0Sopenharmony_ci		}
1883d5ac70f0Sopenharmony_ci		if (strcmp(id, "card") == 0) {
1884d5ac70f0Sopenharmony_ci			err = snd_config_get_card(n);
1885d5ac70f0Sopenharmony_ci			if (err < 0)
1886d5ac70f0Sopenharmony_ci				return err;
1887d5ac70f0Sopenharmony_ci			card = err;
1888d5ac70f0Sopenharmony_ci			continue;
1889d5ac70f0Sopenharmony_ci		}
1890d5ac70f0Sopenharmony_ci		if (strcmp(id, "device") == 0) {
1891d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &device);
1892d5ac70f0Sopenharmony_ci			if (err < 0) {
1893d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
1894d5ac70f0Sopenharmony_ci				return err;
1895d5ac70f0Sopenharmony_ci			}
1896d5ac70f0Sopenharmony_ci			continue;
1897d5ac70f0Sopenharmony_ci		}
1898d5ac70f0Sopenharmony_ci		if (strcmp(id, "subdevice") == 0) {
1899d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &subdevice);
1900d5ac70f0Sopenharmony_ci			if (err < 0) {
1901d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
1902d5ac70f0Sopenharmony_ci				return err;
1903d5ac70f0Sopenharmony_ci			}
1904d5ac70f0Sopenharmony_ci			continue;
1905d5ac70f0Sopenharmony_ci		}
1906d5ac70f0Sopenharmony_ci	}
1907d5ac70f0Sopenharmony_ci	if (device < 0)
1908d5ac70f0Sopenharmony_ci		device = 0;
1909d5ac70f0Sopenharmony_ci	if (subdevice < 0)
1910d5ac70f0Sopenharmony_ci		subdevice = 0;
1911d5ac70f0Sopenharmony_ci	return (direction << 1) + (device << 2) + (subdevice << 8) + (card << 12);
1912d5ac70f0Sopenharmony_ci}
1913d5ac70f0Sopenharmony_ci
1914d5ac70f0Sopenharmony_cistatic int snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root,
1915d5ac70f0Sopenharmony_ci					snd_config_t *sconf,
1916d5ac70f0Sopenharmony_ci					int direction)
1917d5ac70f0Sopenharmony_ci{
1918d5ac70f0Sopenharmony_ci	return _snd_pcm_direct_get_slave_ipc_offset(root, sconf, direction, 0);
1919d5ac70f0Sopenharmony_ci}
1920d5ac70f0Sopenharmony_ci
1921d5ac70f0Sopenharmony_ciint snd_pcm_direct_parse_open_conf(snd_config_t *root, snd_config_t *conf,
1922d5ac70f0Sopenharmony_ci				   int stream, struct snd_pcm_direct_open_conf *rec)
1923d5ac70f0Sopenharmony_ci{
1924d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
1925d5ac70f0Sopenharmony_ci	int ipc_key_add_uid = 0;
1926d5ac70f0Sopenharmony_ci	snd_config_t *n;
1927d5ac70f0Sopenharmony_ci	int err;
1928d5ac70f0Sopenharmony_ci
1929d5ac70f0Sopenharmony_ci	rec->slave = NULL;
1930d5ac70f0Sopenharmony_ci	rec->bindings = NULL;
1931d5ac70f0Sopenharmony_ci	rec->ipc_key = 0;
1932d5ac70f0Sopenharmony_ci	rec->ipc_perm = 0600;
1933d5ac70f0Sopenharmony_ci	rec->ipc_gid = -1;
1934d5ac70f0Sopenharmony_ci	rec->slowptr = 1;
1935d5ac70f0Sopenharmony_ci	rec->max_periods = 0;
1936d5ac70f0Sopenharmony_ci	rec->var_periodsize = 0;
1937d5ac70f0Sopenharmony_ci#ifdef LOCKLESS_DMIX_DEFAULT
1938d5ac70f0Sopenharmony_ci	rec->direct_memory_access = 1;
1939d5ac70f0Sopenharmony_ci#else
1940d5ac70f0Sopenharmony_ci	rec->direct_memory_access = 0;
1941d5ac70f0Sopenharmony_ci#endif
1942d5ac70f0Sopenharmony_ci	rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_AUTO;
1943d5ac70f0Sopenharmony_ci	rec->tstamp_type = -1;
1944d5ac70f0Sopenharmony_ci
1945d5ac70f0Sopenharmony_ci	/* read defaults */
1946d5ac70f0Sopenharmony_ci	if (snd_config_search(root, "defaults.pcm.dmix_max_periods", &n) >= 0) {
1947d5ac70f0Sopenharmony_ci		long val;
1948d5ac70f0Sopenharmony_ci		err = snd_config_get_integer(n, &val);
1949d5ac70f0Sopenharmony_ci		if (err >= 0)
1950d5ac70f0Sopenharmony_ci			rec->max_periods = val;
1951d5ac70f0Sopenharmony_ci	}
1952d5ac70f0Sopenharmony_ci
1953d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, conf) {
1954d5ac70f0Sopenharmony_ci		const char *id;
1955d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
1956d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
1957d5ac70f0Sopenharmony_ci			continue;
1958d5ac70f0Sopenharmony_ci		if (snd_pcm_conf_generic_id(id))
1959d5ac70f0Sopenharmony_ci			continue;
1960d5ac70f0Sopenharmony_ci		if (strcmp(id, "ipc_key") == 0) {
1961d5ac70f0Sopenharmony_ci			long key;
1962d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &key);
1963d5ac70f0Sopenharmony_ci			if (err < 0) {
1964d5ac70f0Sopenharmony_ci				SNDERR("The field ipc_key must be an integer type");
1965d5ac70f0Sopenharmony_ci
1966d5ac70f0Sopenharmony_ci				return err;
1967d5ac70f0Sopenharmony_ci			}
1968d5ac70f0Sopenharmony_ci			rec->ipc_key = key;
1969d5ac70f0Sopenharmony_ci			continue;
1970d5ac70f0Sopenharmony_ci		}
1971d5ac70f0Sopenharmony_ci		if (strcmp(id, "ipc_perm") == 0) {
1972d5ac70f0Sopenharmony_ci			long perm;
1973d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &perm);
1974d5ac70f0Sopenharmony_ci			if (err < 0) {
1975d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
1976d5ac70f0Sopenharmony_ci				return err;
1977d5ac70f0Sopenharmony_ci			}
1978d5ac70f0Sopenharmony_ci			if ((perm & ~0777) != 0) {
1979d5ac70f0Sopenharmony_ci				SNDERR("The field ipc_perm must be a valid file permission");
1980d5ac70f0Sopenharmony_ci				return -EINVAL;
1981d5ac70f0Sopenharmony_ci			}
1982d5ac70f0Sopenharmony_ci			rec->ipc_perm = perm;
1983d5ac70f0Sopenharmony_ci			continue;
1984d5ac70f0Sopenharmony_ci		}
1985d5ac70f0Sopenharmony_ci		if (strcmp(id, "hw_ptr_alignment") == 0) {
1986d5ac70f0Sopenharmony_ci			const char *str;
1987d5ac70f0Sopenharmony_ci			err = snd_config_get_string(n, &str);
1988d5ac70f0Sopenharmony_ci			if (err < 0) {
1989d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
1990d5ac70f0Sopenharmony_ci				return -EINVAL;
1991d5ac70f0Sopenharmony_ci			}
1992d5ac70f0Sopenharmony_ci			if (strcmp(str, "no") == 0 || strcmp(str, "off") == 0)
1993d5ac70f0Sopenharmony_ci				rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_NO;
1994d5ac70f0Sopenharmony_ci			else if (strcmp(str, "roundup") == 0)
1995d5ac70f0Sopenharmony_ci				rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_ROUNDUP;
1996d5ac70f0Sopenharmony_ci			else if (strcmp(str, "rounddown") == 0)
1997d5ac70f0Sopenharmony_ci				rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_ROUNDDOWN;
1998d5ac70f0Sopenharmony_ci			else if (strcmp(str, "auto") == 0)
1999d5ac70f0Sopenharmony_ci				rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_AUTO;
2000d5ac70f0Sopenharmony_ci			else {
2001d5ac70f0Sopenharmony_ci				SNDERR("The field hw_ptr_alignment is invalid : %s", str);
2002d5ac70f0Sopenharmony_ci				return -EINVAL;
2003d5ac70f0Sopenharmony_ci			}
2004d5ac70f0Sopenharmony_ci
2005d5ac70f0Sopenharmony_ci			continue;
2006d5ac70f0Sopenharmony_ci		}
2007d5ac70f0Sopenharmony_ci		if (strcmp(id, "tstamp_type") == 0) {
2008d5ac70f0Sopenharmony_ci			const char *str;
2009d5ac70f0Sopenharmony_ci			err = snd_config_get_string(n, &str);
2010d5ac70f0Sopenharmony_ci			if (err < 0) {
2011d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
2012d5ac70f0Sopenharmony_ci				return -EINVAL;
2013d5ac70f0Sopenharmony_ci			}
2014d5ac70f0Sopenharmony_ci			if (strcmp(str, "default") == 0)
2015d5ac70f0Sopenharmony_ci				rec->tstamp_type = -1;
2016d5ac70f0Sopenharmony_ci			else if (strcmp(str, "gettimeofday") == 0)
2017d5ac70f0Sopenharmony_ci				rec->tstamp_type = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
2018d5ac70f0Sopenharmony_ci			else if (strcmp(str, "monotonic") == 0)
2019d5ac70f0Sopenharmony_ci				rec->tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC;
2020d5ac70f0Sopenharmony_ci			else if (strcmp(str, "monotonic_raw") == 0)
2021d5ac70f0Sopenharmony_ci				rec->tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW;
2022d5ac70f0Sopenharmony_ci			else {
2023d5ac70f0Sopenharmony_ci				SNDERR("The field tstamp_type is invalid : %s", str);
2024d5ac70f0Sopenharmony_ci				return -EINVAL;
2025d5ac70f0Sopenharmony_ci			}
2026d5ac70f0Sopenharmony_ci			continue;
2027d5ac70f0Sopenharmony_ci		}
2028d5ac70f0Sopenharmony_ci		if (strcmp(id, "ipc_gid") == 0) {
2029d5ac70f0Sopenharmony_ci			char *group;
2030d5ac70f0Sopenharmony_ci			char *endp;
2031d5ac70f0Sopenharmony_ci			err = snd_config_get_ascii(n, &group);
2032d5ac70f0Sopenharmony_ci			if (err < 0) {
2033d5ac70f0Sopenharmony_ci				SNDERR("The field ipc_gid must be a valid group");
2034d5ac70f0Sopenharmony_ci				return err;
2035d5ac70f0Sopenharmony_ci			}
2036d5ac70f0Sopenharmony_ci			if (! *group) {
2037d5ac70f0Sopenharmony_ci				rec->ipc_gid = -1;
2038d5ac70f0Sopenharmony_ci				free(group);
2039d5ac70f0Sopenharmony_ci				continue;
2040d5ac70f0Sopenharmony_ci			}
2041d5ac70f0Sopenharmony_ci			if (isdigit(*group) == 0) {
2042d5ac70f0Sopenharmony_ci				long clen = sysconf(_SC_GETGR_R_SIZE_MAX);
2043d5ac70f0Sopenharmony_ci				size_t len = (clen == -1) ? 1024 : (size_t)clen;
2044d5ac70f0Sopenharmony_ci				struct group grp, *pgrp;
2045d5ac70f0Sopenharmony_ci				char *buffer = (char *)malloc(len);
2046d5ac70f0Sopenharmony_ci				if (buffer == NULL)
2047d5ac70f0Sopenharmony_ci					return -ENOMEM;
2048d5ac70f0Sopenharmony_ci				int st = getgrnam_r(group, &grp, buffer, len, &pgrp);
2049d5ac70f0Sopenharmony_ci				if (st != 0 || !pgrp) {
2050d5ac70f0Sopenharmony_ci					SNDERR("The field ipc_gid must be a valid group (create group %s)", group);
2051d5ac70f0Sopenharmony_ci					free(buffer);
2052d5ac70f0Sopenharmony_ci					return -EINVAL;
2053d5ac70f0Sopenharmony_ci				}
2054d5ac70f0Sopenharmony_ci				rec->ipc_gid = pgrp->gr_gid;
2055d5ac70f0Sopenharmony_ci				free(buffer);
2056d5ac70f0Sopenharmony_ci			} else {
2057d5ac70f0Sopenharmony_ci				rec->ipc_gid = strtol(group, &endp, 10);
2058d5ac70f0Sopenharmony_ci			}
2059d5ac70f0Sopenharmony_ci			free(group);
2060d5ac70f0Sopenharmony_ci			continue;
2061d5ac70f0Sopenharmony_ci		}
2062d5ac70f0Sopenharmony_ci		if (strcmp(id, "ipc_key_add_uid") == 0) {
2063d5ac70f0Sopenharmony_ci			if ((err = snd_config_get_bool(n)) < 0) {
2064d5ac70f0Sopenharmony_ci				SNDERR("The field ipc_key_add_uid must be a boolean type");
2065d5ac70f0Sopenharmony_ci				return err;
2066d5ac70f0Sopenharmony_ci			}
2067d5ac70f0Sopenharmony_ci			ipc_key_add_uid = err;
2068d5ac70f0Sopenharmony_ci			continue;
2069d5ac70f0Sopenharmony_ci		}
2070d5ac70f0Sopenharmony_ci		if (strcmp(id, "slave") == 0) {
2071d5ac70f0Sopenharmony_ci			rec->slave = n;
2072d5ac70f0Sopenharmony_ci			continue;
2073d5ac70f0Sopenharmony_ci		}
2074d5ac70f0Sopenharmony_ci		if (strcmp(id, "bindings") == 0) {
2075d5ac70f0Sopenharmony_ci			rec->bindings = n;
2076d5ac70f0Sopenharmony_ci			continue;
2077d5ac70f0Sopenharmony_ci		}
2078d5ac70f0Sopenharmony_ci		if (strcmp(id, "slowptr") == 0) {
2079d5ac70f0Sopenharmony_ci			err = snd_config_get_bool(n);
2080d5ac70f0Sopenharmony_ci			if (err < 0)
2081d5ac70f0Sopenharmony_ci				return err;
2082d5ac70f0Sopenharmony_ci			rec->slowptr = err;
2083d5ac70f0Sopenharmony_ci			continue;
2084d5ac70f0Sopenharmony_ci		}
2085d5ac70f0Sopenharmony_ci		if (strcmp(id, "max_periods") == 0) {
2086d5ac70f0Sopenharmony_ci			long val;
2087d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &val);
2088d5ac70f0Sopenharmony_ci			if (err < 0)
2089d5ac70f0Sopenharmony_ci				return err;
2090d5ac70f0Sopenharmony_ci			rec->max_periods = val;
2091d5ac70f0Sopenharmony_ci			continue;
2092d5ac70f0Sopenharmony_ci		}
2093d5ac70f0Sopenharmony_ci		if (strcmp(id, "var_periodsize") == 0) {
2094d5ac70f0Sopenharmony_ci			err = snd_config_get_bool(n);
2095d5ac70f0Sopenharmony_ci			if (err < 0)
2096d5ac70f0Sopenharmony_ci				return err;
2097d5ac70f0Sopenharmony_ci			rec->var_periodsize = err;
2098d5ac70f0Sopenharmony_ci			continue;
2099d5ac70f0Sopenharmony_ci		}
2100d5ac70f0Sopenharmony_ci		if (strcmp(id, "direct_memory_access") == 0) {
2101d5ac70f0Sopenharmony_ci			err = snd_config_get_bool(n);
2102d5ac70f0Sopenharmony_ci			if (err < 0)
2103d5ac70f0Sopenharmony_ci				return err;
2104d5ac70f0Sopenharmony_ci			rec->direct_memory_access = err;
2105d5ac70f0Sopenharmony_ci			continue;
2106d5ac70f0Sopenharmony_ci		}
2107d5ac70f0Sopenharmony_ci		SNDERR("Unknown field %s", id);
2108d5ac70f0Sopenharmony_ci		return -EINVAL;
2109d5ac70f0Sopenharmony_ci	}
2110d5ac70f0Sopenharmony_ci	if (!rec->slave) {
2111d5ac70f0Sopenharmony_ci		SNDERR("slave is not defined");
2112d5ac70f0Sopenharmony_ci		return -EINVAL;
2113d5ac70f0Sopenharmony_ci	}
2114d5ac70f0Sopenharmony_ci	if (!rec->ipc_key) {
2115d5ac70f0Sopenharmony_ci		SNDERR("Unique IPC key is not defined");
2116d5ac70f0Sopenharmony_ci		return -EINVAL;
2117d5ac70f0Sopenharmony_ci	}
2118d5ac70f0Sopenharmony_ci	if (ipc_key_add_uid)
2119d5ac70f0Sopenharmony_ci		rec->ipc_key += getuid();
2120d5ac70f0Sopenharmony_ci	err = snd_pcm_direct_get_slave_ipc_offset(root, conf, stream);
2121d5ac70f0Sopenharmony_ci	if (err < 0)
2122d5ac70f0Sopenharmony_ci		return err;
2123d5ac70f0Sopenharmony_ci	rec->ipc_key += err;
2124d5ac70f0Sopenharmony_ci
2125d5ac70f0Sopenharmony_ci	return 0;
2126d5ac70f0Sopenharmony_ci}
2127d5ac70f0Sopenharmony_ci
2128d5ac70f0Sopenharmony_civoid snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix,
2129d5ac70f0Sopenharmony_ci				    snd_pcm_uframes_t hw_ptr)
2130d5ac70f0Sopenharmony_ci{
2131d5ac70f0Sopenharmony_ci	dmix->slave_appl_ptr = dmix->slave_hw_ptr = hw_ptr;
2132d5ac70f0Sopenharmony_ci	if (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_ROUNDUP ||
2133d5ac70f0Sopenharmony_ci	    (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO &&
2134d5ac70f0Sopenharmony_ci	     pcm->buffer_size <= pcm->period_size * 2))
2135d5ac70f0Sopenharmony_ci		dmix->slave_appl_ptr =
2136d5ac70f0Sopenharmony_ci			((dmix->slave_appl_ptr + dmix->slave_period_size - 1) /
2137d5ac70f0Sopenharmony_ci					dmix->slave_period_size) * dmix->slave_period_size;
2138d5ac70f0Sopenharmony_ci	else if (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_ROUNDDOWN ||
2139d5ac70f0Sopenharmony_ci		 (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO &&
2140d5ac70f0Sopenharmony_ci		  ((dmix->slave_period_size * SEC_TO_MS) / pcm->rate) < LOW_LATENCY_PERIOD_TIME))
2141d5ac70f0Sopenharmony_ci		dmix->slave_appl_ptr = dmix->slave_hw_ptr =
2142d5ac70f0Sopenharmony_ci			((dmix->slave_hw_ptr / dmix->slave_period_size) *
2143d5ac70f0Sopenharmony_ci							dmix->slave_period_size);
2144d5ac70f0Sopenharmony_ci}
2145d5ac70f0Sopenharmony_ci
2146d5ac70f0Sopenharmony_ciint _snd_pcm_direct_new(snd_pcm_t **pcmp, snd_pcm_direct_t **_dmix, int type,
2147d5ac70f0Sopenharmony_ci			const char *name, struct snd_pcm_direct_open_conf *opts,
2148d5ac70f0Sopenharmony_ci			struct slave_params *params, snd_pcm_stream_t stream, int mode)
2149d5ac70f0Sopenharmony_ci{
2150d5ac70f0Sopenharmony_ci	snd_pcm_direct_t *dmix;
2151d5ac70f0Sopenharmony_ci	int fail_sem_loop = 10;
2152d5ac70f0Sopenharmony_ci	int ret;
2153d5ac70f0Sopenharmony_ci
2154d5ac70f0Sopenharmony_ci	dmix = calloc(1, sizeof(snd_pcm_direct_t));
2155d5ac70f0Sopenharmony_ci	if (!dmix)
2156d5ac70f0Sopenharmony_ci		return -ENOMEM;
2157d5ac70f0Sopenharmony_ci
2158d5ac70f0Sopenharmony_ci	ret = snd_pcm_direct_parse_bindings(dmix, params, opts->bindings);
2159d5ac70f0Sopenharmony_ci	if (ret < 0) {
2160d5ac70f0Sopenharmony_ci		free(dmix);
2161d5ac70f0Sopenharmony_ci		return ret;
2162d5ac70f0Sopenharmony_ci	}
2163d5ac70f0Sopenharmony_ci
2164d5ac70f0Sopenharmony_ci	dmix->ipc_key = opts->ipc_key;
2165d5ac70f0Sopenharmony_ci	dmix->ipc_perm = opts->ipc_perm;
2166d5ac70f0Sopenharmony_ci	dmix->ipc_gid = opts->ipc_gid;
2167d5ac70f0Sopenharmony_ci	dmix->tstamp_type = opts->tstamp_type;
2168d5ac70f0Sopenharmony_ci	dmix->semid = -1;
2169d5ac70f0Sopenharmony_ci	dmix->shmid = -1;
2170d5ac70f0Sopenharmony_ci	dmix->shmptr = (void *) -1;
2171d5ac70f0Sopenharmony_ci	dmix->type = type;
2172d5ac70f0Sopenharmony_ci
2173d5ac70f0Sopenharmony_ci	ret = snd_pcm_new(pcmp, type, name, stream, mode);
2174d5ac70f0Sopenharmony_ci	if (ret < 0)
2175d5ac70f0Sopenharmony_ci		goto _err_nosem;
2176d5ac70f0Sopenharmony_ci
2177d5ac70f0Sopenharmony_ci	while (1) {
2178d5ac70f0Sopenharmony_ci		ret = snd_pcm_direct_semaphore_create_or_connect(dmix);
2179d5ac70f0Sopenharmony_ci		if (ret < 0) {
2180d5ac70f0Sopenharmony_ci			SNDERR("unable to create IPC semaphore");
2181d5ac70f0Sopenharmony_ci			goto _err_nosem_free;
2182d5ac70f0Sopenharmony_ci		}
2183d5ac70f0Sopenharmony_ci		ret = snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
2184d5ac70f0Sopenharmony_ci		if (ret < 0) {
2185d5ac70f0Sopenharmony_ci			snd_pcm_direct_semaphore_discard(dmix);
2186d5ac70f0Sopenharmony_ci			if (--fail_sem_loop <= 0)
2187d5ac70f0Sopenharmony_ci				goto _err_nosem_free;
2188d5ac70f0Sopenharmony_ci			continue;
2189d5ac70f0Sopenharmony_ci		}
2190d5ac70f0Sopenharmony_ci		break;
2191d5ac70f0Sopenharmony_ci	}
2192d5ac70f0Sopenharmony_ci
2193d5ac70f0Sopenharmony_ci	ret = snd_pcm_direct_shm_create_or_connect(dmix);
2194d5ac70f0Sopenharmony_ci	if (ret < 0) {
2195d5ac70f0Sopenharmony_ci		SNDERR("unable to create IPC shm instance");
2196d5ac70f0Sopenharmony_ci		snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
2197d5ac70f0Sopenharmony_ci		goto _err_nosem_free;
2198d5ac70f0Sopenharmony_ci	} else {
2199d5ac70f0Sopenharmony_ci		*_dmix = dmix;
2200d5ac70f0Sopenharmony_ci	}
2201d5ac70f0Sopenharmony_ci
2202d5ac70f0Sopenharmony_ci	return ret;
2203d5ac70f0Sopenharmony_ci_err_nosem_free:
2204d5ac70f0Sopenharmony_ci	snd_pcm_free(*pcmp);
2205d5ac70f0Sopenharmony_ci	*pcmp = NULL;
2206d5ac70f0Sopenharmony_ci_err_nosem:
2207d5ac70f0Sopenharmony_ci	free(dmix->bindings);
2208d5ac70f0Sopenharmony_ci	free(dmix);
2209d5ac70f0Sopenharmony_ci	return ret;
2210d5ac70f0Sopenharmony_ci}
2211