1d5ac70f0Sopenharmony_ci/**
2d5ac70f0Sopenharmony_ci * \file pcm/pcm_hw.c
3d5ac70f0Sopenharmony_ci * \ingroup PCM_Plugins
4d5ac70f0Sopenharmony_ci * \brief PCM HW Plugin Interface
5d5ac70f0Sopenharmony_ci * \author Abramo Bagnara <abramo@alsa-project.org>
6d5ac70f0Sopenharmony_ci * \author Jaroslav Kysela <perex@perex.cz>
7d5ac70f0Sopenharmony_ci * \date 2000-2001
8d5ac70f0Sopenharmony_ci */
9d5ac70f0Sopenharmony_ci/*
10d5ac70f0Sopenharmony_ci *  PCM - Hardware
11d5ac70f0Sopenharmony_ci *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
12d5ac70f0Sopenharmony_ci *
13d5ac70f0Sopenharmony_ci *
14d5ac70f0Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
15d5ac70f0Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as
16d5ac70f0Sopenharmony_ci *   published by the Free Software Foundation; either version 2.1 of
17d5ac70f0Sopenharmony_ci *   the License, or (at your option) any later version.
18d5ac70f0Sopenharmony_ci *
19d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
20d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
21d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22d5ac70f0Sopenharmony_ci *   GNU Lesser General Public License for more details.
23d5ac70f0Sopenharmony_ci *
24d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public
25d5ac70f0Sopenharmony_ci *   License along with this library; if not, write to the Free Software
26d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
27d5ac70f0Sopenharmony_ci *
28d5ac70f0Sopenharmony_ci */
29d5ac70f0Sopenharmony_ci
30d5ac70f0Sopenharmony_ci#include "pcm_local.h"
31d5ac70f0Sopenharmony_ci#include "../control/control_local.h"
32d5ac70f0Sopenharmony_ci#include "../timer/timer_local.h"
33d5ac70f0Sopenharmony_ci#include <stdio.h>
34d5ac70f0Sopenharmony_ci#include <stdlib.h>
35d5ac70f0Sopenharmony_ci#include <stddef.h>
36d5ac70f0Sopenharmony_ci#include <unistd.h>
37d5ac70f0Sopenharmony_ci#include <stdbool.h>
38d5ac70f0Sopenharmony_ci#include <signal.h>
39d5ac70f0Sopenharmony_ci#include <string.h>
40d5ac70f0Sopenharmony_ci#include <fcntl.h>
41d5ac70f0Sopenharmony_ci#include <sys/ioctl.h>
42d5ac70f0Sopenharmony_ci#include <sys/mman.h>
43d5ac70f0Sopenharmony_ci
44d5ac70f0Sopenharmony_ci//#define DEBUG_RW		/* use to debug readi/writei/readn/writen */
45d5ac70f0Sopenharmony_ci//#define DEBUG_MMAP		/* debug mmap_commit */
46d5ac70f0Sopenharmony_ci
47d5ac70f0Sopenharmony_ci#ifndef PIC
48d5ac70f0Sopenharmony_ci/* entry for static linking */
49d5ac70f0Sopenharmony_ciconst char *_snd_module_pcm_hw = "";
50d5ac70f0Sopenharmony_ci#endif
51d5ac70f0Sopenharmony_ci
52d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
53d5ac70f0Sopenharmony_ci
54d5ac70f0Sopenharmony_ci#ifndef F_SETSIG
55d5ac70f0Sopenharmony_ci#define F_SETSIG 10
56d5ac70f0Sopenharmony_ci#endif
57d5ac70f0Sopenharmony_ci
58d5ac70f0Sopenharmony_ci/*
59d5ac70f0Sopenharmony_ci *  Compatibility
60d5ac70f0Sopenharmony_ci */
61d5ac70f0Sopenharmony_ci
62d5ac70f0Sopenharmony_cistruct sndrv_pcm_hw_params_old {
63d5ac70f0Sopenharmony_ci	unsigned int flags;
64d5ac70f0Sopenharmony_ci	unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT -
65d5ac70f0Sopenharmony_ci			   SNDRV_PCM_HW_PARAM_ACCESS + 1];
66d5ac70f0Sopenharmony_ci	struct snd_interval intervals[SNDRV_PCM_HW_PARAM_TICK_TIME -
67d5ac70f0Sopenharmony_ci					SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1];
68d5ac70f0Sopenharmony_ci	unsigned int rmask;
69d5ac70f0Sopenharmony_ci	unsigned int cmask;
70d5ac70f0Sopenharmony_ci	unsigned int info;
71d5ac70f0Sopenharmony_ci	unsigned int msbits;
72d5ac70f0Sopenharmony_ci	unsigned int rate_num;
73d5ac70f0Sopenharmony_ci	unsigned int rate_den;
74d5ac70f0Sopenharmony_ci	sndrv_pcm_uframes_t fifo_size;
75d5ac70f0Sopenharmony_ci	unsigned char reserved[64];
76d5ac70f0Sopenharmony_ci};
77d5ac70f0Sopenharmony_ci
78d5ac70f0Sopenharmony_ci#define SND_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct sndrv_pcm_hw_params_old)
79d5ac70f0Sopenharmony_ci#define SND_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct sndrv_pcm_hw_params_old)
80d5ac70f0Sopenharmony_ci
81d5ac70f0Sopenharmony_cistatic int use_old_hw_params_ioctl(int fd, unsigned int cmd, snd_pcm_hw_params_t *params);
82d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm);
83d5ac70f0Sopenharmony_cistatic const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops;
84d5ac70f0Sopenharmony_cistatic const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops_timer;
85d5ac70f0Sopenharmony_ci
86d5ac70f0Sopenharmony_ci/*
87d5ac70f0Sopenharmony_ci *
88d5ac70f0Sopenharmony_ci */
89d5ac70f0Sopenharmony_ci
90d5ac70f0Sopenharmony_citypedef struct {
91d5ac70f0Sopenharmony_ci	int version;
92d5ac70f0Sopenharmony_ci	int fd;
93d5ac70f0Sopenharmony_ci	int card, device, subdevice;
94d5ac70f0Sopenharmony_ci
95d5ac70f0Sopenharmony_ci	volatile struct snd_pcm_mmap_status * mmap_status;
96d5ac70f0Sopenharmony_ci	struct snd_pcm_mmap_control *mmap_control;
97d5ac70f0Sopenharmony_ci	bool mmap_status_fallbacked;
98d5ac70f0Sopenharmony_ci	bool mmap_control_fallbacked;
99d5ac70f0Sopenharmony_ci	struct snd_pcm_sync_ptr *sync_ptr;
100d5ac70f0Sopenharmony_ci
101d5ac70f0Sopenharmony_ci	bool prepare_reset_sw_params;
102d5ac70f0Sopenharmony_ci	bool perfect_drain;
103d5ac70f0Sopenharmony_ci
104d5ac70f0Sopenharmony_ci	int period_event;
105d5ac70f0Sopenharmony_ci	snd_timer_t *period_timer;
106d5ac70f0Sopenharmony_ci	struct pollfd period_timer_pfd;
107d5ac70f0Sopenharmony_ci	int period_timer_need_poll;
108d5ac70f0Sopenharmony_ci	/* restricted parameters */
109d5ac70f0Sopenharmony_ci	snd_pcm_format_t format;
110d5ac70f0Sopenharmony_ci	struct {
111d5ac70f0Sopenharmony_ci		int min;
112d5ac70f0Sopenharmony_ci		int max;
113d5ac70f0Sopenharmony_ci	} rates;
114d5ac70f0Sopenharmony_ci	int channels;
115d5ac70f0Sopenharmony_ci	int drain_silence;
116d5ac70f0Sopenharmony_ci	/* for chmap */
117d5ac70f0Sopenharmony_ci	unsigned int chmap_caps;
118d5ac70f0Sopenharmony_ci	snd_pcm_chmap_query_t **chmap_override;
119d5ac70f0Sopenharmony_ci} snd_pcm_hw_t;
120d5ac70f0Sopenharmony_ci
121d5ac70f0Sopenharmony_ci#define SNDRV_FILE_PCM_STREAM_PLAYBACK		ALSA_DEVICE_DIRECTORY "pcmC%iD%ip"
122d5ac70f0Sopenharmony_ci#define SNDRV_FILE_PCM_STREAM_CAPTURE		ALSA_DEVICE_DIRECTORY "pcmC%iD%ic"
123d5ac70f0Sopenharmony_ci#define SNDRV_PCM_VERSION_MAX			SNDRV_PROTOCOL_VERSION(2, 0, 9)
124d5ac70f0Sopenharmony_ci
125d5ac70f0Sopenharmony_ci/* update appl_ptr with driver */
126d5ac70f0Sopenharmony_ci#define FAST_PCM_STATE(hw) \
127d5ac70f0Sopenharmony_ci	((snd_pcm_state_t) (hw)->mmap_status->state)
128d5ac70f0Sopenharmony_ci#define FAST_PCM_TSTAMP(hw) \
129d5ac70f0Sopenharmony_ci	((hw)->mmap_status->tstamp)
130d5ac70f0Sopenharmony_ci
131d5ac70f0Sopenharmony_cistruct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm)
132d5ac70f0Sopenharmony_ci{
133d5ac70f0Sopenharmony_ci	struct timespec res;
134d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
135d5ac70f0Sopenharmony_ci	res = FAST_PCM_TSTAMP(hw);
136d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_VERSION(2, 0, 5) > hw->version)
137d5ac70f0Sopenharmony_ci		res.tv_nsec *= 1000L;
138d5ac70f0Sopenharmony_ci	return res;
139d5ac70f0Sopenharmony_ci}
140d5ac70f0Sopenharmony_ci#endif /* DOC_HIDDEN */
141d5ac70f0Sopenharmony_ci
142d5ac70f0Sopenharmony_cistatic int sync_ptr1(snd_pcm_hw_t *hw, unsigned int flags)
143d5ac70f0Sopenharmony_ci{
144d5ac70f0Sopenharmony_ci	int err;
145d5ac70f0Sopenharmony_ci	hw->sync_ptr->flags = flags;
146d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SYNC_PTR, hw->sync_ptr) < 0) {
147d5ac70f0Sopenharmony_ci		err = -errno;
148d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_SYNC_PTR failed (%i)", err);
149d5ac70f0Sopenharmony_ci		return err;
150d5ac70f0Sopenharmony_ci	}
151d5ac70f0Sopenharmony_ci	return 0;
152d5ac70f0Sopenharmony_ci}
153d5ac70f0Sopenharmony_ci
154d5ac70f0Sopenharmony_cistatic int issue_avail_min(snd_pcm_hw_t *hw)
155d5ac70f0Sopenharmony_ci{
156d5ac70f0Sopenharmony_ci	if (!hw->mmap_control_fallbacked)
157d5ac70f0Sopenharmony_ci		return 0;
158d5ac70f0Sopenharmony_ci
159d5ac70f0Sopenharmony_ci	/* Avoid unexpected change of applptr in kernel space. */
160d5ac70f0Sopenharmony_ci	return sync_ptr1(hw, SNDRV_PCM_SYNC_PTR_APPL);
161d5ac70f0Sopenharmony_ci}
162d5ac70f0Sopenharmony_ci
163d5ac70f0Sopenharmony_cistatic int issue_applptr(snd_pcm_hw_t *hw)
164d5ac70f0Sopenharmony_ci{
165d5ac70f0Sopenharmony_ci	if (!hw->mmap_control_fallbacked)
166d5ac70f0Sopenharmony_ci		return 0;
167d5ac70f0Sopenharmony_ci
168d5ac70f0Sopenharmony_ci	/* Avoid unexpected change of avail_min in kernel space. */
169d5ac70f0Sopenharmony_ci	return sync_ptr1(hw, SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
170d5ac70f0Sopenharmony_ci}
171d5ac70f0Sopenharmony_ci
172d5ac70f0Sopenharmony_cistatic int request_hwsync(snd_pcm_hw_t *hw)
173d5ac70f0Sopenharmony_ci{
174d5ac70f0Sopenharmony_ci	if (!hw->mmap_status_fallbacked)
175d5ac70f0Sopenharmony_ci		return 0;
176d5ac70f0Sopenharmony_ci
177d5ac70f0Sopenharmony_ci	/*
178d5ac70f0Sopenharmony_ci	 * Query both of control/status data to avoid unexpected change of
179d5ac70f0Sopenharmony_ci	 * control data in kernel space.
180d5ac70f0Sopenharmony_ci	 */
181d5ac70f0Sopenharmony_ci	return sync_ptr1(hw,
182d5ac70f0Sopenharmony_ci			 SNDRV_PCM_SYNC_PTR_HWSYNC |
183d5ac70f0Sopenharmony_ci			 SNDRV_PCM_SYNC_PTR_APPL |
184d5ac70f0Sopenharmony_ci			 SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
185d5ac70f0Sopenharmony_ci}
186d5ac70f0Sopenharmony_ci
187d5ac70f0Sopenharmony_cistatic int query_status_and_control_data(snd_pcm_hw_t *hw)
188d5ac70f0Sopenharmony_ci{
189d5ac70f0Sopenharmony_ci	if (!hw->mmap_control_fallbacked)
190d5ac70f0Sopenharmony_ci		return 0;
191d5ac70f0Sopenharmony_ci
192d5ac70f0Sopenharmony_ci	/*
193d5ac70f0Sopenharmony_ci	 * Query both of control/status data to avoid unexpected change of
194d5ac70f0Sopenharmony_ci	 * control data in kernel space.
195d5ac70f0Sopenharmony_ci	 */
196d5ac70f0Sopenharmony_ci	return sync_ptr1(hw,
197d5ac70f0Sopenharmony_ci			 SNDRV_PCM_SYNC_PTR_APPL |
198d5ac70f0Sopenharmony_ci			 SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
199d5ac70f0Sopenharmony_ci}
200d5ac70f0Sopenharmony_ci
201d5ac70f0Sopenharmony_cistatic int query_status_data(snd_pcm_hw_t *hw)
202d5ac70f0Sopenharmony_ci{
203d5ac70f0Sopenharmony_ci	if (!hw->mmap_status_fallbacked)
204d5ac70f0Sopenharmony_ci		return 0;
205d5ac70f0Sopenharmony_ci
206d5ac70f0Sopenharmony_ci	/*
207d5ac70f0Sopenharmony_ci	 * Query both of control/status data to avoid unexpected change of
208d5ac70f0Sopenharmony_ci	 * control data in kernel space.
209d5ac70f0Sopenharmony_ci	 */
210d5ac70f0Sopenharmony_ci	return sync_ptr1(hw,
211d5ac70f0Sopenharmony_ci			 SNDRV_PCM_SYNC_PTR_APPL |
212d5ac70f0Sopenharmony_ci			 SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
213d5ac70f0Sopenharmony_ci}
214d5ac70f0Sopenharmony_ci
215d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_clear_timer_queue(snd_pcm_hw_t *hw)
216d5ac70f0Sopenharmony_ci{
217d5ac70f0Sopenharmony_ci	if (hw->period_timer_need_poll) {
218d5ac70f0Sopenharmony_ci		while (poll(&hw->period_timer_pfd, 1, 0) > 0) {
219d5ac70f0Sopenharmony_ci			snd_timer_tread_t rbuf[4];
220d5ac70f0Sopenharmony_ci			snd_timer_read(hw->period_timer, rbuf, sizeof(rbuf));
221d5ac70f0Sopenharmony_ci		}
222d5ac70f0Sopenharmony_ci	} else {
223d5ac70f0Sopenharmony_ci		snd_timer_tread_t rbuf[4];
224d5ac70f0Sopenharmony_ci		snd_timer_read(hw->period_timer, rbuf, sizeof(rbuf));
225d5ac70f0Sopenharmony_ci	}
226d5ac70f0Sopenharmony_ci	return 0;
227d5ac70f0Sopenharmony_ci}
228d5ac70f0Sopenharmony_ci
229d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_poll_descriptors_count(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
230d5ac70f0Sopenharmony_ci{
231d5ac70f0Sopenharmony_ci	return 2;
232d5ac70f0Sopenharmony_ci}
233d5ac70f0Sopenharmony_ci
234d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
235d5ac70f0Sopenharmony_ci{
236d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
237d5ac70f0Sopenharmony_ci
238d5ac70f0Sopenharmony_ci	if (space < 2)
239d5ac70f0Sopenharmony_ci		return -ENOMEM;
240d5ac70f0Sopenharmony_ci	pfds[0].fd = hw->fd;
241d5ac70f0Sopenharmony_ci	pfds[0].events = pcm->poll_events | POLLERR | POLLNVAL;
242d5ac70f0Sopenharmony_ci	pfds[1].fd = hw->period_timer_pfd.fd;
243d5ac70f0Sopenharmony_ci	pfds[1].events = POLLIN | POLLERR | POLLNVAL;
244d5ac70f0Sopenharmony_ci	return 2;
245d5ac70f0Sopenharmony_ci}
246d5ac70f0Sopenharmony_ci
247d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned nfds, unsigned short *revents)
248d5ac70f0Sopenharmony_ci{
249d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
250d5ac70f0Sopenharmony_ci	unsigned int events;
251d5ac70f0Sopenharmony_ci
252d5ac70f0Sopenharmony_ci	if (nfds != 2 || pfds[0].fd != hw->fd || pfds[1].fd != hw->period_timer_pfd.fd)
253d5ac70f0Sopenharmony_ci		return -EINVAL;
254d5ac70f0Sopenharmony_ci	events = pfds[0].revents;
255d5ac70f0Sopenharmony_ci	if (pfds[1].revents & POLLIN) {
256d5ac70f0Sopenharmony_ci		snd_pcm_hw_clear_timer_queue(hw);
257d5ac70f0Sopenharmony_ci		events |= pcm->poll_events & ~(POLLERR|POLLNVAL);
258d5ac70f0Sopenharmony_ci	}
259d5ac70f0Sopenharmony_ci	*revents = events;
260d5ac70f0Sopenharmony_ci	return 0;
261d5ac70f0Sopenharmony_ci}
262d5ac70f0Sopenharmony_ci
263d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_nonblock(snd_pcm_t *pcm, int nonblock)
264d5ac70f0Sopenharmony_ci{
265d5ac70f0Sopenharmony_ci	long flags;
266d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
267d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
268d5ac70f0Sopenharmony_ci
269d5ac70f0Sopenharmony_ci	if ((flags = fcntl(fd, F_GETFL)) < 0) {
270d5ac70f0Sopenharmony_ci		err = -errno;
271d5ac70f0Sopenharmony_ci		SYSMSG("F_GETFL failed (%i)", err);
272d5ac70f0Sopenharmony_ci		return err;
273d5ac70f0Sopenharmony_ci	}
274d5ac70f0Sopenharmony_ci	if (nonblock)
275d5ac70f0Sopenharmony_ci		flags |= O_NONBLOCK;
276d5ac70f0Sopenharmony_ci	else
277d5ac70f0Sopenharmony_ci		flags &= ~O_NONBLOCK;
278d5ac70f0Sopenharmony_ci	if (fcntl(fd, F_SETFL, flags) < 0) {
279d5ac70f0Sopenharmony_ci		err = -errno;
280d5ac70f0Sopenharmony_ci		SYSMSG("F_SETFL for O_NONBLOCK failed (%i)", err);
281d5ac70f0Sopenharmony_ci		return err;
282d5ac70f0Sopenharmony_ci	}
283d5ac70f0Sopenharmony_ci	return 0;
284d5ac70f0Sopenharmony_ci}
285d5ac70f0Sopenharmony_ci
286d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_async(snd_pcm_t *pcm, int sig, pid_t pid)
287d5ac70f0Sopenharmony_ci{
288d5ac70f0Sopenharmony_ci	long flags;
289d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
290d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
291d5ac70f0Sopenharmony_ci
292d5ac70f0Sopenharmony_ci	if ((flags = fcntl(fd, F_GETFL)) < 0) {
293d5ac70f0Sopenharmony_ci		err = -errno;
294d5ac70f0Sopenharmony_ci		SYSMSG("F_GETFL failed (%i)", err);
295d5ac70f0Sopenharmony_ci		return err;
296d5ac70f0Sopenharmony_ci	}
297d5ac70f0Sopenharmony_ci	if (sig >= 0)
298d5ac70f0Sopenharmony_ci		flags |= O_ASYNC;
299d5ac70f0Sopenharmony_ci	else
300d5ac70f0Sopenharmony_ci		flags &= ~O_ASYNC;
301d5ac70f0Sopenharmony_ci	if (fcntl(fd, F_SETFL, flags) < 0) {
302d5ac70f0Sopenharmony_ci		err = -errno;
303d5ac70f0Sopenharmony_ci		SYSMSG("F_SETFL for O_ASYNC failed (%i)", err);
304d5ac70f0Sopenharmony_ci		return err;
305d5ac70f0Sopenharmony_ci	}
306d5ac70f0Sopenharmony_ci	if (sig < 0)
307d5ac70f0Sopenharmony_ci		return 0;
308d5ac70f0Sopenharmony_ci	if (fcntl(fd, F_SETSIG, (long)sig) < 0) {
309d5ac70f0Sopenharmony_ci		err = -errno;
310d5ac70f0Sopenharmony_ci		SYSMSG("F_SETSIG failed (%i)", err);
311d5ac70f0Sopenharmony_ci		return err;
312d5ac70f0Sopenharmony_ci	}
313d5ac70f0Sopenharmony_ci	if (fcntl(fd, F_SETOWN, (long)pid) < 0) {
314d5ac70f0Sopenharmony_ci		err = -errno;
315d5ac70f0Sopenharmony_ci		SYSMSG("F_SETOWN failed (%i)", err);
316d5ac70f0Sopenharmony_ci		return err;
317d5ac70f0Sopenharmony_ci	}
318d5ac70f0Sopenharmony_ci	return 0;
319d5ac70f0Sopenharmony_ci}
320d5ac70f0Sopenharmony_ci
321d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
322d5ac70f0Sopenharmony_ci{
323d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
324d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
325d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_INFO, info) < 0) {
326d5ac70f0Sopenharmony_ci		err = -errno;
327d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_INFO failed (%i)", err);
328d5ac70f0Sopenharmony_ci		return err;
329d5ac70f0Sopenharmony_ci	}
330d5ac70f0Sopenharmony_ci	/* may be configurable (optional) */
331d5ac70f0Sopenharmony_ci	if (__snd_pcm_info_eld_fixup_check(info))
332d5ac70f0Sopenharmony_ci		return __snd_pcm_info_eld_fixup(info);
333d5ac70f0Sopenharmony_ci	return 0;
334d5ac70f0Sopenharmony_ci}
335d5ac70f0Sopenharmony_ci
336d5ac70f0Sopenharmony_cistatic inline int hw_refine_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params)
337d5ac70f0Sopenharmony_ci{
338d5ac70f0Sopenharmony_ci	/* check for new hw_params structure; it's available from 2.0.2 version of PCM API */
339d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= pcm_hw->version)
340d5ac70f0Sopenharmony_ci		return ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_REFINE, params);
341d5ac70f0Sopenharmony_ci	return use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_REFINE_OLD, params);
342d5ac70f0Sopenharmony_ci}
343d5ac70f0Sopenharmony_ci
344d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
345d5ac70f0Sopenharmony_ci{
346d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
347d5ac70f0Sopenharmony_ci	int err;
348d5ac70f0Sopenharmony_ci
349d5ac70f0Sopenharmony_ci	if (hw->format != SND_PCM_FORMAT_UNKNOWN) {
350d5ac70f0Sopenharmony_ci		err = _snd_pcm_hw_params_set_format(params, hw->format);
351d5ac70f0Sopenharmony_ci		if (err < 0)
352d5ac70f0Sopenharmony_ci			return err;
353d5ac70f0Sopenharmony_ci	}
354d5ac70f0Sopenharmony_ci	if (hw->channels > 0) {
355d5ac70f0Sopenharmony_ci		err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS,
356d5ac70f0Sopenharmony_ci					    hw->channels, 0);
357d5ac70f0Sopenharmony_ci		if (err < 0)
358d5ac70f0Sopenharmony_ci			return err;
359d5ac70f0Sopenharmony_ci	}
360d5ac70f0Sopenharmony_ci	if (hw->rates.min > 0) {
361d5ac70f0Sopenharmony_ci		err = _snd_pcm_hw_param_set_minmax(params, SND_PCM_HW_PARAM_RATE,
362d5ac70f0Sopenharmony_ci						   hw->rates.min, 0, hw->rates.max + 1, -1);
363d5ac70f0Sopenharmony_ci		if (err < 0)
364d5ac70f0Sopenharmony_ci			return err;
365d5ac70f0Sopenharmony_ci	}
366d5ac70f0Sopenharmony_ci
367d5ac70f0Sopenharmony_ci	if (hw_refine_call(hw, params) < 0) {
368d5ac70f0Sopenharmony_ci		err = -errno;
369d5ac70f0Sopenharmony_ci		// SYSMSG("SNDRV_PCM_IOCTL_HW_REFINE failed");
370d5ac70f0Sopenharmony_ci		return err;
371d5ac70f0Sopenharmony_ci	}
372d5ac70f0Sopenharmony_ci
373d5ac70f0Sopenharmony_ci	if (params->info != ~0U) {
374d5ac70f0Sopenharmony_ci		params->info &= ~0xf0000000;
375d5ac70f0Sopenharmony_ci		if (pcm->tstamp_type != SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY)
376d5ac70f0Sopenharmony_ci			params->info |= SND_PCM_INFO_MONOTONIC;
377d5ac70f0Sopenharmony_ci	}
378d5ac70f0Sopenharmony_ci
379d5ac70f0Sopenharmony_ci	return 0;
380d5ac70f0Sopenharmony_ci}
381d5ac70f0Sopenharmony_ci
382d5ac70f0Sopenharmony_ci#define hw_param_mask(params,var) \
383d5ac70f0Sopenharmony_ci	&((params)->masks[(var) - SND_PCM_HW_PARAM_FIRST_MASK])
384d5ac70f0Sopenharmony_ci
385d5ac70f0Sopenharmony_cistatic int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params)
386d5ac70f0Sopenharmony_ci{
387d5ac70f0Sopenharmony_ci	int err;
388d5ac70f0Sopenharmony_ci
389d5ac70f0Sopenharmony_ci	/* check for new hw_params structure; it's available from 2.0.2 version of PCM API */
390d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= pcm_hw->version)
391d5ac70f0Sopenharmony_ci		err = ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params);
392d5ac70f0Sopenharmony_ci	else
393d5ac70f0Sopenharmony_ci		err = use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params);
394d5ac70f0Sopenharmony_ci	if (err >= 0 && pcm_hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 17) && params->msbits > 0) {
395d5ac70f0Sopenharmony_ci		snd_mask_t *m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
396d5ac70f0Sopenharmony_ci		if (snd_mask_single(m)) {
397d5ac70f0Sopenharmony_ci			snd_pcm_format_t format = snd_mask_min(m);
398d5ac70f0Sopenharmony_ci			int width = snd_pcm_format_width(format);
399d5ac70f0Sopenharmony_ci			if (width > 0 && params->msbits > (unsigned int)width)
400d5ac70f0Sopenharmony_ci				params->msbits = width;
401d5ac70f0Sopenharmony_ci		}
402d5ac70f0Sopenharmony_ci	}
403d5ac70f0Sopenharmony_ci	return err;
404d5ac70f0Sopenharmony_ci}
405d5ac70f0Sopenharmony_ci
406d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
407d5ac70f0Sopenharmony_ci{
408d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
409d5ac70f0Sopenharmony_ci	int err;
410d5ac70f0Sopenharmony_ci	if (hw_params_call(hw, params) < 0) {
411d5ac70f0Sopenharmony_ci		err = -errno;
412d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_HW_PARAMS failed (%i)", err);
413d5ac70f0Sopenharmony_ci		return err;
414d5ac70f0Sopenharmony_ci	}
415d5ac70f0Sopenharmony_ci	params->info &= ~0xf0000000;
416d5ac70f0Sopenharmony_ci	if (pcm->tstamp_type != SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY)
417d5ac70f0Sopenharmony_ci		params->info |= SND_PCM_INFO_MONOTONIC;
418d5ac70f0Sopenharmony_ci	hw->perfect_drain = !!(params->info & SND_PCM_INFO_PERFECT_DRAIN) ||
419d5ac70f0Sopenharmony_ci			    !!(params->flags & SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE);
420d5ac70f0Sopenharmony_ci	return query_status_data(hw);
421d5ac70f0Sopenharmony_ci}
422d5ac70f0Sopenharmony_ci
423d5ac70f0Sopenharmony_cistatic void snd_pcm_hw_close_timer(snd_pcm_hw_t *hw)
424d5ac70f0Sopenharmony_ci{
425d5ac70f0Sopenharmony_ci	if (hw->period_timer) {
426d5ac70f0Sopenharmony_ci		snd_timer_close(hw->period_timer);
427d5ac70f0Sopenharmony_ci		hw->period_timer = NULL;
428d5ac70f0Sopenharmony_ci	}
429d5ac70f0Sopenharmony_ci}
430d5ac70f0Sopenharmony_ci
431d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_change_timer(snd_pcm_t *pcm, int enable)
432d5ac70f0Sopenharmony_ci{
433d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
434d5ac70f0Sopenharmony_ci	snd_timer_params_t params = {0};
435d5ac70f0Sopenharmony_ci	unsigned int suspend, resume;
436d5ac70f0Sopenharmony_ci	int err;
437d5ac70f0Sopenharmony_ci
438d5ac70f0Sopenharmony_ci	if (enable) {
439d5ac70f0Sopenharmony_ci		err = snd_timer_hw_open(&hw->period_timer,
440d5ac70f0Sopenharmony_ci				"hw-pcm-period-event",
441d5ac70f0Sopenharmony_ci				SND_TIMER_CLASS_PCM, SND_TIMER_SCLASS_NONE,
442d5ac70f0Sopenharmony_ci				hw->card, hw->device,
443d5ac70f0Sopenharmony_ci				(hw->subdevice << 1) | (pcm->stream & 1),
444d5ac70f0Sopenharmony_ci				SND_TIMER_OPEN_NONBLOCK | SND_TIMER_OPEN_TREAD);
445d5ac70f0Sopenharmony_ci		if (err < 0) {
446d5ac70f0Sopenharmony_ci			err = snd_timer_hw_open(&hw->period_timer,
447d5ac70f0Sopenharmony_ci				"hw-pcm-period-event",
448d5ac70f0Sopenharmony_ci				SND_TIMER_CLASS_PCM, SND_TIMER_SCLASS_NONE,
449d5ac70f0Sopenharmony_ci				hw->card, hw->device,
450d5ac70f0Sopenharmony_ci				(hw->subdevice << 1) | (pcm->stream & 1),
451d5ac70f0Sopenharmony_ci				SND_TIMER_OPEN_NONBLOCK);
452d5ac70f0Sopenharmony_ci			return err;
453d5ac70f0Sopenharmony_ci		}
454d5ac70f0Sopenharmony_ci		if (snd_timer_poll_descriptors_count(hw->period_timer) != 1) {
455d5ac70f0Sopenharmony_ci			snd_pcm_hw_close_timer(hw);
456d5ac70f0Sopenharmony_ci			return -EINVAL;
457d5ac70f0Sopenharmony_ci		}
458d5ac70f0Sopenharmony_ci		hw->period_timer_pfd.events = POLLIN;
459d5ac70f0Sopenharmony_ci 		hw->period_timer_pfd.revents = 0;
460d5ac70f0Sopenharmony_ci		snd_timer_poll_descriptors(hw->period_timer,
461d5ac70f0Sopenharmony_ci					   &hw->period_timer_pfd, 1);
462d5ac70f0Sopenharmony_ci		hw->period_timer_need_poll = 0;
463d5ac70f0Sopenharmony_ci		suspend = 1<<SND_TIMER_EVENT_MSUSPEND;
464d5ac70f0Sopenharmony_ci		resume = 1<<SND_TIMER_EVENT_MRESUME;
465d5ac70f0Sopenharmony_ci		/*
466d5ac70f0Sopenharmony_ci		 * hacks for older kernel drivers
467d5ac70f0Sopenharmony_ci		 */
468d5ac70f0Sopenharmony_ci		{
469d5ac70f0Sopenharmony_ci			int ver = 0;
470d5ac70f0Sopenharmony_ci			ioctl(hw->period_timer_pfd.fd,
471d5ac70f0Sopenharmony_ci			      SNDRV_TIMER_IOCTL_PVERSION, &ver);
472d5ac70f0Sopenharmony_ci			/*
473d5ac70f0Sopenharmony_ci			 * In older versions, check via poll before read() is
474d5ac70f0Sopenharmony_ci			 * needed because of the confliction between
475d5ac70f0Sopenharmony_ci			 * TIMER_START and FIONBIO ioctls.
476d5ac70f0Sopenharmony_ci                         */
477d5ac70f0Sopenharmony_ci			if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 4))
478d5ac70f0Sopenharmony_ci				hw->period_timer_need_poll = 1;
479d5ac70f0Sopenharmony_ci			/*
480d5ac70f0Sopenharmony_ci			 * In older versions, timer uses pause events instead
481d5ac70f0Sopenharmony_ci			 * suspend/resume events.
482d5ac70f0Sopenharmony_ci			 */
483d5ac70f0Sopenharmony_ci			if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 5)) {
484d5ac70f0Sopenharmony_ci				suspend = 1<<SND_TIMER_EVENT_MPAUSE;
485d5ac70f0Sopenharmony_ci				resume = 1<<SND_TIMER_EVENT_MCONTINUE;
486d5ac70f0Sopenharmony_ci			}
487d5ac70f0Sopenharmony_ci		}
488d5ac70f0Sopenharmony_ci		snd_timer_params_set_auto_start(&params, 1);
489d5ac70f0Sopenharmony_ci		snd_timer_params_set_ticks(&params, 1);
490d5ac70f0Sopenharmony_ci		INTERNAL(snd_timer_params_set_filter)(&params, (1<<SND_TIMER_EVENT_TICK) |
491d5ac70f0Sopenharmony_ci					    suspend | resume);
492d5ac70f0Sopenharmony_ci		err = snd_timer_params(hw->period_timer, &params);
493d5ac70f0Sopenharmony_ci		if (err < 0) {
494d5ac70f0Sopenharmony_ci			snd_pcm_hw_close_timer(hw);
495d5ac70f0Sopenharmony_ci			return err;
496d5ac70f0Sopenharmony_ci		}
497d5ac70f0Sopenharmony_ci		err = snd_timer_start(hw->period_timer);
498d5ac70f0Sopenharmony_ci		if (err < 0) {
499d5ac70f0Sopenharmony_ci			snd_pcm_hw_close_timer(hw);
500d5ac70f0Sopenharmony_ci			return err;
501d5ac70f0Sopenharmony_ci		}
502d5ac70f0Sopenharmony_ci		pcm->fast_ops = &snd_pcm_hw_fast_ops_timer;
503d5ac70f0Sopenharmony_ci	} else {
504d5ac70f0Sopenharmony_ci		snd_pcm_hw_close_timer(hw);
505d5ac70f0Sopenharmony_ci		pcm->fast_ops = &snd_pcm_hw_fast_ops;
506d5ac70f0Sopenharmony_ci		hw->period_event = 0;
507d5ac70f0Sopenharmony_ci	}
508d5ac70f0Sopenharmony_ci	return 0;
509d5ac70f0Sopenharmony_ci}
510d5ac70f0Sopenharmony_ci
511d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_hw_free(snd_pcm_t *pcm)
512d5ac70f0Sopenharmony_ci{
513d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
514d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
515d5ac70f0Sopenharmony_ci	snd_pcm_hw_change_timer(pcm, 0);
516d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_HW_FREE) < 0) {
517d5ac70f0Sopenharmony_ci		err = -errno;
518d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_HW_FREE failed (%i)", err);
519d5ac70f0Sopenharmony_ci		return err;
520d5ac70f0Sopenharmony_ci	}
521d5ac70f0Sopenharmony_ci	return 0;
522d5ac70f0Sopenharmony_ci}
523d5ac70f0Sopenharmony_ci
524d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
525d5ac70f0Sopenharmony_ci{
526d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
527d5ac70f0Sopenharmony_ci	int fd = hw->fd, err = 0;
528d5ac70f0Sopenharmony_ci	int old_period_event = sw_get_period_event(params);
529d5ac70f0Sopenharmony_ci	sw_set_period_event(params, 0);
530d5ac70f0Sopenharmony_ci	if ((snd_pcm_tstamp_t) params->tstamp_mode == pcm->tstamp_mode &&
531d5ac70f0Sopenharmony_ci	    (snd_pcm_tstamp_type_t) params->tstamp_type == pcm->tstamp_type &&
532d5ac70f0Sopenharmony_ci	    params->period_step == pcm->period_step &&
533d5ac70f0Sopenharmony_ci	    params->start_threshold == pcm->start_threshold &&
534d5ac70f0Sopenharmony_ci	    params->stop_threshold == pcm->stop_threshold &&
535d5ac70f0Sopenharmony_ci	    params->silence_threshold == pcm->silence_threshold &&
536d5ac70f0Sopenharmony_ci	    params->silence_size == pcm->silence_size &&
537d5ac70f0Sopenharmony_ci	    old_period_event == hw->period_event) {
538d5ac70f0Sopenharmony_ci		hw->mmap_control->avail_min = params->avail_min;
539d5ac70f0Sopenharmony_ci		err = issue_avail_min(hw);
540d5ac70f0Sopenharmony_ci		goto out;
541d5ac70f0Sopenharmony_ci	}
542d5ac70f0Sopenharmony_ci	if (params->tstamp_type == SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW &&
543d5ac70f0Sopenharmony_ci	    hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) {
544d5ac70f0Sopenharmony_ci		SYSMSG("Kernel doesn't support SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW");
545d5ac70f0Sopenharmony_ci		err = -EINVAL;
546d5ac70f0Sopenharmony_ci		goto out;
547d5ac70f0Sopenharmony_ci	}
548d5ac70f0Sopenharmony_ci	if (params->tstamp_type == SND_PCM_TSTAMP_TYPE_MONOTONIC &&
549d5ac70f0Sopenharmony_ci	    hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 5)) {
550d5ac70f0Sopenharmony_ci		SYSMSG("Kernel doesn't support SND_PCM_TSTAMP_TYPE_MONOTONIC");
551d5ac70f0Sopenharmony_ci		err = -EINVAL;
552d5ac70f0Sopenharmony_ci		goto out;
553d5ac70f0Sopenharmony_ci	}
554d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_SW_PARAMS, params) < 0) {
555d5ac70f0Sopenharmony_ci		err = -errno;
556d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
557d5ac70f0Sopenharmony_ci		goto out;
558d5ac70f0Sopenharmony_ci	}
559d5ac70f0Sopenharmony_ci	hw->prepare_reset_sw_params = false;
560d5ac70f0Sopenharmony_ci	if ((snd_pcm_tstamp_type_t) params->tstamp_type != pcm->tstamp_type) {
561d5ac70f0Sopenharmony_ci		if (hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) {
562d5ac70f0Sopenharmony_ci			int on = (snd_pcm_tstamp_type_t) params->tstamp_type ==
563d5ac70f0Sopenharmony_ci				SND_PCM_TSTAMP_TYPE_MONOTONIC;
564d5ac70f0Sopenharmony_ci			if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) {
565d5ac70f0Sopenharmony_ci				err = -errno;
566d5ac70f0Sopenharmony_ci				SNDMSG("TSTAMP failed");
567d5ac70f0Sopenharmony_ci				goto out;
568d5ac70f0Sopenharmony_ci			}
569d5ac70f0Sopenharmony_ci		}
570d5ac70f0Sopenharmony_ci		pcm->tstamp_type = params->tstamp_type;
571d5ac70f0Sopenharmony_ci	}
572d5ac70f0Sopenharmony_ci	hw->mmap_control->avail_min = params->avail_min;
573d5ac70f0Sopenharmony_ci	if (hw->period_event != old_period_event) {
574d5ac70f0Sopenharmony_ci		err = snd_pcm_hw_change_timer(pcm, old_period_event);
575d5ac70f0Sopenharmony_ci		if (err < 0)
576d5ac70f0Sopenharmony_ci			goto out;
577d5ac70f0Sopenharmony_ci		hw->period_event = old_period_event;
578d5ac70f0Sopenharmony_ci	}
579d5ac70f0Sopenharmony_ci out:
580d5ac70f0Sopenharmony_ci	sw_set_period_event(params, old_period_event);
581d5ac70f0Sopenharmony_ci	return err;
582d5ac70f0Sopenharmony_ci}
583d5ac70f0Sopenharmony_ci
584d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
585d5ac70f0Sopenharmony_ci{
586d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
587d5ac70f0Sopenharmony_ci	struct snd_pcm_channel_info i;
588d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
589d5ac70f0Sopenharmony_ci	i.channel = info->channel;
590d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_CHANNEL_INFO, &i) < 0) {
591d5ac70f0Sopenharmony_ci		err = -errno;
592d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_CHANNEL_INFO failed (%i)", err);
593d5ac70f0Sopenharmony_ci		return err;
594d5ac70f0Sopenharmony_ci	}
595d5ac70f0Sopenharmony_ci	info->channel = i.channel;
596d5ac70f0Sopenharmony_ci	info->addr = 0;
597d5ac70f0Sopenharmony_ci	info->first = i.first;
598d5ac70f0Sopenharmony_ci	info->step = i.step;
599d5ac70f0Sopenharmony_ci	info->type = SND_PCM_AREA_MMAP;
600d5ac70f0Sopenharmony_ci	info->u.mmap.fd = fd;
601d5ac70f0Sopenharmony_ci	info->u.mmap.offset = i.offset;
602d5ac70f0Sopenharmony_ci	return 0;
603d5ac70f0Sopenharmony_ci}
604d5ac70f0Sopenharmony_ci
605d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
606d5ac70f0Sopenharmony_ci{
607d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
608d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
609d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_VERSION(2, 0, 13) > hw->version) {
610d5ac70f0Sopenharmony_ci		if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, status) < 0) {
611d5ac70f0Sopenharmony_ci			err = -errno;
612d5ac70f0Sopenharmony_ci			SYSMSG("SNDRV_PCM_IOCTL_STATUS failed (%i)", err);
613d5ac70f0Sopenharmony_ci			return err;
614d5ac70f0Sopenharmony_ci		}
615d5ac70f0Sopenharmony_ci	} else {
616d5ac70f0Sopenharmony_ci		if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS_EXT, status) < 0) {
617d5ac70f0Sopenharmony_ci			err = -errno;
618d5ac70f0Sopenharmony_ci			SYSMSG("SNDRV_PCM_IOCTL_STATUS_EXT failed (%i)", err);
619d5ac70f0Sopenharmony_ci			return err;
620d5ac70f0Sopenharmony_ci		}
621d5ac70f0Sopenharmony_ci	}
622d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_VERSION(2, 0, 5) > hw->version) {
623d5ac70f0Sopenharmony_ci		status->tstamp.tv_nsec *= 1000L;
624d5ac70f0Sopenharmony_ci		status->trigger_tstamp.tv_nsec *= 1000L;
625d5ac70f0Sopenharmony_ci	}
626d5ac70f0Sopenharmony_ci	return 0;
627d5ac70f0Sopenharmony_ci}
628d5ac70f0Sopenharmony_ci
629d5ac70f0Sopenharmony_cistatic snd_pcm_state_t snd_pcm_hw_state(snd_pcm_t *pcm)
630d5ac70f0Sopenharmony_ci{
631d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
632d5ac70f0Sopenharmony_ci	/* the -ENODEV may come from the snd_disconnect_ioctl() or
633d5ac70f0Sopenharmony_ci	   snd_power_wait() in kernel */
634d5ac70f0Sopenharmony_ci	if (query_status_data(hw) == -ENODEV)
635d5ac70f0Sopenharmony_ci		return SND_PCM_STATE_DISCONNECTED;
636d5ac70f0Sopenharmony_ci	return (snd_pcm_state_t) hw->mmap_status->state;
637d5ac70f0Sopenharmony_ci}
638d5ac70f0Sopenharmony_ci
639d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
640d5ac70f0Sopenharmony_ci{
641d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
642d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
643d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_DELAY, delayp) < 0) {
644d5ac70f0Sopenharmony_ci		err = -errno;
645d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_DELAY failed (%i)", err);
646d5ac70f0Sopenharmony_ci		return err;
647d5ac70f0Sopenharmony_ci	}
648d5ac70f0Sopenharmony_ci	return 0;
649d5ac70f0Sopenharmony_ci}
650d5ac70f0Sopenharmony_ci
651d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_hwsync(snd_pcm_t *pcm)
652d5ac70f0Sopenharmony_ci{
653d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
654d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
655d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_VERSION(2, 0, 3) <= hw->version) {
656d5ac70f0Sopenharmony_ci		if (hw->mmap_status_fallbacked) {
657d5ac70f0Sopenharmony_ci			err = request_hwsync(hw);
658d5ac70f0Sopenharmony_ci			if (err < 0)
659d5ac70f0Sopenharmony_ci				return err;
660d5ac70f0Sopenharmony_ci		} else {
661d5ac70f0Sopenharmony_ci			if (ioctl(fd, SNDRV_PCM_IOCTL_HWSYNC) < 0) {
662d5ac70f0Sopenharmony_ci				err = -errno;
663d5ac70f0Sopenharmony_ci				SYSMSG("SNDRV_PCM_IOCTL_HWSYNC failed (%i)", err);
664d5ac70f0Sopenharmony_ci				return err;
665d5ac70f0Sopenharmony_ci			}
666d5ac70f0Sopenharmony_ci		}
667d5ac70f0Sopenharmony_ci	} else {
668d5ac70f0Sopenharmony_ci		snd_pcm_sframes_t delay;
669d5ac70f0Sopenharmony_ci		int err = snd_pcm_hw_delay(pcm, &delay);
670d5ac70f0Sopenharmony_ci		if (err < 0) {
671d5ac70f0Sopenharmony_ci			switch (FAST_PCM_STATE(hw)) {
672d5ac70f0Sopenharmony_ci			case SND_PCM_STATE_PREPARED:
673d5ac70f0Sopenharmony_ci			case SND_PCM_STATE_SUSPENDED:
674d5ac70f0Sopenharmony_ci				return 0;
675d5ac70f0Sopenharmony_ci			default:
676d5ac70f0Sopenharmony_ci				return err;
677d5ac70f0Sopenharmony_ci			}
678d5ac70f0Sopenharmony_ci		}
679d5ac70f0Sopenharmony_ci	}
680d5ac70f0Sopenharmony_ci	return 0;
681d5ac70f0Sopenharmony_ci}
682d5ac70f0Sopenharmony_ci
683d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_prepare(snd_pcm_t *pcm)
684d5ac70f0Sopenharmony_ci{
685d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
686d5ac70f0Sopenharmony_ci	snd_pcm_sw_params_t sw_params;
687d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
688d5ac70f0Sopenharmony_ci
689d5ac70f0Sopenharmony_ci	if (hw->prepare_reset_sw_params) {
690d5ac70f0Sopenharmony_ci		snd_pcm_sw_params_current_no_lock(pcm, &sw_params);
691d5ac70f0Sopenharmony_ci		if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params) < 0) {
692d5ac70f0Sopenharmony_ci			err = -errno;
693d5ac70f0Sopenharmony_ci			SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
694d5ac70f0Sopenharmony_ci			return err;
695d5ac70f0Sopenharmony_ci		}
696d5ac70f0Sopenharmony_ci		hw->prepare_reset_sw_params = false;
697d5ac70f0Sopenharmony_ci	}
698d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE) < 0) {
699d5ac70f0Sopenharmony_ci		err = -errno;
700d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_PREPARE failed (%i)", err);
701d5ac70f0Sopenharmony_ci		return err;
702d5ac70f0Sopenharmony_ci	}
703d5ac70f0Sopenharmony_ci	return query_status_and_control_data(hw);
704d5ac70f0Sopenharmony_ci}
705d5ac70f0Sopenharmony_ci
706d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_reset(snd_pcm_t *pcm)
707d5ac70f0Sopenharmony_ci{
708d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
709d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
710d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_RESET) < 0) {
711d5ac70f0Sopenharmony_ci		err = -errno;
712d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_RESET failed (%i)", err);
713d5ac70f0Sopenharmony_ci		return err;
714d5ac70f0Sopenharmony_ci	}
715d5ac70f0Sopenharmony_ci	return query_status_and_control_data(hw);
716d5ac70f0Sopenharmony_ci}
717d5ac70f0Sopenharmony_ci
718d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_start(snd_pcm_t *pcm)
719d5ac70f0Sopenharmony_ci{
720d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
721d5ac70f0Sopenharmony_ci	int err;
722d5ac70f0Sopenharmony_ci#if 0
723d5ac70f0Sopenharmony_ci	assert(pcm->stream != SND_PCM_STREAM_PLAYBACK ||
724d5ac70f0Sopenharmony_ci	       snd_pcm_mmap_playback_hw_avail(pcm) > 0);
725d5ac70f0Sopenharmony_ci#endif
726d5ac70f0Sopenharmony_ci	issue_applptr(hw);
727d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_START) < 0) {
728d5ac70f0Sopenharmony_ci		err = -errno;
729d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_START failed (%i)", err);
730d5ac70f0Sopenharmony_ci#if 0
731d5ac70f0Sopenharmony_ci		if (err == -EBADFD)
732d5ac70f0Sopenharmony_ci			SNDERR("PCM state = %s", snd_pcm_state_name(snd_pcm_hw_state(pcm)));
733d5ac70f0Sopenharmony_ci#endif
734d5ac70f0Sopenharmony_ci		return err;
735d5ac70f0Sopenharmony_ci	}
736d5ac70f0Sopenharmony_ci	return 0;
737d5ac70f0Sopenharmony_ci}
738d5ac70f0Sopenharmony_ci
739d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_drop(snd_pcm_t *pcm)
740d5ac70f0Sopenharmony_ci{
741d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
742d5ac70f0Sopenharmony_ci	int err;
743d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_DROP) < 0) {
744d5ac70f0Sopenharmony_ci		err = -errno;
745d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_DROP failed (%i)", err);
746d5ac70f0Sopenharmony_ci		return err;
747d5ac70f0Sopenharmony_ci	} else {
748d5ac70f0Sopenharmony_ci	}
749d5ac70f0Sopenharmony_ci	return 0;
750d5ac70f0Sopenharmony_ci}
751d5ac70f0Sopenharmony_ci
752d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_drain(snd_pcm_t *pcm)
753d5ac70f0Sopenharmony_ci{
754d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
755d5ac70f0Sopenharmony_ci	snd_pcm_sw_params_t sw_params;
756d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t silence_size;
757d5ac70f0Sopenharmony_ci	int err;
758d5ac70f0Sopenharmony_ci
759d5ac70f0Sopenharmony_ci	if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
760d5ac70f0Sopenharmony_ci		goto __skip_silence;
761d5ac70f0Sopenharmony_ci	/* stream probably in SETUP, prevent divide by zero */
762d5ac70f0Sopenharmony_ci	if (pcm->period_size == 0)
763d5ac70f0Sopenharmony_ci		goto __skip_silence;
764d5ac70f0Sopenharmony_ci	if (hw->drain_silence == 0 || hw->perfect_drain)
765d5ac70f0Sopenharmony_ci		goto __skip_silence;
766d5ac70f0Sopenharmony_ci	snd_pcm_sw_params_current_no_lock(pcm, &sw_params);
767d5ac70f0Sopenharmony_ci	if (hw->drain_silence > 0) {
768d5ac70f0Sopenharmony_ci		silence_size = (pcm->rate * hw->drain_silence) / 1000;
769d5ac70f0Sopenharmony_ci		goto __manual_silence;
770d5ac70f0Sopenharmony_ci	}
771d5ac70f0Sopenharmony_ci	/* compute end silence size, align to period size + extra time */
772d5ac70f0Sopenharmony_ci	if ((pcm->boundary % pcm->period_size) == 0) {
773d5ac70f0Sopenharmony_ci		silence_size = pcm->period_size - (*pcm->appl.ptr % pcm->period_size);
774d5ac70f0Sopenharmony_ci		if (silence_size == pcm->period_size)
775d5ac70f0Sopenharmony_ci			silence_size = 0;
776d5ac70f0Sopenharmony_ci	} else {
777d5ac70f0Sopenharmony_ci		/* it not not easy to compute the period crossing point
778d5ac70f0Sopenharmony_ci		 * in this case because the period is not aligned to the boundary
779d5ac70f0Sopenharmony_ci		 * - use the full range (one period) in this case
780d5ac70f0Sopenharmony_ci		 */
781d5ac70f0Sopenharmony_ci		silence_size = pcm->period_size;
782d5ac70f0Sopenharmony_ci	}
783d5ac70f0Sopenharmony_ci	silence_size += pcm->rate / 10;	/* 1/10th of second */
784d5ac70f0Sopenharmony_ci__manual_silence:
785d5ac70f0Sopenharmony_ci	if (sw_params.silence_size < silence_size) {
786d5ac70f0Sopenharmony_ci		/* fill the silence soon as possible (in the bellow ioctl
787d5ac70f0Sopenharmony_ci		 * or the next period wake up)
788d5ac70f0Sopenharmony_ci		 */
789d5ac70f0Sopenharmony_ci		sw_params.silence_threshold = pcm->buffer_size;
790d5ac70f0Sopenharmony_ci		if (silence_size > pcm->buffer_size)
791d5ac70f0Sopenharmony_ci			silence_size = pcm->buffer_size;
792d5ac70f0Sopenharmony_ci		sw_params.silence_size = silence_size;
793d5ac70f0Sopenharmony_ci		if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params) < 0) {
794d5ac70f0Sopenharmony_ci			err = -errno;
795d5ac70f0Sopenharmony_ci			SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
796d5ac70f0Sopenharmony_ci			return err;
797d5ac70f0Sopenharmony_ci		}
798d5ac70f0Sopenharmony_ci		hw->prepare_reset_sw_params = true;
799d5ac70f0Sopenharmony_ci	}
800d5ac70f0Sopenharmony_ci__skip_silence:
801d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_DRAIN) < 0) {
802d5ac70f0Sopenharmony_ci		err = -errno;
803d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_DRAIN failed (%i)", err);
804d5ac70f0Sopenharmony_ci		return err;
805d5ac70f0Sopenharmony_ci	}
806d5ac70f0Sopenharmony_ci	return 0;
807d5ac70f0Sopenharmony_ci}
808d5ac70f0Sopenharmony_ci
809d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_pause(snd_pcm_t *pcm, int enable)
810d5ac70f0Sopenharmony_ci{
811d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
812d5ac70f0Sopenharmony_ci	int err;
813d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_PAUSE, enable) < 0) {
814d5ac70f0Sopenharmony_ci		err = -errno;
815d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_PAUSE failed (%i)", err);
816d5ac70f0Sopenharmony_ci		return err;
817d5ac70f0Sopenharmony_ci	}
818d5ac70f0Sopenharmony_ci	return 0;
819d5ac70f0Sopenharmony_ci}
820d5ac70f0Sopenharmony_ci
821d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_rewindable(snd_pcm_t *pcm)
822d5ac70f0Sopenharmony_ci{
823d5ac70f0Sopenharmony_ci	return snd_pcm_mmap_hw_rewindable(pcm);
824d5ac70f0Sopenharmony_ci}
825d5ac70f0Sopenharmony_ci
826d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
827d5ac70f0Sopenharmony_ci{
828d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
829d5ac70f0Sopenharmony_ci	int err;
830d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_REWIND, &frames) < 0) {
831d5ac70f0Sopenharmony_ci		err = -errno;
832d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_REWIND failed (%i)", err);
833d5ac70f0Sopenharmony_ci		return err;
834d5ac70f0Sopenharmony_ci	}
835d5ac70f0Sopenharmony_ci	err = query_status_and_control_data(hw);
836d5ac70f0Sopenharmony_ci	if (err < 0)
837d5ac70f0Sopenharmony_ci		return err;
838d5ac70f0Sopenharmony_ci	return frames;
839d5ac70f0Sopenharmony_ci}
840d5ac70f0Sopenharmony_ci
841d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_forwardable(snd_pcm_t *pcm)
842d5ac70f0Sopenharmony_ci{
843d5ac70f0Sopenharmony_ci	return snd_pcm_mmap_avail(pcm);
844d5ac70f0Sopenharmony_ci}
845d5ac70f0Sopenharmony_ci
846d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
847d5ac70f0Sopenharmony_ci{
848d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
849d5ac70f0Sopenharmony_ci	int err;
850d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_VERSION(2, 0, 4) <= hw->version) {
851d5ac70f0Sopenharmony_ci		if (ioctl(hw->fd, SNDRV_PCM_IOCTL_FORWARD, &frames) < 0) {
852d5ac70f0Sopenharmony_ci			err = -errno;
853d5ac70f0Sopenharmony_ci			SYSMSG("SNDRV_PCM_IOCTL_FORWARD failed (%i)", err);
854d5ac70f0Sopenharmony_ci			return err;
855d5ac70f0Sopenharmony_ci		}
856d5ac70f0Sopenharmony_ci		err = query_status_and_control_data(hw);
857d5ac70f0Sopenharmony_ci		if (err < 0)
858d5ac70f0Sopenharmony_ci			return err;
859d5ac70f0Sopenharmony_ci		return frames;
860d5ac70f0Sopenharmony_ci	} else {
861d5ac70f0Sopenharmony_ci		snd_pcm_sframes_t avail;
862d5ac70f0Sopenharmony_ci
863d5ac70f0Sopenharmony_ci		switch (FAST_PCM_STATE(hw)) {
864d5ac70f0Sopenharmony_ci		case SNDRV_PCM_STATE_RUNNING:
865d5ac70f0Sopenharmony_ci		case SNDRV_PCM_STATE_DRAINING:
866d5ac70f0Sopenharmony_ci		case SNDRV_PCM_STATE_PAUSED:
867d5ac70f0Sopenharmony_ci		case SNDRV_PCM_STATE_PREPARED:
868d5ac70f0Sopenharmony_ci			break;
869d5ac70f0Sopenharmony_ci		case SNDRV_PCM_STATE_XRUN:
870d5ac70f0Sopenharmony_ci			return -EPIPE;
871d5ac70f0Sopenharmony_ci		default:
872d5ac70f0Sopenharmony_ci			return -EBADFD;
873d5ac70f0Sopenharmony_ci		}
874d5ac70f0Sopenharmony_ci		avail = snd_pcm_mmap_avail(pcm);
875d5ac70f0Sopenharmony_ci		if (avail < 0)
876d5ac70f0Sopenharmony_ci			return 0;
877d5ac70f0Sopenharmony_ci		if (frames > (snd_pcm_uframes_t)avail)
878d5ac70f0Sopenharmony_ci			frames = avail;
879d5ac70f0Sopenharmony_ci		snd_pcm_mmap_appl_forward(pcm, frames);
880d5ac70f0Sopenharmony_ci		return frames;
881d5ac70f0Sopenharmony_ci	}
882d5ac70f0Sopenharmony_ci}
883d5ac70f0Sopenharmony_ci
884d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_resume(snd_pcm_t *pcm)
885d5ac70f0Sopenharmony_ci{
886d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
887d5ac70f0Sopenharmony_ci	int fd = hw->fd, err;
888d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_RESUME) < 0) {
889d5ac70f0Sopenharmony_ci		err = -errno;
890d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_RESUME failed (%i)", err);
891d5ac70f0Sopenharmony_ci		return err;
892d5ac70f0Sopenharmony_ci	}
893d5ac70f0Sopenharmony_ci	return 0;
894d5ac70f0Sopenharmony_ci}
895d5ac70f0Sopenharmony_ci
896d5ac70f0Sopenharmony_cistatic int hw_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
897d5ac70f0Sopenharmony_ci{
898d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw1 = pcm1->private_data;
899d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw2 = pcm2->private_data;
900d5ac70f0Sopenharmony_ci	if (ioctl(hw1->fd, SNDRV_PCM_IOCTL_LINK, hw2->fd) < 0) {
901d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_LINK failed (%i)", -errno);
902d5ac70f0Sopenharmony_ci		return -errno;
903d5ac70f0Sopenharmony_ci	}
904d5ac70f0Sopenharmony_ci	return 0;
905d5ac70f0Sopenharmony_ci}
906d5ac70f0Sopenharmony_ci
907d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master)
908d5ac70f0Sopenharmony_ci{
909d5ac70f0Sopenharmony_ci	if (master->type != SND_PCM_TYPE_HW) {
910d5ac70f0Sopenharmony_ci		SYSMSG("Invalid type for SNDRV_PCM_IOCTL_LINK (%i)", master->type);
911d5ac70f0Sopenharmony_ci		return -EINVAL;
912d5ac70f0Sopenharmony_ci	}
913d5ac70f0Sopenharmony_ci	return hw_link(master, pcm);
914d5ac70f0Sopenharmony_ci}
915d5ac70f0Sopenharmony_ci
916d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
917d5ac70f0Sopenharmony_ci{
918d5ac70f0Sopenharmony_ci	if (pcm2->type != SND_PCM_TYPE_HW) {
919d5ac70f0Sopenharmony_ci		if (pcm2->fast_ops->link_slaves)
920d5ac70f0Sopenharmony_ci			return pcm2->fast_ops->link_slaves(pcm2->fast_op_arg, pcm1);
921d5ac70f0Sopenharmony_ci		return -ENOSYS;
922d5ac70f0Sopenharmony_ci	}
923d5ac70f0Sopenharmony_ci	return hw_link(pcm1, pcm2);
924d5ac70f0Sopenharmony_ci }
925d5ac70f0Sopenharmony_ci
926d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_unlink(snd_pcm_t *pcm)
927d5ac70f0Sopenharmony_ci{
928d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
929d5ac70f0Sopenharmony_ci
930d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_UNLINK) < 0) {
931d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_UNLINK failed (%i)", -errno);
932d5ac70f0Sopenharmony_ci		return -errno;
933d5ac70f0Sopenharmony_ci	}
934d5ac70f0Sopenharmony_ci	return 0;
935d5ac70f0Sopenharmony_ci}
936d5ac70f0Sopenharmony_ci
937d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
938d5ac70f0Sopenharmony_ci{
939d5ac70f0Sopenharmony_ci	int err;
940d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
941d5ac70f0Sopenharmony_ci	int fd = hw->fd;
942d5ac70f0Sopenharmony_ci	struct snd_xferi xferi;
943d5ac70f0Sopenharmony_ci	xferi.buf = (char*) buffer;
944d5ac70f0Sopenharmony_ci	xferi.frames = size;
945d5ac70f0Sopenharmony_ci	xferi.result = 0; /* make valgrind happy */
946d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi) < 0)
947d5ac70f0Sopenharmony_ci		err = -errno;
948d5ac70f0Sopenharmony_ci	else
949d5ac70f0Sopenharmony_ci		err = query_status_and_control_data(hw);
950d5ac70f0Sopenharmony_ci#ifdef DEBUG_RW
951d5ac70f0Sopenharmony_ci	fprintf(stderr, "hw_writei: frames = %li, xferi.result = %li, err = %i\n", size, xferi.result, err);
952d5ac70f0Sopenharmony_ci#endif
953d5ac70f0Sopenharmony_ci	if (err < 0)
954d5ac70f0Sopenharmony_ci		return snd_pcm_check_error(pcm, err);
955d5ac70f0Sopenharmony_ci	return xferi.result;
956d5ac70f0Sopenharmony_ci}
957d5ac70f0Sopenharmony_ci
958d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
959d5ac70f0Sopenharmony_ci{
960d5ac70f0Sopenharmony_ci	int err;
961d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
962d5ac70f0Sopenharmony_ci	int fd = hw->fd;
963d5ac70f0Sopenharmony_ci	struct snd_xfern xfern;
964d5ac70f0Sopenharmony_ci	memset(&xfern, 0, sizeof(xfern)); /* make valgrind happy */
965d5ac70f0Sopenharmony_ci	xfern.bufs = bufs;
966d5ac70f0Sopenharmony_ci	xfern.frames = size;
967d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_WRITEN_FRAMES, &xfern) < 0)
968d5ac70f0Sopenharmony_ci		err = -errno;
969d5ac70f0Sopenharmony_ci	else
970d5ac70f0Sopenharmony_ci		err = query_status_and_control_data(hw);
971d5ac70f0Sopenharmony_ci#ifdef DEBUG_RW
972d5ac70f0Sopenharmony_ci	fprintf(stderr, "hw_writen: frames = %li, result = %li, err = %i\n", size, xfern.result, err);
973d5ac70f0Sopenharmony_ci#endif
974d5ac70f0Sopenharmony_ci	if (err < 0)
975d5ac70f0Sopenharmony_ci		return snd_pcm_check_error(pcm, err);
976d5ac70f0Sopenharmony_ci	return xfern.result;
977d5ac70f0Sopenharmony_ci}
978d5ac70f0Sopenharmony_ci
979d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
980d5ac70f0Sopenharmony_ci{
981d5ac70f0Sopenharmony_ci	int err;
982d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
983d5ac70f0Sopenharmony_ci	int fd = hw->fd;
984d5ac70f0Sopenharmony_ci	struct snd_xferi xferi;
985d5ac70f0Sopenharmony_ci	xferi.buf = buffer;
986d5ac70f0Sopenharmony_ci	xferi.frames = size;
987d5ac70f0Sopenharmony_ci	xferi.result = 0; /* make valgrind happy */
988d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_READI_FRAMES, &xferi) < 0)
989d5ac70f0Sopenharmony_ci		err = -errno;
990d5ac70f0Sopenharmony_ci	else
991d5ac70f0Sopenharmony_ci		err = query_status_and_control_data(hw);
992d5ac70f0Sopenharmony_ci#ifdef DEBUG_RW
993d5ac70f0Sopenharmony_ci	fprintf(stderr, "hw_readi: frames = %li, result = %li, err = %i\n", size, xferi.result, err);
994d5ac70f0Sopenharmony_ci#endif
995d5ac70f0Sopenharmony_ci	if (err < 0)
996d5ac70f0Sopenharmony_ci		return snd_pcm_check_error(pcm, err);
997d5ac70f0Sopenharmony_ci	return xferi.result;
998d5ac70f0Sopenharmony_ci}
999d5ac70f0Sopenharmony_ci
1000d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
1001d5ac70f0Sopenharmony_ci{
1002d5ac70f0Sopenharmony_ci	int err;
1003d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
1004d5ac70f0Sopenharmony_ci	int fd = hw->fd;
1005d5ac70f0Sopenharmony_ci	struct snd_xfern xfern;
1006d5ac70f0Sopenharmony_ci	memset(&xfern, 0, sizeof(xfern)); /* make valgrind happy */
1007d5ac70f0Sopenharmony_ci	xfern.bufs = bufs;
1008d5ac70f0Sopenharmony_ci	xfern.frames = size;
1009d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_READN_FRAMES, &xfern) < 0)
1010d5ac70f0Sopenharmony_ci		err = -errno;
1011d5ac70f0Sopenharmony_ci	else
1012d5ac70f0Sopenharmony_ci		err = query_status_and_control_data(hw);
1013d5ac70f0Sopenharmony_ci#ifdef DEBUG_RW
1014d5ac70f0Sopenharmony_ci	fprintf(stderr, "hw_readn: frames = %li, result = %li, err = %i\n", size, xfern.result, err);
1015d5ac70f0Sopenharmony_ci#endif
1016d5ac70f0Sopenharmony_ci	if (err < 0)
1017d5ac70f0Sopenharmony_ci		return snd_pcm_check_error(pcm, err);
1018d5ac70f0Sopenharmony_ci	return xfern.result;
1019d5ac70f0Sopenharmony_ci}
1020d5ac70f0Sopenharmony_ci
1021d5ac70f0Sopenharmony_cistatic bool map_status_data(snd_pcm_hw_t *hw, struct snd_pcm_sync_ptr *sync_ptr,
1022d5ac70f0Sopenharmony_ci			    bool force_fallback)
1023d5ac70f0Sopenharmony_ci{
1024d5ac70f0Sopenharmony_ci	struct snd_pcm_mmap_status *mmap_status;
1025d5ac70f0Sopenharmony_ci	bool fallbacked;
1026d5ac70f0Sopenharmony_ci
1027d5ac70f0Sopenharmony_ci	mmap_status = MAP_FAILED;
1028d5ac70f0Sopenharmony_ci	if (!force_fallback) {
1029d5ac70f0Sopenharmony_ci		mmap_status = mmap(NULL, page_align(sizeof(*mmap_status)),
1030d5ac70f0Sopenharmony_ci				   PROT_READ, MAP_FILE|MAP_SHARED,
1031d5ac70f0Sopenharmony_ci				   hw->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
1032d5ac70f0Sopenharmony_ci	}
1033d5ac70f0Sopenharmony_ci
1034d5ac70f0Sopenharmony_ci	if (mmap_status == MAP_FAILED || mmap_status == NULL) {
1035d5ac70f0Sopenharmony_ci		mmap_status = &sync_ptr->s.status;
1036d5ac70f0Sopenharmony_ci		fallbacked = true;
1037d5ac70f0Sopenharmony_ci	} else {
1038d5ac70f0Sopenharmony_ci		fallbacked = false;
1039d5ac70f0Sopenharmony_ci	}
1040d5ac70f0Sopenharmony_ci
1041d5ac70f0Sopenharmony_ci	hw->mmap_status = mmap_status;
1042d5ac70f0Sopenharmony_ci
1043d5ac70f0Sopenharmony_ci	return fallbacked;
1044d5ac70f0Sopenharmony_ci}
1045d5ac70f0Sopenharmony_ci
1046d5ac70f0Sopenharmony_cistatic bool map_control_data(snd_pcm_hw_t *hw,
1047d5ac70f0Sopenharmony_ci			     struct snd_pcm_sync_ptr *sync_ptr,
1048d5ac70f0Sopenharmony_ci			     bool force_fallback)
1049d5ac70f0Sopenharmony_ci{
1050d5ac70f0Sopenharmony_ci	struct snd_pcm_mmap_control *mmap_control;
1051d5ac70f0Sopenharmony_ci	bool fallbacked;
1052d5ac70f0Sopenharmony_ci
1053d5ac70f0Sopenharmony_ci	mmap_control = MAP_FAILED;
1054d5ac70f0Sopenharmony_ci	if (!force_fallback) {
1055d5ac70f0Sopenharmony_ci		mmap_control = mmap(NULL, page_align(sizeof(*mmap_control)),
1056d5ac70f0Sopenharmony_ci				    PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
1057d5ac70f0Sopenharmony_ci				    hw->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
1058d5ac70f0Sopenharmony_ci	}
1059d5ac70f0Sopenharmony_ci
1060d5ac70f0Sopenharmony_ci	if (mmap_control == MAP_FAILED || mmap_control == NULL) {
1061d5ac70f0Sopenharmony_ci		mmap_control = &sync_ptr->c.control;
1062d5ac70f0Sopenharmony_ci		fallbacked = true;
1063d5ac70f0Sopenharmony_ci	} else {
1064d5ac70f0Sopenharmony_ci		fallbacked = false;
1065d5ac70f0Sopenharmony_ci	}
1066d5ac70f0Sopenharmony_ci
1067d5ac70f0Sopenharmony_ci	hw->mmap_control = mmap_control;
1068d5ac70f0Sopenharmony_ci
1069d5ac70f0Sopenharmony_ci	return fallbacked;
1070d5ac70f0Sopenharmony_ci}
1071d5ac70f0Sopenharmony_ci
1072d5ac70f0Sopenharmony_cistatic int map_status_and_control_data(snd_pcm_t *pcm, bool force_fallback)
1073d5ac70f0Sopenharmony_ci{
1074d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
1075d5ac70f0Sopenharmony_ci	struct snd_pcm_sync_ptr *sync_ptr;
1076d5ac70f0Sopenharmony_ci	int err;
1077d5ac70f0Sopenharmony_ci
1078d5ac70f0Sopenharmony_ci	/* Preparation for fallback to failure of mmap(2). */
1079d5ac70f0Sopenharmony_ci	sync_ptr = malloc(sizeof(*sync_ptr));
1080d5ac70f0Sopenharmony_ci	if (sync_ptr == NULL)
1081d5ac70f0Sopenharmony_ci		return -ENOMEM;
1082d5ac70f0Sopenharmony_ci	memset(sync_ptr, 0, sizeof(*sync_ptr));
1083d5ac70f0Sopenharmony_ci
1084d5ac70f0Sopenharmony_ci	hw->mmap_status_fallbacked =
1085d5ac70f0Sopenharmony_ci			map_status_data(hw, sync_ptr, force_fallback);
1086d5ac70f0Sopenharmony_ci	hw->mmap_control_fallbacked =
1087d5ac70f0Sopenharmony_ci			map_control_data(hw, sync_ptr, force_fallback);
1088d5ac70f0Sopenharmony_ci
1089d5ac70f0Sopenharmony_ci	/* Any fallback mode needs to keep the buffer. */
1090d5ac70f0Sopenharmony_ci	if (hw->mmap_status_fallbacked || hw->mmap_control_fallbacked) {
1091d5ac70f0Sopenharmony_ci		hw->sync_ptr = sync_ptr;
1092d5ac70f0Sopenharmony_ci	} else {
1093d5ac70f0Sopenharmony_ci		free(sync_ptr);
1094d5ac70f0Sopenharmony_ci		hw->sync_ptr = NULL;
1095d5ac70f0Sopenharmony_ci	}
1096d5ac70f0Sopenharmony_ci
1097d5ac70f0Sopenharmony_ci	/* do not initialize in case of append and keep the values from the
1098d5ac70f0Sopenharmony_ci	 * kernel
1099d5ac70f0Sopenharmony_ci	 */
1100d5ac70f0Sopenharmony_ci	if (!(pcm->mode & SND_PCM_APPEND)) {
1101d5ac70f0Sopenharmony_ci		/* Initialize the data. */
1102d5ac70f0Sopenharmony_ci		hw->mmap_control->appl_ptr = 0;
1103d5ac70f0Sopenharmony_ci		hw->mmap_control->avail_min = 1;
1104d5ac70f0Sopenharmony_ci	}
1105d5ac70f0Sopenharmony_ci	snd_pcm_set_hw_ptr(pcm, &hw->mmap_status->hw_ptr, hw->fd,
1106d5ac70f0Sopenharmony_ci			   SNDRV_PCM_MMAP_OFFSET_STATUS +
1107d5ac70f0Sopenharmony_ci				offsetof(struct snd_pcm_mmap_status, hw_ptr));
1108d5ac70f0Sopenharmony_ci	snd_pcm_set_appl_ptr(pcm, &hw->mmap_control->appl_ptr, hw->fd,
1109d5ac70f0Sopenharmony_ci			     SNDRV_PCM_MMAP_OFFSET_CONTROL);
1110d5ac70f0Sopenharmony_ci	if (hw->mmap_control_fallbacked) {
1111d5ac70f0Sopenharmony_ci		unsigned int flags = 0;
1112d5ac70f0Sopenharmony_ci		/* read appl_ptr and avail_min from kernel when device opened
1113d5ac70f0Sopenharmony_ci		 * with SND_PCM_APPEND flag
1114d5ac70f0Sopenharmony_ci		 */
1115d5ac70f0Sopenharmony_ci		if (pcm->mode & SND_PCM_APPEND)
1116d5ac70f0Sopenharmony_ci			flags = SNDRV_PCM_SYNC_PTR_APPL |
1117d5ac70f0Sopenharmony_ci				SNDRV_PCM_SYNC_PTR_AVAIL_MIN;
1118d5ac70f0Sopenharmony_ci		err = sync_ptr1(hw, flags);
1119d5ac70f0Sopenharmony_ci		if (err < 0)
1120d5ac70f0Sopenharmony_ci			return err;
1121d5ac70f0Sopenharmony_ci	}
1122d5ac70f0Sopenharmony_ci
1123d5ac70f0Sopenharmony_ci	return 0;
1124d5ac70f0Sopenharmony_ci}
1125d5ac70f0Sopenharmony_ci
1126d5ac70f0Sopenharmony_cistatic void unmap_status_data(snd_pcm_hw_t *hw)
1127d5ac70f0Sopenharmony_ci{
1128d5ac70f0Sopenharmony_ci	if (!hw->mmap_status_fallbacked) {
1129d5ac70f0Sopenharmony_ci		if (munmap((void *)hw->mmap_status,
1130d5ac70f0Sopenharmony_ci			   page_align(sizeof(*hw->mmap_status))) < 0)
1131d5ac70f0Sopenharmony_ci			SYSMSG("status munmap failed (%u)", errno);
1132d5ac70f0Sopenharmony_ci	}
1133d5ac70f0Sopenharmony_ci}
1134d5ac70f0Sopenharmony_ci
1135d5ac70f0Sopenharmony_cistatic void unmap_control_data(snd_pcm_hw_t *hw)
1136d5ac70f0Sopenharmony_ci{
1137d5ac70f0Sopenharmony_ci	if (!hw->mmap_control_fallbacked) {
1138d5ac70f0Sopenharmony_ci		if (munmap((void *)hw->mmap_control,
1139d5ac70f0Sopenharmony_ci			   page_align(sizeof(*hw->mmap_control))) < 0)
1140d5ac70f0Sopenharmony_ci			SYSMSG("control munmap failed (%u)", errno);
1141d5ac70f0Sopenharmony_ci	}
1142d5ac70f0Sopenharmony_ci}
1143d5ac70f0Sopenharmony_ci
1144d5ac70f0Sopenharmony_cistatic void unmap_status_and_control_data(snd_pcm_hw_t *hw)
1145d5ac70f0Sopenharmony_ci{
1146d5ac70f0Sopenharmony_ci	unmap_status_data(hw);
1147d5ac70f0Sopenharmony_ci	unmap_control_data(hw);
1148d5ac70f0Sopenharmony_ci
1149d5ac70f0Sopenharmony_ci	if (hw->mmap_status_fallbacked || hw->mmap_control_fallbacked)
1150d5ac70f0Sopenharmony_ci		free(hw->sync_ptr);
1151d5ac70f0Sopenharmony_ci
1152d5ac70f0Sopenharmony_ci	hw->mmap_status = NULL;
1153d5ac70f0Sopenharmony_ci	hw->mmap_control = NULL;
1154d5ac70f0Sopenharmony_ci	hw->mmap_status_fallbacked = false;
1155d5ac70f0Sopenharmony_ci	hw->mmap_control_fallbacked = false;
1156d5ac70f0Sopenharmony_ci	hw->sync_ptr = NULL;
1157d5ac70f0Sopenharmony_ci}
1158d5ac70f0Sopenharmony_ci
1159d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
1160d5ac70f0Sopenharmony_ci{
1161d5ac70f0Sopenharmony_ci	return 0;
1162d5ac70f0Sopenharmony_ci}
1163d5ac70f0Sopenharmony_ci
1164d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
1165d5ac70f0Sopenharmony_ci{
1166d5ac70f0Sopenharmony_ci	return 0;
1167d5ac70f0Sopenharmony_ci}
1168d5ac70f0Sopenharmony_ci
1169d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_close(snd_pcm_t *pcm)
1170d5ac70f0Sopenharmony_ci{
1171d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
1172d5ac70f0Sopenharmony_ci	int err = 0;
1173d5ac70f0Sopenharmony_ci	if (close(hw->fd)) {
1174d5ac70f0Sopenharmony_ci		err = -errno;
1175d5ac70f0Sopenharmony_ci		SYSMSG("close failed (%i)", err);
1176d5ac70f0Sopenharmony_ci	}
1177d5ac70f0Sopenharmony_ci
1178d5ac70f0Sopenharmony_ci	unmap_status_and_control_data(hw);
1179d5ac70f0Sopenharmony_ci
1180d5ac70f0Sopenharmony_ci	free(hw);
1181d5ac70f0Sopenharmony_ci	return err;
1182d5ac70f0Sopenharmony_ci}
1183d5ac70f0Sopenharmony_ci
1184d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_mmap_commit(snd_pcm_t *pcm,
1185d5ac70f0Sopenharmony_ci						snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
1186d5ac70f0Sopenharmony_ci						snd_pcm_uframes_t size)
1187d5ac70f0Sopenharmony_ci{
1188d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
1189d5ac70f0Sopenharmony_ci
1190d5ac70f0Sopenharmony_ci	snd_pcm_mmap_appl_forward(pcm, size);
1191d5ac70f0Sopenharmony_ci	issue_applptr(hw);
1192d5ac70f0Sopenharmony_ci#ifdef DEBUG_MMAP
1193d5ac70f0Sopenharmony_ci	fprintf(stderr, "appl_forward: hw_ptr = %li, appl_ptr = %li, size = %li\n", *pcm->hw.ptr, *pcm->appl.ptr, size);
1194d5ac70f0Sopenharmony_ci#endif
1195d5ac70f0Sopenharmony_ci	return size;
1196d5ac70f0Sopenharmony_ci}
1197d5ac70f0Sopenharmony_ci
1198d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
1199d5ac70f0Sopenharmony_ci{
1200d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
1201d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t avail;
1202d5ac70f0Sopenharmony_ci
1203d5ac70f0Sopenharmony_ci	query_status_data(hw);
1204d5ac70f0Sopenharmony_ci	avail = snd_pcm_mmap_avail(pcm);
1205d5ac70f0Sopenharmony_ci	switch (FAST_PCM_STATE(hw)) {
1206d5ac70f0Sopenharmony_ci	case SNDRV_PCM_STATE_RUNNING:
1207d5ac70f0Sopenharmony_ci		if (avail >= pcm->stop_threshold) {
1208d5ac70f0Sopenharmony_ci			/* SNDRV_PCM_IOCTL_XRUN ioctl has been implemented since PCM kernel API 2.0.1 */
1209d5ac70f0Sopenharmony_ci			if (SNDRV_PROTOCOL_VERSION(2, 0, 1) <= hw->version) {
1210d5ac70f0Sopenharmony_ci				if (ioctl(hw->fd, SNDRV_PCM_IOCTL_XRUN) < 0)
1211d5ac70f0Sopenharmony_ci					return -errno;
1212d5ac70f0Sopenharmony_ci			}
1213d5ac70f0Sopenharmony_ci			/* everything is ok, state == SND_PCM_STATE_XRUN at the moment */
1214d5ac70f0Sopenharmony_ci			return -EPIPE;
1215d5ac70f0Sopenharmony_ci		}
1216d5ac70f0Sopenharmony_ci		break;
1217d5ac70f0Sopenharmony_ci	case SNDRV_PCM_STATE_XRUN:
1218d5ac70f0Sopenharmony_ci		return -EPIPE;
1219d5ac70f0Sopenharmony_ci	default:
1220d5ac70f0Sopenharmony_ci		break;
1221d5ac70f0Sopenharmony_ci	}
1222d5ac70f0Sopenharmony_ci	return avail;
1223d5ac70f0Sopenharmony_ci}
1224d5ac70f0Sopenharmony_ci
1225d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail,
1226d5ac70f0Sopenharmony_ci				 snd_htimestamp_t *tstamp)
1227d5ac70f0Sopenharmony_ci{
1228d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t avail1;
1229d5ac70f0Sopenharmony_ci	int ok = 0;
1230d5ac70f0Sopenharmony_ci
1231d5ac70f0Sopenharmony_ci	/* unfortunately, loop is necessary to ensure valid timestamp */
1232d5ac70f0Sopenharmony_ci	while (1) {
1233d5ac70f0Sopenharmony_ci		avail1 = snd_pcm_hw_avail_update(pcm);
1234d5ac70f0Sopenharmony_ci		if (avail1 < 0)
1235d5ac70f0Sopenharmony_ci			return avail1;
1236d5ac70f0Sopenharmony_ci		if (ok && (snd_pcm_uframes_t)avail1 == *avail)
1237d5ac70f0Sopenharmony_ci			break;
1238d5ac70f0Sopenharmony_ci		*avail = avail1;
1239d5ac70f0Sopenharmony_ci		*tstamp = snd_pcm_hw_fast_tstamp(pcm);
1240d5ac70f0Sopenharmony_ci		ok = 1;
1241d5ac70f0Sopenharmony_ci	}
1242d5ac70f0Sopenharmony_ci	return 0;
1243d5ac70f0Sopenharmony_ci}
1244d5ac70f0Sopenharmony_ci
1245d5ac70f0Sopenharmony_cistatic void __fill_chmap_ctl_id(snd_ctl_elem_id_t *id, int dev, int subdev,
1246d5ac70f0Sopenharmony_ci				int stream)
1247d5ac70f0Sopenharmony_ci{
1248d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
1249d5ac70f0Sopenharmony_ci	if (stream == SND_PCM_STREAM_PLAYBACK)
1250d5ac70f0Sopenharmony_ci		snd_ctl_elem_id_set_name(id, "Playback Channel Map");
1251d5ac70f0Sopenharmony_ci	else
1252d5ac70f0Sopenharmony_ci		snd_ctl_elem_id_set_name(id, "Capture Channel Map");
1253d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_set_device(id, dev);
1254d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_set_index(id, subdev);
1255d5ac70f0Sopenharmony_ci}
1256d5ac70f0Sopenharmony_ci
1257d5ac70f0Sopenharmony_cistatic void fill_chmap_ctl_id(snd_pcm_t *pcm, snd_ctl_elem_id_t *id)
1258d5ac70f0Sopenharmony_ci{
1259d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
1260d5ac70f0Sopenharmony_ci	__fill_chmap_ctl_id(id, hw->device, hw->subdevice, pcm->stream);
1261d5ac70f0Sopenharmony_ci}
1262d5ac70f0Sopenharmony_ci
1263d5ac70f0Sopenharmony_cistatic int is_chmap_type(int type)
1264d5ac70f0Sopenharmony_ci{
1265d5ac70f0Sopenharmony_ci	return (type >= SND_CTL_TLVT_CHMAP_FIXED &&
1266d5ac70f0Sopenharmony_ci		type <= SND_CTL_TLVT_CHMAP_PAIRED);
1267d5ac70f0Sopenharmony_ci}
1268d5ac70f0Sopenharmony_ci
1269d5ac70f0Sopenharmony_ci/**
1270d5ac70f0Sopenharmony_ci * \!brief Query the available channel maps
1271d5ac70f0Sopenharmony_ci * \param card the card number
1272d5ac70f0Sopenharmony_ci * \param dev the PCM device number
1273d5ac70f0Sopenharmony_ci * \param subdev the PCM substream index
1274d5ac70f0Sopenharmony_ci * \param stream the direction of PCM stream
1275d5ac70f0Sopenharmony_ci * \return the NULL-terminated array of integer pointers, or NULL at error.
1276d5ac70f0Sopenharmony_ci *
1277d5ac70f0Sopenharmony_ci * This function works like snd_pcm_query_chmaps() but it takes the card,
1278d5ac70f0Sopenharmony_ci * device, substream and stream numbers instead of the already opened
1279d5ac70f0Sopenharmony_ci * snd_pcm_t instance, so that you can query available channel maps of
1280d5ac70f0Sopenharmony_ci * a PCM before actually opening it.
1281d5ac70f0Sopenharmony_ci *
1282d5ac70f0Sopenharmony_ci * As the parameters stand, the query is performed only to the hw PCM
1283d5ac70f0Sopenharmony_ci * devices, not the abstracted PCM object in alsa-lib.
1284d5ac70f0Sopenharmony_ci */
1285d5ac70f0Sopenharmony_cisnd_pcm_chmap_query_t **
1286d5ac70f0Sopenharmony_cisnd_pcm_query_chmaps_from_hw(int card, int dev, int subdev,
1287d5ac70f0Sopenharmony_ci			     snd_pcm_stream_t stream)
1288d5ac70f0Sopenharmony_ci{
1289d5ac70f0Sopenharmony_ci	snd_ctl_t *ctl;
1290d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_t id = {0};
1291d5ac70f0Sopenharmony_ci	unsigned int tlv[2048], *start;
1292d5ac70f0Sopenharmony_ci	unsigned int type;
1293d5ac70f0Sopenharmony_ci	snd_pcm_chmap_query_t **map;
1294d5ac70f0Sopenharmony_ci	int i, ret, nums;
1295d5ac70f0Sopenharmony_ci
1296d5ac70f0Sopenharmony_ci	ret = snd_ctl_hw_open(&ctl, NULL, card, 0);
1297d5ac70f0Sopenharmony_ci	if (ret < 0) {
1298d5ac70f0Sopenharmony_ci		SYSMSG("Cannot open the associated CTL");
1299d5ac70f0Sopenharmony_ci		return NULL;
1300d5ac70f0Sopenharmony_ci	}
1301d5ac70f0Sopenharmony_ci
1302d5ac70f0Sopenharmony_ci	__fill_chmap_ctl_id(&id, dev, subdev, stream);
1303d5ac70f0Sopenharmony_ci	ret = snd_ctl_elem_tlv_read(ctl, &id, tlv, sizeof(tlv));
1304d5ac70f0Sopenharmony_ci	snd_ctl_close(ctl);
1305d5ac70f0Sopenharmony_ci	if (ret < 0) {
1306d5ac70f0Sopenharmony_ci		SYSMSG("Cannot read Channel Map TLV");
1307d5ac70f0Sopenharmony_ci		return NULL;
1308d5ac70f0Sopenharmony_ci	}
1309d5ac70f0Sopenharmony_ci
1310d5ac70f0Sopenharmony_ci#if 0
1311d5ac70f0Sopenharmony_ci	for (i = 0; i < 32; i++)
1312d5ac70f0Sopenharmony_ci		fprintf(stderr, "%02x: %08x\n", i, tlv[i]);
1313d5ac70f0Sopenharmony_ci#endif
1314d5ac70f0Sopenharmony_ci	/* FIXME: the parser below assumes that the TLV only contains
1315d5ac70f0Sopenharmony_ci	 * chmap-related blocks
1316d5ac70f0Sopenharmony_ci	 */
1317d5ac70f0Sopenharmony_ci	type = tlv[SNDRV_CTL_TLVO_TYPE];
1318d5ac70f0Sopenharmony_ci	if (type != SND_CTL_TLVT_CONTAINER) {
1319d5ac70f0Sopenharmony_ci		if (!is_chmap_type(type)) {
1320d5ac70f0Sopenharmony_ci			SYSMSG("Invalid TLV type %d", type);
1321d5ac70f0Sopenharmony_ci			return NULL;
1322d5ac70f0Sopenharmony_ci		}
1323d5ac70f0Sopenharmony_ci		start = tlv;
1324d5ac70f0Sopenharmony_ci		nums = 1;
1325d5ac70f0Sopenharmony_ci	} else {
1326d5ac70f0Sopenharmony_ci		unsigned int *p;
1327d5ac70f0Sopenharmony_ci		int size;
1328d5ac70f0Sopenharmony_ci		start = tlv + 2;
1329d5ac70f0Sopenharmony_ci		size = tlv[SNDRV_CTL_TLVO_LEN];
1330d5ac70f0Sopenharmony_ci		nums = 0;
1331d5ac70f0Sopenharmony_ci		for (p = start; size > 0; ) {
1332d5ac70f0Sopenharmony_ci			if (!is_chmap_type(p[0])) {
1333d5ac70f0Sopenharmony_ci				SYSMSG("Invalid TLV type %d", p[0]);
1334d5ac70f0Sopenharmony_ci				return NULL;
1335d5ac70f0Sopenharmony_ci			}
1336d5ac70f0Sopenharmony_ci			nums++;
1337d5ac70f0Sopenharmony_ci			size -= p[1] + 8;
1338d5ac70f0Sopenharmony_ci			p += p[1] / 4 + 2;
1339d5ac70f0Sopenharmony_ci		}
1340d5ac70f0Sopenharmony_ci	}
1341d5ac70f0Sopenharmony_ci	map = calloc(nums + 1, sizeof(int *));
1342d5ac70f0Sopenharmony_ci	if (!map)
1343d5ac70f0Sopenharmony_ci		return NULL;
1344d5ac70f0Sopenharmony_ci	for (i = 0; i < nums; i++) {
1345d5ac70f0Sopenharmony_ci		map[i] = malloc(start[1] + 8);
1346d5ac70f0Sopenharmony_ci		if (!map[i]) {
1347d5ac70f0Sopenharmony_ci			snd_pcm_free_chmaps(map);
1348d5ac70f0Sopenharmony_ci			return NULL;
1349d5ac70f0Sopenharmony_ci		}
1350d5ac70f0Sopenharmony_ci		map[i]->type = start[0] - 0x100;
1351d5ac70f0Sopenharmony_ci		map[i]->map.channels = start[1] / 4;
1352d5ac70f0Sopenharmony_ci		memcpy(map[i]->map.pos, start + 2, start[1]);
1353d5ac70f0Sopenharmony_ci		start += start[1] / 4 + 2;
1354d5ac70f0Sopenharmony_ci	}
1355d5ac70f0Sopenharmony_ci	return map;
1356d5ac70f0Sopenharmony_ci}
1357d5ac70f0Sopenharmony_ci
1358d5ac70f0Sopenharmony_cienum { CHMAP_CTL_QUERY, CHMAP_CTL_GET, CHMAP_CTL_SET };
1359d5ac70f0Sopenharmony_ci
1360d5ac70f0Sopenharmony_cistatic int chmap_caps(snd_pcm_hw_t *hw, int type)
1361d5ac70f0Sopenharmony_ci{
1362d5ac70f0Sopenharmony_ci	if (hw->chmap_caps & (1 << type))
1363d5ac70f0Sopenharmony_ci		return 1;
1364d5ac70f0Sopenharmony_ci	if (hw->chmap_caps & (1 << (type + 8)))
1365d5ac70f0Sopenharmony_ci		return 0;
1366d5ac70f0Sopenharmony_ci	return 1;
1367d5ac70f0Sopenharmony_ci}
1368d5ac70f0Sopenharmony_ci
1369d5ac70f0Sopenharmony_cistatic void chmap_caps_set_ok(snd_pcm_hw_t *hw, int type)
1370d5ac70f0Sopenharmony_ci{
1371d5ac70f0Sopenharmony_ci	hw->chmap_caps |= (1 << type);
1372d5ac70f0Sopenharmony_ci}
1373d5ac70f0Sopenharmony_ci
1374d5ac70f0Sopenharmony_cistatic void chmap_caps_set_error(snd_pcm_hw_t *hw, int type)
1375d5ac70f0Sopenharmony_ci{
1376d5ac70f0Sopenharmony_ci	hw->chmap_caps |= (1 << (type + 8));
1377d5ac70f0Sopenharmony_ci}
1378d5ac70f0Sopenharmony_ci
1379d5ac70f0Sopenharmony_cistatic snd_pcm_chmap_query_t **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm)
1380d5ac70f0Sopenharmony_ci{
1381d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
1382d5ac70f0Sopenharmony_ci	snd_pcm_chmap_query_t **map;
1383d5ac70f0Sopenharmony_ci
1384d5ac70f0Sopenharmony_ci	if (hw->chmap_override)
1385d5ac70f0Sopenharmony_ci		return _snd_pcm_copy_chmap_query(hw->chmap_override);
1386d5ac70f0Sopenharmony_ci
1387d5ac70f0Sopenharmony_ci	if (!chmap_caps(hw, CHMAP_CTL_QUERY))
1388d5ac70f0Sopenharmony_ci		return NULL;
1389d5ac70f0Sopenharmony_ci
1390d5ac70f0Sopenharmony_ci	map = snd_pcm_query_chmaps_from_hw(hw->card, hw->device,
1391d5ac70f0Sopenharmony_ci					   hw->subdevice, pcm->stream);
1392d5ac70f0Sopenharmony_ci	if (map)
1393d5ac70f0Sopenharmony_ci		chmap_caps_set_ok(hw, CHMAP_CTL_QUERY);
1394d5ac70f0Sopenharmony_ci	else
1395d5ac70f0Sopenharmony_ci		chmap_caps_set_error(hw, CHMAP_CTL_QUERY);
1396d5ac70f0Sopenharmony_ci	return map;
1397d5ac70f0Sopenharmony_ci}
1398d5ac70f0Sopenharmony_ci
1399d5ac70f0Sopenharmony_cistatic snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm)
1400d5ac70f0Sopenharmony_ci{
1401d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
1402d5ac70f0Sopenharmony_ci	snd_pcm_chmap_t *map;
1403d5ac70f0Sopenharmony_ci	snd_ctl_t *ctl;
1404d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_t id = {0};
1405d5ac70f0Sopenharmony_ci	snd_ctl_elem_value_t val = {0};
1406d5ac70f0Sopenharmony_ci	unsigned int i;
1407d5ac70f0Sopenharmony_ci	int ret;
1408d5ac70f0Sopenharmony_ci
1409d5ac70f0Sopenharmony_ci	if (hw->chmap_override)
1410d5ac70f0Sopenharmony_ci		return _snd_pcm_choose_fixed_chmap(pcm, hw->chmap_override);
1411d5ac70f0Sopenharmony_ci
1412d5ac70f0Sopenharmony_ci	if (!chmap_caps(hw, CHMAP_CTL_GET))
1413d5ac70f0Sopenharmony_ci		return NULL;
1414d5ac70f0Sopenharmony_ci
1415d5ac70f0Sopenharmony_ci	switch (FAST_PCM_STATE(hw)) {
1416d5ac70f0Sopenharmony_ci	case SNDRV_PCM_STATE_PREPARED:
1417d5ac70f0Sopenharmony_ci	case SNDRV_PCM_STATE_RUNNING:
1418d5ac70f0Sopenharmony_ci	case SNDRV_PCM_STATE_XRUN:
1419d5ac70f0Sopenharmony_ci	case SNDRV_PCM_STATE_DRAINING:
1420d5ac70f0Sopenharmony_ci	case SNDRV_PCM_STATE_PAUSED:
1421d5ac70f0Sopenharmony_ci	case SNDRV_PCM_STATE_SUSPENDED:
1422d5ac70f0Sopenharmony_ci		break;
1423d5ac70f0Sopenharmony_ci	default:
1424d5ac70f0Sopenharmony_ci		SYSMSG("Invalid PCM state for chmap_get: %s",
1425d5ac70f0Sopenharmony_ci		       snd_pcm_state_name(FAST_PCM_STATE(hw)));
1426d5ac70f0Sopenharmony_ci		return NULL;
1427d5ac70f0Sopenharmony_ci	}
1428d5ac70f0Sopenharmony_ci	map = malloc(pcm->channels * sizeof(map->pos[0]) + sizeof(*map));
1429d5ac70f0Sopenharmony_ci	if (!map)
1430d5ac70f0Sopenharmony_ci		return NULL;
1431d5ac70f0Sopenharmony_ci	map->channels = pcm->channels;
1432d5ac70f0Sopenharmony_ci	ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0);
1433d5ac70f0Sopenharmony_ci	if (ret < 0) {
1434d5ac70f0Sopenharmony_ci		free(map);
1435d5ac70f0Sopenharmony_ci		SYSMSG("Cannot open the associated CTL");
1436d5ac70f0Sopenharmony_ci		chmap_caps_set_error(hw, CHMAP_CTL_GET);
1437d5ac70f0Sopenharmony_ci		return NULL;
1438d5ac70f0Sopenharmony_ci	}
1439d5ac70f0Sopenharmony_ci	fill_chmap_ctl_id(pcm, &id);
1440d5ac70f0Sopenharmony_ci	snd_ctl_elem_value_set_id(&val, &id);
1441d5ac70f0Sopenharmony_ci	ret = snd_ctl_elem_read(ctl, &val);
1442d5ac70f0Sopenharmony_ci	snd_ctl_close(ctl);
1443d5ac70f0Sopenharmony_ci	if (ret < 0) {
1444d5ac70f0Sopenharmony_ci		free(map);
1445d5ac70f0Sopenharmony_ci		SYSMSG("Cannot read Channel Map ctl");
1446d5ac70f0Sopenharmony_ci		chmap_caps_set_error(hw, CHMAP_CTL_GET);
1447d5ac70f0Sopenharmony_ci		return NULL;
1448d5ac70f0Sopenharmony_ci	}
1449d5ac70f0Sopenharmony_ci	for (i = 0; i < pcm->channels; i++)
1450d5ac70f0Sopenharmony_ci		map->pos[i] = snd_ctl_elem_value_get_integer(&val, i);
1451d5ac70f0Sopenharmony_ci	chmap_caps_set_ok(hw, CHMAP_CTL_GET);
1452d5ac70f0Sopenharmony_ci	return map;
1453d5ac70f0Sopenharmony_ci}
1454d5ac70f0Sopenharmony_ci
1455d5ac70f0Sopenharmony_cistatic int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
1456d5ac70f0Sopenharmony_ci{
1457d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
1458d5ac70f0Sopenharmony_ci	snd_ctl_t *ctl;
1459d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_t id = {0};
1460d5ac70f0Sopenharmony_ci	snd_ctl_elem_value_t val = {0};
1461d5ac70f0Sopenharmony_ci	unsigned int i;
1462d5ac70f0Sopenharmony_ci	int ret;
1463d5ac70f0Sopenharmony_ci
1464d5ac70f0Sopenharmony_ci	if (hw->chmap_override)
1465d5ac70f0Sopenharmony_ci		return -ENXIO;
1466d5ac70f0Sopenharmony_ci
1467d5ac70f0Sopenharmony_ci	if (!chmap_caps(hw, CHMAP_CTL_SET))
1468d5ac70f0Sopenharmony_ci		return -ENXIO;
1469d5ac70f0Sopenharmony_ci
1470d5ac70f0Sopenharmony_ci	if (map->channels > 128) {
1471d5ac70f0Sopenharmony_ci		SYSMSG("Invalid number of channels %d", map->channels);
1472d5ac70f0Sopenharmony_ci		return -EINVAL;
1473d5ac70f0Sopenharmony_ci	}
1474d5ac70f0Sopenharmony_ci	if (FAST_PCM_STATE(hw) != SNDRV_PCM_STATE_PREPARED) {
1475d5ac70f0Sopenharmony_ci		SYSMSG("Invalid PCM state for chmap_set: %s",
1476d5ac70f0Sopenharmony_ci		       snd_pcm_state_name(FAST_PCM_STATE(hw)));
1477d5ac70f0Sopenharmony_ci		return -EBADFD;
1478d5ac70f0Sopenharmony_ci	}
1479d5ac70f0Sopenharmony_ci	ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0);
1480d5ac70f0Sopenharmony_ci	if (ret < 0) {
1481d5ac70f0Sopenharmony_ci		SYSMSG("Cannot open the associated CTL");
1482d5ac70f0Sopenharmony_ci		chmap_caps_set_error(hw, CHMAP_CTL_SET);
1483d5ac70f0Sopenharmony_ci		return ret;
1484d5ac70f0Sopenharmony_ci	}
1485d5ac70f0Sopenharmony_ci
1486d5ac70f0Sopenharmony_ci	fill_chmap_ctl_id(pcm, &id);
1487d5ac70f0Sopenharmony_ci	snd_ctl_elem_value_set_id(&val, &id);
1488d5ac70f0Sopenharmony_ci	for (i = 0; i < map->channels; i++)
1489d5ac70f0Sopenharmony_ci		snd_ctl_elem_value_set_integer(&val, i, map->pos[i]);
1490d5ac70f0Sopenharmony_ci	ret = snd_ctl_elem_write(ctl, &val);
1491d5ac70f0Sopenharmony_ci	snd_ctl_close(ctl);
1492d5ac70f0Sopenharmony_ci	if (ret >= 0)
1493d5ac70f0Sopenharmony_ci		chmap_caps_set_ok(hw, CHMAP_CTL_SET);
1494d5ac70f0Sopenharmony_ci	else if (ret == -ENOENT || ret == -EPERM || ret == -ENXIO) {
1495d5ac70f0Sopenharmony_ci		chmap_caps_set_error(hw, CHMAP_CTL_SET);
1496d5ac70f0Sopenharmony_ci		ret = -ENXIO;
1497d5ac70f0Sopenharmony_ci	}
1498d5ac70f0Sopenharmony_ci	if (ret < 0)
1499d5ac70f0Sopenharmony_ci		SYSMSG("Cannot write Channel Map ctl");
1500d5ac70f0Sopenharmony_ci	return ret;
1501d5ac70f0Sopenharmony_ci}
1502d5ac70f0Sopenharmony_ci
1503d5ac70f0Sopenharmony_cistatic void snd_pcm_hw_dump(snd_pcm_t *pcm, snd_output_t *out)
1504d5ac70f0Sopenharmony_ci{
1505d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = pcm->private_data;
1506d5ac70f0Sopenharmony_ci	char *name;
1507d5ac70f0Sopenharmony_ci	int err = snd_card_get_name(hw->card, &name);
1508d5ac70f0Sopenharmony_ci	if (err < 0) {
1509d5ac70f0Sopenharmony_ci		SNDERR("cannot get card name");
1510d5ac70f0Sopenharmony_ci		return;
1511d5ac70f0Sopenharmony_ci	}
1512d5ac70f0Sopenharmony_ci	snd_output_printf(out, "Hardware PCM card %d '%s' device %d subdevice %d\n",
1513d5ac70f0Sopenharmony_ci			  hw->card, name, hw->device, hw->subdevice);
1514d5ac70f0Sopenharmony_ci	free(name);
1515d5ac70f0Sopenharmony_ci	if (pcm->setup) {
1516d5ac70f0Sopenharmony_ci		snd_output_printf(out, "Its setup is:\n");
1517d5ac70f0Sopenharmony_ci		snd_pcm_dump_setup(pcm, out);
1518d5ac70f0Sopenharmony_ci		snd_output_printf(out, "  appl_ptr     : %li\n", hw->mmap_control->appl_ptr);
1519d5ac70f0Sopenharmony_ci		snd_output_printf(out, "  hw_ptr       : %li\n", hw->mmap_status->hw_ptr);
1520d5ac70f0Sopenharmony_ci	}
1521d5ac70f0Sopenharmony_ci}
1522d5ac70f0Sopenharmony_ci
1523d5ac70f0Sopenharmony_cistatic const snd_pcm_ops_t snd_pcm_hw_ops = {
1524d5ac70f0Sopenharmony_ci	.close = snd_pcm_hw_close,
1525d5ac70f0Sopenharmony_ci	.info = snd_pcm_hw_info,
1526d5ac70f0Sopenharmony_ci	.hw_refine = snd_pcm_hw_hw_refine,
1527d5ac70f0Sopenharmony_ci	.hw_params = snd_pcm_hw_hw_params,
1528d5ac70f0Sopenharmony_ci	.hw_free = snd_pcm_hw_hw_free,
1529d5ac70f0Sopenharmony_ci	.sw_params = snd_pcm_hw_sw_params,
1530d5ac70f0Sopenharmony_ci	.channel_info = snd_pcm_hw_channel_info,
1531d5ac70f0Sopenharmony_ci	.dump = snd_pcm_hw_dump,
1532d5ac70f0Sopenharmony_ci	.nonblock = snd_pcm_hw_nonblock,
1533d5ac70f0Sopenharmony_ci	.async = snd_pcm_hw_async,
1534d5ac70f0Sopenharmony_ci	.mmap = snd_pcm_hw_mmap,
1535d5ac70f0Sopenharmony_ci	.munmap = snd_pcm_hw_munmap,
1536d5ac70f0Sopenharmony_ci	.query_chmaps = snd_pcm_hw_query_chmaps,
1537d5ac70f0Sopenharmony_ci	.get_chmap = snd_pcm_hw_get_chmap,
1538d5ac70f0Sopenharmony_ci	.set_chmap = snd_pcm_hw_set_chmap,
1539d5ac70f0Sopenharmony_ci};
1540d5ac70f0Sopenharmony_ci
1541d5ac70f0Sopenharmony_cistatic const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = {
1542d5ac70f0Sopenharmony_ci	.status = snd_pcm_hw_status,
1543d5ac70f0Sopenharmony_ci	.state = snd_pcm_hw_state,
1544d5ac70f0Sopenharmony_ci	.hwsync = snd_pcm_hw_hwsync,
1545d5ac70f0Sopenharmony_ci	.delay = snd_pcm_hw_delay,
1546d5ac70f0Sopenharmony_ci	.prepare = snd_pcm_hw_prepare,
1547d5ac70f0Sopenharmony_ci	.reset = snd_pcm_hw_reset,
1548d5ac70f0Sopenharmony_ci	.start = snd_pcm_hw_start,
1549d5ac70f0Sopenharmony_ci	.drop = snd_pcm_hw_drop,
1550d5ac70f0Sopenharmony_ci	.drain = snd_pcm_hw_drain,
1551d5ac70f0Sopenharmony_ci	.pause = snd_pcm_hw_pause,
1552d5ac70f0Sopenharmony_ci	.rewindable = snd_pcm_hw_rewindable,
1553d5ac70f0Sopenharmony_ci	.rewind = snd_pcm_hw_rewind,
1554d5ac70f0Sopenharmony_ci	.forwardable = snd_pcm_hw_forwardable,
1555d5ac70f0Sopenharmony_ci	.forward = snd_pcm_hw_forward,
1556d5ac70f0Sopenharmony_ci	.resume = snd_pcm_hw_resume,
1557d5ac70f0Sopenharmony_ci	.link = snd_pcm_hw_link,
1558d5ac70f0Sopenharmony_ci	.link_slaves = snd_pcm_hw_link_slaves,
1559d5ac70f0Sopenharmony_ci	.unlink = snd_pcm_hw_unlink,
1560d5ac70f0Sopenharmony_ci	.writei = snd_pcm_hw_writei,
1561d5ac70f0Sopenharmony_ci	.writen = snd_pcm_hw_writen,
1562d5ac70f0Sopenharmony_ci	.readi = snd_pcm_hw_readi,
1563d5ac70f0Sopenharmony_ci	.readn = snd_pcm_hw_readn,
1564d5ac70f0Sopenharmony_ci	.avail_update = snd_pcm_hw_avail_update,
1565d5ac70f0Sopenharmony_ci	.mmap_commit = snd_pcm_hw_mmap_commit,
1566d5ac70f0Sopenharmony_ci	.htimestamp = snd_pcm_hw_htimestamp,
1567d5ac70f0Sopenharmony_ci	.poll_descriptors = NULL,
1568d5ac70f0Sopenharmony_ci	.poll_descriptors_count = NULL,
1569d5ac70f0Sopenharmony_ci	.poll_revents = NULL,
1570d5ac70f0Sopenharmony_ci};
1571d5ac70f0Sopenharmony_ci
1572d5ac70f0Sopenharmony_cistatic const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops_timer = {
1573d5ac70f0Sopenharmony_ci	.status = snd_pcm_hw_status,
1574d5ac70f0Sopenharmony_ci	.state = snd_pcm_hw_state,
1575d5ac70f0Sopenharmony_ci	.hwsync = snd_pcm_hw_hwsync,
1576d5ac70f0Sopenharmony_ci	.delay = snd_pcm_hw_delay,
1577d5ac70f0Sopenharmony_ci	.prepare = snd_pcm_hw_prepare,
1578d5ac70f0Sopenharmony_ci	.reset = snd_pcm_hw_reset,
1579d5ac70f0Sopenharmony_ci	.start = snd_pcm_hw_start,
1580d5ac70f0Sopenharmony_ci	.drop = snd_pcm_hw_drop,
1581d5ac70f0Sopenharmony_ci	.drain = snd_pcm_hw_drain,
1582d5ac70f0Sopenharmony_ci	.pause = snd_pcm_hw_pause,
1583d5ac70f0Sopenharmony_ci	.rewindable = snd_pcm_hw_rewindable,
1584d5ac70f0Sopenharmony_ci	.rewind = snd_pcm_hw_rewind,
1585d5ac70f0Sopenharmony_ci	.forwardable = snd_pcm_hw_forwardable,
1586d5ac70f0Sopenharmony_ci	.forward = snd_pcm_hw_forward,
1587d5ac70f0Sopenharmony_ci	.resume = snd_pcm_hw_resume,
1588d5ac70f0Sopenharmony_ci	.link = snd_pcm_hw_link,
1589d5ac70f0Sopenharmony_ci	.link_slaves = snd_pcm_hw_link_slaves,
1590d5ac70f0Sopenharmony_ci	.unlink = snd_pcm_hw_unlink,
1591d5ac70f0Sopenharmony_ci	.writei = snd_pcm_hw_writei,
1592d5ac70f0Sopenharmony_ci	.writen = snd_pcm_hw_writen,
1593d5ac70f0Sopenharmony_ci	.readi = snd_pcm_hw_readi,
1594d5ac70f0Sopenharmony_ci	.readn = snd_pcm_hw_readn,
1595d5ac70f0Sopenharmony_ci	.avail_update = snd_pcm_hw_avail_update,
1596d5ac70f0Sopenharmony_ci	.mmap_commit = snd_pcm_hw_mmap_commit,
1597d5ac70f0Sopenharmony_ci	.htimestamp = snd_pcm_hw_htimestamp,
1598d5ac70f0Sopenharmony_ci	.poll_descriptors = snd_pcm_hw_poll_descriptors,
1599d5ac70f0Sopenharmony_ci	.poll_descriptors_count = snd_pcm_hw_poll_descriptors_count,
1600d5ac70f0Sopenharmony_ci	.poll_revents = snd_pcm_hw_poll_revents,
1601d5ac70f0Sopenharmony_ci};
1602d5ac70f0Sopenharmony_ci
1603d5ac70f0Sopenharmony_ci/**
1604d5ac70f0Sopenharmony_ci * \brief Creates a new hw PCM
1605d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle
1606d5ac70f0Sopenharmony_ci * \param name Name of PCM
1607d5ac70f0Sopenharmony_ci * \param fd File descriptor
1608d5ac70f0Sopenharmony_ci * \param sync_ptr_ioctl Boolean flag for sync_ptr ioctl
1609d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code
1610d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense
1611d5ac70f0Sopenharmony_ci *          of compatibility reasons. The prototype might be freely
1612d5ac70f0Sopenharmony_ci *          changed in future.
1613d5ac70f0Sopenharmony_ci */
1614d5ac70f0Sopenharmony_ciint snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd,
1615d5ac70f0Sopenharmony_ci		       int sync_ptr_ioctl)
1616d5ac70f0Sopenharmony_ci{
1617d5ac70f0Sopenharmony_ci	int ver, mode;
1618d5ac70f0Sopenharmony_ci	snd_pcm_tstamp_type_t tstamp_type = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
1619d5ac70f0Sopenharmony_ci	long fmode;
1620d5ac70f0Sopenharmony_ci	snd_pcm_t *pcm = NULL;
1621d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw = NULL;
1622d5ac70f0Sopenharmony_ci	snd_pcm_info_t info;
1623d5ac70f0Sopenharmony_ci	int ret;
1624d5ac70f0Sopenharmony_ci
1625d5ac70f0Sopenharmony_ci	assert(pcmp);
1626d5ac70f0Sopenharmony_ci
1627d5ac70f0Sopenharmony_ci	memset(&info, 0, sizeof(info));
1628d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_INFO, &info) < 0) {
1629d5ac70f0Sopenharmony_ci		ret = -errno;
1630d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_INFO failed (%i)", ret);
1631d5ac70f0Sopenharmony_ci		close(fd);
1632d5ac70f0Sopenharmony_ci		return ret;
1633d5ac70f0Sopenharmony_ci
1634d5ac70f0Sopenharmony_ci	}
1635d5ac70f0Sopenharmony_ci
1636d5ac70f0Sopenharmony_ci	if ((fmode = fcntl(fd, F_GETFL)) < 0) {
1637d5ac70f0Sopenharmony_ci		ret = -errno;
1638d5ac70f0Sopenharmony_ci		close(fd);
1639d5ac70f0Sopenharmony_ci		return ret;
1640d5ac70f0Sopenharmony_ci	}
1641d5ac70f0Sopenharmony_ci	mode = 0;
1642d5ac70f0Sopenharmony_ci	if (fmode & O_NONBLOCK)
1643d5ac70f0Sopenharmony_ci		mode |= SND_PCM_NONBLOCK;
1644d5ac70f0Sopenharmony_ci	if (fmode & O_ASYNC)
1645d5ac70f0Sopenharmony_ci		mode |= SND_PCM_ASYNC;
1646d5ac70f0Sopenharmony_ci	if (fmode & O_APPEND)
1647d5ac70f0Sopenharmony_ci		mode |= SND_PCM_APPEND;
1648d5ac70f0Sopenharmony_ci
1649d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_PCM_IOCTL_PVERSION, &ver) < 0) {
1650d5ac70f0Sopenharmony_ci		ret = -errno;
1651d5ac70f0Sopenharmony_ci		SYSMSG("SNDRV_PCM_IOCTL_PVERSION failed (%i)", ret);
1652d5ac70f0Sopenharmony_ci		close(fd);
1653d5ac70f0Sopenharmony_ci		return ret;
1654d5ac70f0Sopenharmony_ci	}
1655d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_PCM_VERSION_MAX))
1656d5ac70f0Sopenharmony_ci		return -SND_ERROR_INCOMPATIBLE_VERSION;
1657d5ac70f0Sopenharmony_ci
1658d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_VERSION(2, 0, 14) <= ver) {
1659d5ac70f0Sopenharmony_ci		/* inform the protocol version we're supporting */
1660d5ac70f0Sopenharmony_ci		unsigned int user_ver = SNDRV_PCM_VERSION;
1661d5ac70f0Sopenharmony_ci		if (ioctl(fd, SNDRV_PCM_IOCTL_USER_PVERSION, &user_ver) < 0) {
1662d5ac70f0Sopenharmony_ci			ret = -errno;
1663d5ac70f0Sopenharmony_ci			SNDMSG("USER_PVERSION failed");
1664d5ac70f0Sopenharmony_ci			return ret;
1665d5ac70f0Sopenharmony_ci		}
1666d5ac70f0Sopenharmony_ci	}
1667d5ac70f0Sopenharmony_ci
1668d5ac70f0Sopenharmony_ci#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
1669d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_VERSION(2, 0, 9) <= ver) {
1670d5ac70f0Sopenharmony_ci		struct timespec timespec;
1671d5ac70f0Sopenharmony_ci		if (clock_gettime(CLOCK_MONOTONIC, &timespec) == 0) {
1672d5ac70f0Sopenharmony_ci			int on = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
1673d5ac70f0Sopenharmony_ci			if (ioctl(fd, SNDRV_PCM_IOCTL_TTSTAMP, &on) < 0) {
1674d5ac70f0Sopenharmony_ci				ret = -errno;
1675d5ac70f0Sopenharmony_ci				SNDMSG("TTSTAMP failed");
1676d5ac70f0Sopenharmony_ci				return ret;
1677d5ac70f0Sopenharmony_ci			}
1678d5ac70f0Sopenharmony_ci			tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC;
1679d5ac70f0Sopenharmony_ci		}
1680d5ac70f0Sopenharmony_ci	} else
1681d5ac70f0Sopenharmony_ci#endif
1682d5ac70f0Sopenharmony_ci	  if (SNDRV_PROTOCOL_VERSION(2, 0, 5) <= ver) {
1683d5ac70f0Sopenharmony_ci		int on = 1;
1684d5ac70f0Sopenharmony_ci		if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) {
1685d5ac70f0Sopenharmony_ci			ret = -errno;
1686d5ac70f0Sopenharmony_ci			SNDMSG("TSTAMP failed");
1687d5ac70f0Sopenharmony_ci			return ret;
1688d5ac70f0Sopenharmony_ci		}
1689d5ac70f0Sopenharmony_ci	}
1690d5ac70f0Sopenharmony_ci
1691d5ac70f0Sopenharmony_ci	hw = calloc(1, sizeof(snd_pcm_hw_t));
1692d5ac70f0Sopenharmony_ci	if (!hw) {
1693d5ac70f0Sopenharmony_ci		close(fd);
1694d5ac70f0Sopenharmony_ci		return -ENOMEM;
1695d5ac70f0Sopenharmony_ci	}
1696d5ac70f0Sopenharmony_ci
1697d5ac70f0Sopenharmony_ci	hw->version = ver;
1698d5ac70f0Sopenharmony_ci	hw->card = info.card;
1699d5ac70f0Sopenharmony_ci	hw->device = info.device;
1700d5ac70f0Sopenharmony_ci	hw->subdevice = info.subdevice;
1701d5ac70f0Sopenharmony_ci	hw->fd = fd;
1702d5ac70f0Sopenharmony_ci	/* no restriction */
1703d5ac70f0Sopenharmony_ci	hw->format = SND_PCM_FORMAT_UNKNOWN;
1704d5ac70f0Sopenharmony_ci	hw->rates.min = hw->rates.max = 0;
1705d5ac70f0Sopenharmony_ci	hw->channels = 0;
1706d5ac70f0Sopenharmony_ci
1707d5ac70f0Sopenharmony_ci	ret = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, info.stream, mode);
1708d5ac70f0Sopenharmony_ci	if (ret < 0) {
1709d5ac70f0Sopenharmony_ci		free(hw);
1710d5ac70f0Sopenharmony_ci		close(fd);
1711d5ac70f0Sopenharmony_ci		return ret;
1712d5ac70f0Sopenharmony_ci	}
1713d5ac70f0Sopenharmony_ci
1714d5ac70f0Sopenharmony_ci	pcm->ops = &snd_pcm_hw_ops;
1715d5ac70f0Sopenharmony_ci	pcm->fast_ops = &snd_pcm_hw_fast_ops;
1716d5ac70f0Sopenharmony_ci	pcm->private_data = hw;
1717d5ac70f0Sopenharmony_ci	pcm->poll_fd = fd;
1718d5ac70f0Sopenharmony_ci	pcm->poll_events = info.stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
1719d5ac70f0Sopenharmony_ci	pcm->tstamp_type = tstamp_type;
1720d5ac70f0Sopenharmony_ci#ifdef THREAD_SAFE_API
1721d5ac70f0Sopenharmony_ci	pcm->need_lock = 0;	/* hw plugin is thread-safe */
1722d5ac70f0Sopenharmony_ci#endif
1723d5ac70f0Sopenharmony_ci	pcm->own_state_check = 1; /* skip the common state check */
1724d5ac70f0Sopenharmony_ci
1725d5ac70f0Sopenharmony_ci	ret = map_status_and_control_data(pcm, !!sync_ptr_ioctl);
1726d5ac70f0Sopenharmony_ci	if (ret < 0) {
1727d5ac70f0Sopenharmony_ci		snd_pcm_close(pcm);
1728d5ac70f0Sopenharmony_ci		return ret;
1729d5ac70f0Sopenharmony_ci	}
1730d5ac70f0Sopenharmony_ci
1731d5ac70f0Sopenharmony_ci	*pcmp = pcm;
1732d5ac70f0Sopenharmony_ci	return 0;
1733d5ac70f0Sopenharmony_ci}
1734d5ac70f0Sopenharmony_ci
1735d5ac70f0Sopenharmony_ci/**
1736d5ac70f0Sopenharmony_ci * \brief Creates a new hw PCM
1737d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle
1738d5ac70f0Sopenharmony_ci * \param name Name of PCM
1739d5ac70f0Sopenharmony_ci * \param card Number of card
1740d5ac70f0Sopenharmony_ci * \param device Number of device
1741d5ac70f0Sopenharmony_ci * \param subdevice Number of subdevice
1742d5ac70f0Sopenharmony_ci * \param stream PCM Stream
1743d5ac70f0Sopenharmony_ci * \param mode PCM Mode
1744d5ac70f0Sopenharmony_ci * \param mmap_emulation Obsoleted parameter
1745d5ac70f0Sopenharmony_ci * \param sync_ptr_ioctl Use SYNC_PTR ioctl rather than mmap for control structures
1746d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code
1747d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense
1748d5ac70f0Sopenharmony_ci *          of compatibility reasons. The prototype might be freely
1749d5ac70f0Sopenharmony_ci *          changed in future.
1750d5ac70f0Sopenharmony_ci */
1751d5ac70f0Sopenharmony_ciint snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
1752d5ac70f0Sopenharmony_ci		    int card, int device, int subdevice,
1753d5ac70f0Sopenharmony_ci		    snd_pcm_stream_t stream, int mode,
1754d5ac70f0Sopenharmony_ci		    int mmap_emulation ATTRIBUTE_UNUSED,
1755d5ac70f0Sopenharmony_ci		    int sync_ptr_ioctl)
1756d5ac70f0Sopenharmony_ci{
1757d5ac70f0Sopenharmony_ci	char filename[sizeof(SNDRV_FILE_PCM_STREAM_PLAYBACK) + 20];
1758d5ac70f0Sopenharmony_ci	const char *filefmt;
1759d5ac70f0Sopenharmony_ci	int ret = 0, fd = -1;
1760d5ac70f0Sopenharmony_ci	int attempt = 0;
1761d5ac70f0Sopenharmony_ci	snd_pcm_info_t info;
1762d5ac70f0Sopenharmony_ci	int fmode;
1763d5ac70f0Sopenharmony_ci	snd_ctl_t *ctl;
1764d5ac70f0Sopenharmony_ci
1765d5ac70f0Sopenharmony_ci	assert(pcmp);
1766d5ac70f0Sopenharmony_ci
1767d5ac70f0Sopenharmony_ci	if ((ret = snd_ctl_hw_open(&ctl, NULL, card, 0)) < 0)
1768d5ac70f0Sopenharmony_ci		return ret;
1769d5ac70f0Sopenharmony_ci
1770d5ac70f0Sopenharmony_ci	switch (stream) {
1771d5ac70f0Sopenharmony_ci	case SND_PCM_STREAM_PLAYBACK:
1772d5ac70f0Sopenharmony_ci		filefmt = SNDRV_FILE_PCM_STREAM_PLAYBACK;
1773d5ac70f0Sopenharmony_ci		break;
1774d5ac70f0Sopenharmony_ci	case SND_PCM_STREAM_CAPTURE:
1775d5ac70f0Sopenharmony_ci		filefmt = SNDRV_FILE_PCM_STREAM_CAPTURE;
1776d5ac70f0Sopenharmony_ci		break;
1777d5ac70f0Sopenharmony_ci	default:
1778d5ac70f0Sopenharmony_ci		SNDERR("invalid stream %d", stream);
1779d5ac70f0Sopenharmony_ci		return -EINVAL;
1780d5ac70f0Sopenharmony_ci	}
1781d5ac70f0Sopenharmony_ci	sprintf(filename, filefmt, card, device);
1782d5ac70f0Sopenharmony_ci
1783d5ac70f0Sopenharmony_ci      __again:
1784d5ac70f0Sopenharmony_ci      	if (attempt++ > 3) {
1785d5ac70f0Sopenharmony_ci		ret = -EBUSY;
1786d5ac70f0Sopenharmony_ci		goto _err;
1787d5ac70f0Sopenharmony_ci	}
1788d5ac70f0Sopenharmony_ci	ret = snd_ctl_pcm_prefer_subdevice(ctl, subdevice);
1789d5ac70f0Sopenharmony_ci	if (ret < 0)
1790d5ac70f0Sopenharmony_ci		goto _err;
1791d5ac70f0Sopenharmony_ci	fmode = O_RDWR;
1792d5ac70f0Sopenharmony_ci	if (mode & SND_PCM_NONBLOCK)
1793d5ac70f0Sopenharmony_ci		fmode |= O_NONBLOCK;
1794d5ac70f0Sopenharmony_ci	if (mode & SND_PCM_ASYNC)
1795d5ac70f0Sopenharmony_ci		fmode |= O_ASYNC;
1796d5ac70f0Sopenharmony_ci	if (mode & SND_PCM_APPEND)
1797d5ac70f0Sopenharmony_ci		fmode |= O_APPEND;
1798d5ac70f0Sopenharmony_ci	fd = snd_open_device(filename, fmode);
1799d5ac70f0Sopenharmony_ci	if (fd < 0) {
1800d5ac70f0Sopenharmony_ci		ret = -errno;
1801d5ac70f0Sopenharmony_ci		SYSMSG("open '%s' failed (%i)", filename, ret);
1802d5ac70f0Sopenharmony_ci		goto _err;
1803d5ac70f0Sopenharmony_ci	}
1804d5ac70f0Sopenharmony_ci	if (subdevice >= 0) {
1805d5ac70f0Sopenharmony_ci		memset(&info, 0, sizeof(info));
1806d5ac70f0Sopenharmony_ci		if (ioctl(fd, SNDRV_PCM_IOCTL_INFO, &info) < 0) {
1807d5ac70f0Sopenharmony_ci			ret = -errno;
1808d5ac70f0Sopenharmony_ci			SYSMSG("SNDRV_PCM_IOCTL_INFO failed (%i)", ret);
1809d5ac70f0Sopenharmony_ci			goto _err;
1810d5ac70f0Sopenharmony_ci		}
1811d5ac70f0Sopenharmony_ci		if (info.subdevice != (unsigned int) subdevice) {
1812d5ac70f0Sopenharmony_ci			close(fd);
1813d5ac70f0Sopenharmony_ci			fd = -1;
1814d5ac70f0Sopenharmony_ci			goto __again;
1815d5ac70f0Sopenharmony_ci		}
1816d5ac70f0Sopenharmony_ci	}
1817d5ac70f0Sopenharmony_ci	snd_ctl_close(ctl);
1818d5ac70f0Sopenharmony_ci	return snd_pcm_hw_open_fd(pcmp, name, fd, sync_ptr_ioctl);
1819d5ac70f0Sopenharmony_ci       _err:
1820d5ac70f0Sopenharmony_ci	if (fd >= 0)
1821d5ac70f0Sopenharmony_ci		close(fd);
1822d5ac70f0Sopenharmony_ci	snd_ctl_close(ctl);
1823d5ac70f0Sopenharmony_ci	return ret;
1824d5ac70f0Sopenharmony_ci}
1825d5ac70f0Sopenharmony_ci
1826d5ac70f0Sopenharmony_ci/*! \page pcm_plugins
1827d5ac70f0Sopenharmony_ci
1828d5ac70f0Sopenharmony_ci\section pcm_plugins_hw Plugin: hw
1829d5ac70f0Sopenharmony_ci
1830d5ac70f0Sopenharmony_ciThis plugin communicates directly with the ALSA kernel driver. It is a raw
1831d5ac70f0Sopenharmony_cicommunication without any conversions. The emulation of mmap access can be
1832d5ac70f0Sopenharmony_cioptionally enabled, but expect worse latency in the case.
1833d5ac70f0Sopenharmony_ci
1834d5ac70f0Sopenharmony_ciThe nonblock option specifies whether the device is opened in a non-blocking
1835d5ac70f0Sopenharmony_cimanner.  Note that the blocking behavior for read/write access won't be
1836d5ac70f0Sopenharmony_cichanged by this option.  This influences only on the blocking behavior at
1837d5ac70f0Sopenharmony_ciopening the device.  If you would like to keep the compatibility with the
1838d5ac70f0Sopenharmony_ciolder ALSA stuff, turn this option off.
1839d5ac70f0Sopenharmony_ci
1840d5ac70f0Sopenharmony_ci\code
1841d5ac70f0Sopenharmony_cipcm.name {
1842d5ac70f0Sopenharmony_ci	type hw			# Kernel PCM
1843d5ac70f0Sopenharmony_ci	card INT/STR		# Card name (string) or number (integer)
1844d5ac70f0Sopenharmony_ci	[device INT]		# Device number (default 0)
1845d5ac70f0Sopenharmony_ci	[subdevice INT]		# Subdevice number (default -1: first available)
1846d5ac70f0Sopenharmony_ci	[sync_ptr_ioctl BOOL]	# Use SYNC_PTR ioctl rather than the direct mmap access for control structures
1847d5ac70f0Sopenharmony_ci	[nonblock BOOL]		# Force non-blocking open mode
1848d5ac70f0Sopenharmony_ci	[format STR]		# Restrict only to the given format
1849d5ac70f0Sopenharmony_ci	[channels INT]		# Restrict only to the given channels
1850d5ac70f0Sopenharmony_ci	[rate INT]		# Restrict only to the given rate
1851d5ac70f0Sopenharmony_ci	  or [rate [INT INT]]	# Restrict only to the given rate range (min max)
1852d5ac70f0Sopenharmony_ci	[chmap MAP]		# Override channel maps; MAP is a string array
1853d5ac70f0Sopenharmony_ci	[drain_silence INT]	# Add silence in drain (-1 = auto /default/, 0 = off, > 0 milliseconds)
1854d5ac70f0Sopenharmony_ci}
1855d5ac70f0Sopenharmony_ci\endcode
1856d5ac70f0Sopenharmony_ci
1857d5ac70f0Sopenharmony_ci\subsection pcm_plugins_hw_funcref Function reference
1858d5ac70f0Sopenharmony_ci
1859d5ac70f0Sopenharmony_ci<UL>
1860d5ac70f0Sopenharmony_ci  <LI>snd_pcm_hw_open()
1861d5ac70f0Sopenharmony_ci  <LI>_snd_pcm_hw_open()
1862d5ac70f0Sopenharmony_ci</UL>
1863d5ac70f0Sopenharmony_ci
1864d5ac70f0Sopenharmony_ci*/
1865d5ac70f0Sopenharmony_ci
1866d5ac70f0Sopenharmony_ci/**
1867d5ac70f0Sopenharmony_ci * \brief Creates a new hw PCM
1868d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle
1869d5ac70f0Sopenharmony_ci * \param name Name of PCM
1870d5ac70f0Sopenharmony_ci * \param root Root configuration node
1871d5ac70f0Sopenharmony_ci * \param conf Configuration node with hw PCM description
1872d5ac70f0Sopenharmony_ci * \param stream PCM Stream
1873d5ac70f0Sopenharmony_ci * \param mode PCM Mode
1874d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense
1875d5ac70f0Sopenharmony_ci *          of compatibility reasons. The prototype might be freely
1876d5ac70f0Sopenharmony_ci *          changed in future.
1877d5ac70f0Sopenharmony_ci */
1878d5ac70f0Sopenharmony_ciint _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
1879d5ac70f0Sopenharmony_ci		     snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf,
1880d5ac70f0Sopenharmony_ci		     snd_pcm_stream_t stream, int mode)
1881d5ac70f0Sopenharmony_ci{
1882d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
1883d5ac70f0Sopenharmony_ci	long card = -1, device = 0, subdevice = -1;
1884d5ac70f0Sopenharmony_ci	const char *str;
1885d5ac70f0Sopenharmony_ci	int err, sync_ptr_ioctl = 0;
1886d5ac70f0Sopenharmony_ci	int min_rate = 0, max_rate = 0, channels = 0, drain_silence = -1;
1887d5ac70f0Sopenharmony_ci	snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
1888d5ac70f0Sopenharmony_ci	snd_config_t *n;
1889d5ac70f0Sopenharmony_ci	int nonblock = 1; /* non-block per default */
1890d5ac70f0Sopenharmony_ci	snd_pcm_chmap_query_t **chmap = NULL;
1891d5ac70f0Sopenharmony_ci	snd_pcm_hw_t *hw;
1892d5ac70f0Sopenharmony_ci
1893d5ac70f0Sopenharmony_ci	/* look for defaults.pcm.nonblock definition */
1894d5ac70f0Sopenharmony_ci	if (snd_config_search(root, "defaults.pcm.nonblock", &n) >= 0) {
1895d5ac70f0Sopenharmony_ci		err = snd_config_get_bool(n);
1896d5ac70f0Sopenharmony_ci		if (err >= 0)
1897d5ac70f0Sopenharmony_ci			nonblock = err;
1898d5ac70f0Sopenharmony_ci	}
1899d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, conf) {
1900d5ac70f0Sopenharmony_ci		const char *id;
1901d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
1902d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
1903d5ac70f0Sopenharmony_ci			continue;
1904d5ac70f0Sopenharmony_ci		if (snd_pcm_conf_generic_id(id))
1905d5ac70f0Sopenharmony_ci			continue;
1906d5ac70f0Sopenharmony_ci		if (strcmp(id, "card") == 0) {
1907d5ac70f0Sopenharmony_ci			err = snd_config_get_card(n);
1908d5ac70f0Sopenharmony_ci			if (err < 0)
1909d5ac70f0Sopenharmony_ci				goto fail;
1910d5ac70f0Sopenharmony_ci			card = err;
1911d5ac70f0Sopenharmony_ci			continue;
1912d5ac70f0Sopenharmony_ci		}
1913d5ac70f0Sopenharmony_ci		if (strcmp(id, "device") == 0) {
1914d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &device);
1915d5ac70f0Sopenharmony_ci			if (err < 0) {
1916d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
1917d5ac70f0Sopenharmony_ci				goto fail;
1918d5ac70f0Sopenharmony_ci			}
1919d5ac70f0Sopenharmony_ci			continue;
1920d5ac70f0Sopenharmony_ci		}
1921d5ac70f0Sopenharmony_ci		if (strcmp(id, "subdevice") == 0) {
1922d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &subdevice);
1923d5ac70f0Sopenharmony_ci			if (err < 0) {
1924d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
1925d5ac70f0Sopenharmony_ci				goto fail;
1926d5ac70f0Sopenharmony_ci			}
1927d5ac70f0Sopenharmony_ci			continue;
1928d5ac70f0Sopenharmony_ci		}
1929d5ac70f0Sopenharmony_ci		if (strcmp(id, "sync_ptr_ioctl") == 0) {
1930d5ac70f0Sopenharmony_ci			err = snd_config_get_bool(n);
1931d5ac70f0Sopenharmony_ci			if (err < 0)
1932d5ac70f0Sopenharmony_ci				continue;
1933d5ac70f0Sopenharmony_ci			sync_ptr_ioctl = err;
1934d5ac70f0Sopenharmony_ci			continue;
1935d5ac70f0Sopenharmony_ci		}
1936d5ac70f0Sopenharmony_ci		if (strcmp(id, "nonblock") == 0) {
1937d5ac70f0Sopenharmony_ci			err = snd_config_get_bool(n);
1938d5ac70f0Sopenharmony_ci			if (err < 0)
1939d5ac70f0Sopenharmony_ci				continue;
1940d5ac70f0Sopenharmony_ci			nonblock = err;
1941d5ac70f0Sopenharmony_ci			continue;
1942d5ac70f0Sopenharmony_ci		}
1943d5ac70f0Sopenharmony_ci		if (strcmp(id, "rate") == 0) {
1944d5ac70f0Sopenharmony_ci			long val;
1945d5ac70f0Sopenharmony_ci			if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND &&
1946d5ac70f0Sopenharmony_ci			    snd_config_is_array(n)) {
1947d5ac70f0Sopenharmony_ci				snd_config_t *m;
1948d5ac70f0Sopenharmony_ci				err = snd_config_search(n, "0", &m);
1949d5ac70f0Sopenharmony_ci				if (err < 0) {
1950d5ac70f0Sopenharmony_ci					SNDERR("array expected for rate compound");
1951d5ac70f0Sopenharmony_ci					goto fail;
1952d5ac70f0Sopenharmony_ci				}
1953d5ac70f0Sopenharmony_ci				err = snd_config_get_integer(m, &val);
1954d5ac70f0Sopenharmony_ci				if (err < 0) {
1955d5ac70f0Sopenharmony_ci					SNDERR("Invalid type for rate.0");
1956d5ac70f0Sopenharmony_ci					goto fail;
1957d5ac70f0Sopenharmony_ci				}
1958d5ac70f0Sopenharmony_ci				min_rate = max_rate = val;
1959d5ac70f0Sopenharmony_ci				err = snd_config_search(n, "1", &m);
1960d5ac70f0Sopenharmony_ci				if (err >= 0) {
1961d5ac70f0Sopenharmony_ci					err = snd_config_get_integer(m, &val);
1962d5ac70f0Sopenharmony_ci					if (err < 0) {
1963d5ac70f0Sopenharmony_ci						SNDERR("Invalid type for rate.0");
1964d5ac70f0Sopenharmony_ci						goto fail;
1965d5ac70f0Sopenharmony_ci					}
1966d5ac70f0Sopenharmony_ci					max_rate = val;
1967d5ac70f0Sopenharmony_ci				}
1968d5ac70f0Sopenharmony_ci			} else {
1969d5ac70f0Sopenharmony_ci				err = snd_config_get_integer(n, &val);
1970d5ac70f0Sopenharmony_ci				if (err < 0) {
1971d5ac70f0Sopenharmony_ci					SNDERR("Invalid type for %s", id);
1972d5ac70f0Sopenharmony_ci					goto fail;
1973d5ac70f0Sopenharmony_ci				}
1974d5ac70f0Sopenharmony_ci				min_rate = max_rate = val;
1975d5ac70f0Sopenharmony_ci			}
1976d5ac70f0Sopenharmony_ci			continue;
1977d5ac70f0Sopenharmony_ci		}
1978d5ac70f0Sopenharmony_ci		if (strcmp(id, "min_rate") == 0) {
1979d5ac70f0Sopenharmony_ci			long val;
1980d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &val);
1981d5ac70f0Sopenharmony_ci			if (err < 0) {
1982d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
1983d5ac70f0Sopenharmony_ci				goto fail;
1984d5ac70f0Sopenharmony_ci			}
1985d5ac70f0Sopenharmony_ci			min_rate = val;
1986d5ac70f0Sopenharmony_ci			continue;
1987d5ac70f0Sopenharmony_ci		}
1988d5ac70f0Sopenharmony_ci		if (strcmp(id, "max_rate") == 0) {
1989d5ac70f0Sopenharmony_ci			long val;
1990d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &val);
1991d5ac70f0Sopenharmony_ci			if (err < 0) {
1992d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
1993d5ac70f0Sopenharmony_ci				goto fail;
1994d5ac70f0Sopenharmony_ci			}
1995d5ac70f0Sopenharmony_ci			max_rate = val;
1996d5ac70f0Sopenharmony_ci			continue;
1997d5ac70f0Sopenharmony_ci		}
1998d5ac70f0Sopenharmony_ci		if (strcmp(id, "format") == 0) {
1999d5ac70f0Sopenharmony_ci			err = snd_config_get_string(n, &str);
2000d5ac70f0Sopenharmony_ci			if (err < 0) {
2001d5ac70f0Sopenharmony_ci				SNDERR("invalid type for %s", id);
2002d5ac70f0Sopenharmony_ci				goto fail;
2003d5ac70f0Sopenharmony_ci			}
2004d5ac70f0Sopenharmony_ci			format = snd_pcm_format_value(str);
2005d5ac70f0Sopenharmony_ci			continue;
2006d5ac70f0Sopenharmony_ci		}
2007d5ac70f0Sopenharmony_ci		if (strcmp(id, "channels") == 0) {
2008d5ac70f0Sopenharmony_ci			long val;
2009d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &val);
2010d5ac70f0Sopenharmony_ci			if (err < 0) {
2011d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
2012d5ac70f0Sopenharmony_ci				goto fail;
2013d5ac70f0Sopenharmony_ci			}
2014d5ac70f0Sopenharmony_ci			channels = val;
2015d5ac70f0Sopenharmony_ci			continue;
2016d5ac70f0Sopenharmony_ci		}
2017d5ac70f0Sopenharmony_ci		if (strcmp(id, "chmap") == 0) {
2018d5ac70f0Sopenharmony_ci			snd_pcm_free_chmaps(chmap);
2019d5ac70f0Sopenharmony_ci			chmap = _snd_pcm_parse_config_chmaps(n);
2020d5ac70f0Sopenharmony_ci			if (!chmap) {
2021d5ac70f0Sopenharmony_ci				SNDERR("Invalid channel map for %s", id);
2022d5ac70f0Sopenharmony_ci				err = -EINVAL;
2023d5ac70f0Sopenharmony_ci				goto fail;
2024d5ac70f0Sopenharmony_ci			}
2025d5ac70f0Sopenharmony_ci			continue;
2026d5ac70f0Sopenharmony_ci		}
2027d5ac70f0Sopenharmony_ci		if (strcmp(id, "drain_silence") == 0) {
2028d5ac70f0Sopenharmony_ci			long val;
2029d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &val);
2030d5ac70f0Sopenharmony_ci			if (err < 0) {
2031d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
2032d5ac70f0Sopenharmony_ci				goto fail;
2033d5ac70f0Sopenharmony_ci			}
2034d5ac70f0Sopenharmony_ci			drain_silence = val;
2035d5ac70f0Sopenharmony_ci			continue;
2036d5ac70f0Sopenharmony_ci		}
2037d5ac70f0Sopenharmony_ci		SNDERR("Unknown field %s", id);
2038d5ac70f0Sopenharmony_ci		err = -EINVAL;
2039d5ac70f0Sopenharmony_ci		goto fail;
2040d5ac70f0Sopenharmony_ci	}
2041d5ac70f0Sopenharmony_ci	if (card < 0) {
2042d5ac70f0Sopenharmony_ci		SNDERR("card is not defined");
2043d5ac70f0Sopenharmony_ci		err = -EINVAL;
2044d5ac70f0Sopenharmony_ci		goto fail;
2045d5ac70f0Sopenharmony_ci	}
2046d5ac70f0Sopenharmony_ci	if ((min_rate < 0) || (max_rate < min_rate)) {
2047d5ac70f0Sopenharmony_ci		SNDERR("min_rate - max_rate configuration invalid");
2048d5ac70f0Sopenharmony_ci		err = -EINVAL;
2049d5ac70f0Sopenharmony_ci		goto fail;
2050d5ac70f0Sopenharmony_ci	}
2051d5ac70f0Sopenharmony_ci	err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream,
2052d5ac70f0Sopenharmony_ci			      mode | (nonblock ? SND_PCM_NONBLOCK : 0),
2053d5ac70f0Sopenharmony_ci			      0, sync_ptr_ioctl);
2054d5ac70f0Sopenharmony_ci	if (err < 0)
2055d5ac70f0Sopenharmony_ci		goto fail;
2056d5ac70f0Sopenharmony_ci	if (nonblock && ! (mode & SND_PCM_NONBLOCK)) {
2057d5ac70f0Sopenharmony_ci		/* revert to blocking mode for read/write access */
2058d5ac70f0Sopenharmony_ci		snd_pcm_hw_nonblock(*pcmp, 0);
2059d5ac70f0Sopenharmony_ci		(*pcmp)->mode = mode;
2060d5ac70f0Sopenharmony_ci	} else
2061d5ac70f0Sopenharmony_ci		/* make sure the SND_PCM_NO_xxx flags don't get lost on the
2062d5ac70f0Sopenharmony_ci		 * way */
2063d5ac70f0Sopenharmony_ci		(*pcmp)->mode |= mode & (SND_PCM_NO_AUTO_RESAMPLE|
2064d5ac70f0Sopenharmony_ci					 SND_PCM_NO_AUTO_CHANNELS|
2065d5ac70f0Sopenharmony_ci					 SND_PCM_NO_AUTO_FORMAT|
2066d5ac70f0Sopenharmony_ci					 SND_PCM_NO_SOFTVOL);
2067d5ac70f0Sopenharmony_ci
2068d5ac70f0Sopenharmony_ci	hw = (*pcmp)->private_data;
2069d5ac70f0Sopenharmony_ci	if (format != SND_PCM_FORMAT_UNKNOWN)
2070d5ac70f0Sopenharmony_ci		hw->format = format;
2071d5ac70f0Sopenharmony_ci	if (channels > 0)
2072d5ac70f0Sopenharmony_ci		hw->channels = channels;
2073d5ac70f0Sopenharmony_ci	if (min_rate > 0) {
2074d5ac70f0Sopenharmony_ci		hw->rates.min = min_rate;
2075d5ac70f0Sopenharmony_ci		hw->rates.max = max_rate;
2076d5ac70f0Sopenharmony_ci	}
2077d5ac70f0Sopenharmony_ci	if (chmap)
2078d5ac70f0Sopenharmony_ci		hw->chmap_override = chmap;
2079d5ac70f0Sopenharmony_ci	hw->drain_silence = drain_silence;
2080d5ac70f0Sopenharmony_ci
2081d5ac70f0Sopenharmony_ci	return 0;
2082d5ac70f0Sopenharmony_ci
2083d5ac70f0Sopenharmony_cifail:
2084d5ac70f0Sopenharmony_ci        snd_pcm_free_chmaps(chmap);
2085d5ac70f0Sopenharmony_ci        return err;
2086d5ac70f0Sopenharmony_ci}
2087d5ac70f0Sopenharmony_ci
2088d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
2089d5ac70f0Sopenharmony_ciSND_DLSYM_BUILD_VERSION(_snd_pcm_hw_open, SND_PCM_DLSYM_VERSION);
2090d5ac70f0Sopenharmony_ci#endif
2091d5ac70f0Sopenharmony_ci
2092d5ac70f0Sopenharmony_ci/*
2093d5ac70f0Sopenharmony_ci *  To be removed helpers, but keep binary compatibility at the time
2094d5ac70f0Sopenharmony_ci */
2095d5ac70f0Sopenharmony_ci
2096d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
2097d5ac70f0Sopenharmony_ci#define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5))
2098d5ac70f0Sopenharmony_ci#define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5))
2099d5ac70f0Sopenharmony_ci#endif
2100d5ac70f0Sopenharmony_ci
2101d5ac70f0Sopenharmony_cistatic void snd_pcm_hw_convert_from_old_params(snd_pcm_hw_params_t *params,
2102d5ac70f0Sopenharmony_ci					       struct sndrv_pcm_hw_params_old *oparams)
2103d5ac70f0Sopenharmony_ci{
2104d5ac70f0Sopenharmony_ci	unsigned int i;
2105d5ac70f0Sopenharmony_ci
2106d5ac70f0Sopenharmony_ci	memset(params, 0, sizeof(*params));
2107d5ac70f0Sopenharmony_ci	params->flags = oparams->flags;
2108d5ac70f0Sopenharmony_ci	for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++)
2109d5ac70f0Sopenharmony_ci		params->masks[i].bits[0] = oparams->masks[i];
2110d5ac70f0Sopenharmony_ci	memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals));
2111d5ac70f0Sopenharmony_ci	params->rmask = __OLD_TO_NEW_MASK(oparams->rmask);
2112d5ac70f0Sopenharmony_ci	params->cmask = __OLD_TO_NEW_MASK(oparams->cmask);
2113d5ac70f0Sopenharmony_ci	params->info = oparams->info;
2114d5ac70f0Sopenharmony_ci	params->msbits = oparams->msbits;
2115d5ac70f0Sopenharmony_ci	params->rate_num = oparams->rate_num;
2116d5ac70f0Sopenharmony_ci	params->rate_den = oparams->rate_den;
2117d5ac70f0Sopenharmony_ci	params->fifo_size = oparams->fifo_size;
2118d5ac70f0Sopenharmony_ci}
2119d5ac70f0Sopenharmony_ci
2120d5ac70f0Sopenharmony_cistatic void snd_pcm_hw_convert_to_old_params(struct sndrv_pcm_hw_params_old *oparams,
2121d5ac70f0Sopenharmony_ci					     snd_pcm_hw_params_t *params,
2122d5ac70f0Sopenharmony_ci					     unsigned int *cmask)
2123d5ac70f0Sopenharmony_ci{
2124d5ac70f0Sopenharmony_ci	unsigned int i, j;
2125d5ac70f0Sopenharmony_ci
2126d5ac70f0Sopenharmony_ci	memset(oparams, 0, sizeof(*oparams));
2127d5ac70f0Sopenharmony_ci	oparams->flags = params->flags;
2128d5ac70f0Sopenharmony_ci	for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++) {
2129d5ac70f0Sopenharmony_ci		oparams->masks[i] = params->masks[i].bits[0];
2130d5ac70f0Sopenharmony_ci		for (j = 1; j < sizeof(params->masks[i].bits) / sizeof(unsigned int); j++)
2131d5ac70f0Sopenharmony_ci			if (params->masks[i].bits[j]) {
2132d5ac70f0Sopenharmony_ci				*cmask |= 1 << i;
2133d5ac70f0Sopenharmony_ci				break;
2134d5ac70f0Sopenharmony_ci			}
2135d5ac70f0Sopenharmony_ci	}
2136d5ac70f0Sopenharmony_ci	memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals));
2137d5ac70f0Sopenharmony_ci	oparams->rmask = __NEW_TO_OLD_MASK(params->rmask);
2138d5ac70f0Sopenharmony_ci	oparams->cmask = __NEW_TO_OLD_MASK(params->cmask);
2139d5ac70f0Sopenharmony_ci	oparams->info = params->info;
2140d5ac70f0Sopenharmony_ci	oparams->msbits = params->msbits;
2141d5ac70f0Sopenharmony_ci	oparams->rate_num = params->rate_num;
2142d5ac70f0Sopenharmony_ci	oparams->rate_den = params->rate_den;
2143d5ac70f0Sopenharmony_ci	oparams->fifo_size = params->fifo_size;
2144d5ac70f0Sopenharmony_ci}
2145d5ac70f0Sopenharmony_ci
2146d5ac70f0Sopenharmony_cistatic int use_old_hw_params_ioctl(int fd, unsigned int cmd, snd_pcm_hw_params_t *params)
2147d5ac70f0Sopenharmony_ci{
2148d5ac70f0Sopenharmony_ci	struct sndrv_pcm_hw_params_old oparams;
2149d5ac70f0Sopenharmony_ci	unsigned int cmask = 0;
2150d5ac70f0Sopenharmony_ci	int res;
2151d5ac70f0Sopenharmony_ci
2152d5ac70f0Sopenharmony_ci	snd_pcm_hw_convert_to_old_params(&oparams, params, &cmask);
2153d5ac70f0Sopenharmony_ci	res = ioctl(fd, cmd, &oparams);
2154d5ac70f0Sopenharmony_ci	snd_pcm_hw_convert_from_old_params(params, &oparams);
2155d5ac70f0Sopenharmony_ci	params->cmask |= cmask;
2156d5ac70f0Sopenharmony_ci	return res;
2157d5ac70f0Sopenharmony_ci}
2158