1c72fcc34Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
2c72fcc34Sopenharmony_ci//
3c72fcc34Sopenharmony_ci// xfer-libasound.c - receive/transmit frames by alsa-lib.
4c72fcc34Sopenharmony_ci//
5c72fcc34Sopenharmony_ci// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6c72fcc34Sopenharmony_ci//
7c72fcc34Sopenharmony_ci// Licensed under the terms of the GNU General Public License, version 2.
8c72fcc34Sopenharmony_ci
9c72fcc34Sopenharmony_ci#include "xfer-libasound.h"
10c72fcc34Sopenharmony_ci#include "misc.h"
11c72fcc34Sopenharmony_ci
12c72fcc34Sopenharmony_cistatic const char *const sched_model_labels [] = {
13c72fcc34Sopenharmony_ci	[SCHED_MODEL_IRQ] = "irq",
14c72fcc34Sopenharmony_ci	[SCHED_MODEL_TIMER] = "timer",
15c72fcc34Sopenharmony_ci};
16c72fcc34Sopenharmony_ci
17c72fcc34Sopenharmony_cienum no_short_opts {
18c72fcc34Sopenharmony_ci        // 200 or later belong to non us-ascii character set.
19c72fcc34Sopenharmony_ci	OPT_PERIOD_SIZE = 200,
20c72fcc34Sopenharmony_ci	OPT_BUFFER_SIZE,
21c72fcc34Sopenharmony_ci	OPT_WAITER_TYPE,
22c72fcc34Sopenharmony_ci	OPT_SCHED_MODEL,
23c72fcc34Sopenharmony_ci	OPT_DISABLE_RESAMPLE,
24c72fcc34Sopenharmony_ci	OPT_DISABLE_CHANNELS,
25c72fcc34Sopenharmony_ci	OPT_DISABLE_FORMAT,
26c72fcc34Sopenharmony_ci	OPT_DISABLE_SOFTVOL,
27c72fcc34Sopenharmony_ci	OPT_FATAL_ERRORS,
28c72fcc34Sopenharmony_ci	OPT_TEST_NOWAIT,
29c72fcc34Sopenharmony_ci	// Obsoleted.
30c72fcc34Sopenharmony_ci	OPT_TEST_POSITION,
31c72fcc34Sopenharmony_ci	OPT_TEST_COEF,
32c72fcc34Sopenharmony_ci};
33c72fcc34Sopenharmony_ci
34c72fcc34Sopenharmony_ci#define S_OPTS	"D:NMF:B:A:R:T:m:"
35c72fcc34Sopenharmony_cistatic const struct option l_opts[] = {
36c72fcc34Sopenharmony_ci	{"device",		1, 0, 'D'},
37c72fcc34Sopenharmony_ci	{"nonblock",		0, 0, 'N'},
38c72fcc34Sopenharmony_ci	{"mmap",		0, 0, 'M'},
39c72fcc34Sopenharmony_ci	{"period-time",		1, 0, 'F'},
40c72fcc34Sopenharmony_ci	{"buffer-time",		1, 0, 'B'},
41c72fcc34Sopenharmony_ci	{"period-size",		1, 0, OPT_PERIOD_SIZE},
42c72fcc34Sopenharmony_ci	{"buffer-size",		1, 0, OPT_BUFFER_SIZE},
43c72fcc34Sopenharmony_ci	{"avail-min",		1, 0, 'A'},
44c72fcc34Sopenharmony_ci	{"start-delay",		1, 0, 'R'},
45c72fcc34Sopenharmony_ci	{"stop-delay",		1, 0, 'T'},
46c72fcc34Sopenharmony_ci	{"waiter-type",		1, 0, OPT_WAITER_TYPE},
47c72fcc34Sopenharmony_ci	{"sched-model",		1, 0, OPT_SCHED_MODEL},
48c72fcc34Sopenharmony_ci	// For plugins in alsa-lib.
49c72fcc34Sopenharmony_ci	{"disable-resample",	0, 0, OPT_DISABLE_RESAMPLE},
50c72fcc34Sopenharmony_ci	{"disable-channels",	0, 0, OPT_DISABLE_CHANNELS},
51c72fcc34Sopenharmony_ci	{"disable-format",	0, 0, OPT_DISABLE_FORMAT},
52c72fcc34Sopenharmony_ci	{"disable-softvol",	0, 0, OPT_DISABLE_SOFTVOL},
53c72fcc34Sopenharmony_ci	// For debugging.
54c72fcc34Sopenharmony_ci	{"fatal-errors",	0, 0, OPT_FATAL_ERRORS},
55c72fcc34Sopenharmony_ci	{"test-nowait",		0, 0, OPT_TEST_NOWAIT},
56c72fcc34Sopenharmony_ci	// Obsoleted.
57c72fcc34Sopenharmony_ci	{"chmap",		1, 0, 'm'},
58c72fcc34Sopenharmony_ci	{"test-position",	0, 0, OPT_TEST_POSITION},
59c72fcc34Sopenharmony_ci	{"test-coef",		1, 0, OPT_TEST_COEF},
60c72fcc34Sopenharmony_ci};
61c72fcc34Sopenharmony_ci
62c72fcc34Sopenharmony_cistatic int xfer_libasound_init(struct xfer_context *xfer,
63c72fcc34Sopenharmony_ci			       snd_pcm_stream_t stream ATTRIBUTE_UNUSED)
64c72fcc34Sopenharmony_ci{
65c72fcc34Sopenharmony_ci	struct libasound_state *state = xfer->private_data;
66c72fcc34Sopenharmony_ci	int err;
67c72fcc34Sopenharmony_ci
68c72fcc34Sopenharmony_ci	err = snd_output_stdio_attach(&state->log, stderr, 0);
69c72fcc34Sopenharmony_ci	if (err < 0)
70c72fcc34Sopenharmony_ci		return err;
71c72fcc34Sopenharmony_ci
72c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_malloc(&state->hw_params);
73c72fcc34Sopenharmony_ci	if (err < 0)
74c72fcc34Sopenharmony_ci		return err;
75c72fcc34Sopenharmony_ci
76c72fcc34Sopenharmony_ci	return snd_pcm_sw_params_malloc(&state->sw_params);
77c72fcc34Sopenharmony_ci}
78c72fcc34Sopenharmony_ci
79c72fcc34Sopenharmony_cistatic int xfer_libasound_parse_opt(struct xfer_context *xfer, int key,
80c72fcc34Sopenharmony_ci				    const char *optarg)
81c72fcc34Sopenharmony_ci{
82c72fcc34Sopenharmony_ci	struct libasound_state *state = xfer->private_data;
83c72fcc34Sopenharmony_ci	int err = 0;
84c72fcc34Sopenharmony_ci
85c72fcc34Sopenharmony_ci	if (key == 'D')
86c72fcc34Sopenharmony_ci		state->node_literal = arg_duplicate_string(optarg, &err);
87c72fcc34Sopenharmony_ci	else if (key == 'N')
88c72fcc34Sopenharmony_ci		state->nonblock = true;
89c72fcc34Sopenharmony_ci	else if (key == 'M')
90c72fcc34Sopenharmony_ci		state->mmap = true;
91c72fcc34Sopenharmony_ci	else if (key == 'F')
92c72fcc34Sopenharmony_ci		state->msec_per_period = arg_parse_decimal_num(optarg, &err);
93c72fcc34Sopenharmony_ci	else if (key == 'B')
94c72fcc34Sopenharmony_ci		state->msec_per_buffer = arg_parse_decimal_num(optarg, &err);
95c72fcc34Sopenharmony_ci	else if (key == OPT_PERIOD_SIZE)
96c72fcc34Sopenharmony_ci		state->frames_per_period = arg_parse_decimal_num(optarg, &err);
97c72fcc34Sopenharmony_ci	else if (key == OPT_BUFFER_SIZE)
98c72fcc34Sopenharmony_ci		state->frames_per_buffer = arg_parse_decimal_num(optarg, &err);
99c72fcc34Sopenharmony_ci	else if (key == 'A')
100c72fcc34Sopenharmony_ci		state->msec_for_avail_min = arg_parse_decimal_num(optarg, &err);
101c72fcc34Sopenharmony_ci	else if (key == 'R')
102c72fcc34Sopenharmony_ci		state->msec_for_start_threshold = arg_parse_decimal_num(optarg, &err);
103c72fcc34Sopenharmony_ci	else if (key == 'T')
104c72fcc34Sopenharmony_ci		state->msec_for_stop_threshold = arg_parse_decimal_num(optarg, &err);
105c72fcc34Sopenharmony_ci	else if (key == OPT_WAITER_TYPE)
106c72fcc34Sopenharmony_ci		state->waiter_type_literal = arg_duplicate_string(optarg, &err);
107c72fcc34Sopenharmony_ci	else if (key == OPT_SCHED_MODEL)
108c72fcc34Sopenharmony_ci		state->sched_model_literal = arg_duplicate_string(optarg, &err);
109c72fcc34Sopenharmony_ci	else if (key == OPT_DISABLE_RESAMPLE)
110c72fcc34Sopenharmony_ci		state->no_auto_resample = true;
111c72fcc34Sopenharmony_ci	else if (key == OPT_DISABLE_CHANNELS)
112c72fcc34Sopenharmony_ci		state->no_auto_channels = true;
113c72fcc34Sopenharmony_ci	else if (key == OPT_DISABLE_FORMAT)
114c72fcc34Sopenharmony_ci		state->no_auto_format = true;
115c72fcc34Sopenharmony_ci	else if (key == OPT_DISABLE_SOFTVOL)
116c72fcc34Sopenharmony_ci		state->no_softvol = true;
117c72fcc34Sopenharmony_ci	else if (key == 'm' ||
118c72fcc34Sopenharmony_ci		 key == OPT_TEST_POSITION ||
119c72fcc34Sopenharmony_ci		 key == OPT_TEST_COEF)
120c72fcc34Sopenharmony_ci		err = -EINVAL;
121c72fcc34Sopenharmony_ci	else if (key == OPT_FATAL_ERRORS)
122c72fcc34Sopenharmony_ci		state->finish_at_xrun = true;
123c72fcc34Sopenharmony_ci	else if (key == OPT_TEST_NOWAIT)
124c72fcc34Sopenharmony_ci		state->test_nowait = true;
125c72fcc34Sopenharmony_ci	else
126c72fcc34Sopenharmony_ci		err = -ENXIO;
127c72fcc34Sopenharmony_ci
128c72fcc34Sopenharmony_ci	return err;
129c72fcc34Sopenharmony_ci}
130c72fcc34Sopenharmony_ci
131c72fcc34Sopenharmony_ciint xfer_libasound_validate_opts(struct xfer_context *xfer)
132c72fcc34Sopenharmony_ci{
133c72fcc34Sopenharmony_ci	struct libasound_state *state = xfer->private_data;
134c72fcc34Sopenharmony_ci	int err = 0;
135c72fcc34Sopenharmony_ci
136c72fcc34Sopenharmony_ci	state->verbose = xfer->verbose > 1;
137c72fcc34Sopenharmony_ci
138c72fcc34Sopenharmony_ci	if (state->node_literal == NULL) {
139c72fcc34Sopenharmony_ci		state->node_literal = strdup("default");
140c72fcc34Sopenharmony_ci		if (state->node_literal == NULL)
141c72fcc34Sopenharmony_ci			return -ENOMEM;
142c72fcc34Sopenharmony_ci	}
143c72fcc34Sopenharmony_ci
144c72fcc34Sopenharmony_ci	if (state->mmap && state->nonblock) {
145c72fcc34Sopenharmony_ci		fprintf(stderr,
146c72fcc34Sopenharmony_ci			"An option for mmap operation should not be used with "
147c72fcc34Sopenharmony_ci			"nonblocking option.\n");
148c72fcc34Sopenharmony_ci		return -EINVAL;
149c72fcc34Sopenharmony_ci	}
150c72fcc34Sopenharmony_ci
151c72fcc34Sopenharmony_ci	if (state->test_nowait) {
152c72fcc34Sopenharmony_ci		if (!state->nonblock && !state->mmap) {
153c72fcc34Sopenharmony_ci			fprintf(stderr,
154c72fcc34Sopenharmony_ci				"An option for nowait test should be used with "
155c72fcc34Sopenharmony_ci				"nonblock or mmap options.\n");
156c72fcc34Sopenharmony_ci			return -EINVAL;
157c72fcc34Sopenharmony_ci		}
158c72fcc34Sopenharmony_ci	}
159c72fcc34Sopenharmony_ci
160c72fcc34Sopenharmony_ci	if (state->msec_per_period > 0 && state->msec_per_buffer > 0) {
161c72fcc34Sopenharmony_ci		if (state->msec_per_period > state->msec_per_buffer) {
162c72fcc34Sopenharmony_ci			state->msec_per_period = state->msec_per_buffer;
163c72fcc34Sopenharmony_ci			state->msec_per_buffer = 0;
164c72fcc34Sopenharmony_ci		}
165c72fcc34Sopenharmony_ci	}
166c72fcc34Sopenharmony_ci
167c72fcc34Sopenharmony_ci	if (state->frames_per_period > 0 && state->frames_per_buffer > 0) {
168c72fcc34Sopenharmony_ci		if (state->frames_per_period > state->frames_per_buffer) {
169c72fcc34Sopenharmony_ci			state->frames_per_period = state->frames_per_buffer;
170c72fcc34Sopenharmony_ci			state->frames_per_buffer = 0;
171c72fcc34Sopenharmony_ci		}
172c72fcc34Sopenharmony_ci	}
173c72fcc34Sopenharmony_ci
174c72fcc34Sopenharmony_ci	state->sched_model = SCHED_MODEL_IRQ;
175c72fcc34Sopenharmony_ci	if (state->sched_model_literal != NULL) {
176c72fcc34Sopenharmony_ci		if (!strcmp(state->sched_model_literal, "timer")) {
177c72fcc34Sopenharmony_ci			state->sched_model = SCHED_MODEL_TIMER;
178c72fcc34Sopenharmony_ci			state->mmap = true;
179c72fcc34Sopenharmony_ci			state->nonblock = true;
180c72fcc34Sopenharmony_ci		}
181c72fcc34Sopenharmony_ci	}
182c72fcc34Sopenharmony_ci
183c72fcc34Sopenharmony_ci	if (state->waiter_type_literal != NULL) {
184c72fcc34Sopenharmony_ci		if (state->test_nowait) {
185c72fcc34Sopenharmony_ci			fprintf(stderr,
186c72fcc34Sopenharmony_ci				"An option for waiter type should not be "
187c72fcc34Sopenharmony_ci				"used with nowait test option.\n");
188c72fcc34Sopenharmony_ci			return -EINVAL;
189c72fcc34Sopenharmony_ci		}
190c72fcc34Sopenharmony_ci		if (!state->nonblock && !state->mmap) {
191c72fcc34Sopenharmony_ci			fprintf(stderr,
192c72fcc34Sopenharmony_ci				"An option for waiter type should be used "
193c72fcc34Sopenharmony_ci				"with nonblock or mmap or timer-based "
194c72fcc34Sopenharmony_ci				"scheduling options.\n");
195c72fcc34Sopenharmony_ci			return -EINVAL;
196c72fcc34Sopenharmony_ci		}
197c72fcc34Sopenharmony_ci		state->waiter_type =
198c72fcc34Sopenharmony_ci			waiter_type_from_label(state->waiter_type_literal);
199c72fcc34Sopenharmony_ci	} else {
200c72fcc34Sopenharmony_ci		state->waiter_type = WAITER_TYPE_DEFAULT;
201c72fcc34Sopenharmony_ci	}
202c72fcc34Sopenharmony_ci
203c72fcc34Sopenharmony_ci	return err;
204c72fcc34Sopenharmony_ci}
205c72fcc34Sopenharmony_ci
206c72fcc34Sopenharmony_cistatic int set_access_hw_param(struct libasound_state *state)
207c72fcc34Sopenharmony_ci{
208c72fcc34Sopenharmony_ci	snd_pcm_access_mask_t *mask;
209c72fcc34Sopenharmony_ci	int err;
210c72fcc34Sopenharmony_ci
211c72fcc34Sopenharmony_ci	err = snd_pcm_access_mask_malloc(&mask);
212c72fcc34Sopenharmony_ci	if (err < 0)
213c72fcc34Sopenharmony_ci		return err;
214c72fcc34Sopenharmony_ci	snd_pcm_access_mask_none(mask);
215c72fcc34Sopenharmony_ci	if (state->mmap) {
216c72fcc34Sopenharmony_ci		snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
217c72fcc34Sopenharmony_ci		snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
218c72fcc34Sopenharmony_ci	} else {
219c72fcc34Sopenharmony_ci		snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_RW_INTERLEAVED);
220c72fcc34Sopenharmony_ci		snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
221c72fcc34Sopenharmony_ci	}
222c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_set_access_mask(state->handle, state->hw_params,
223c72fcc34Sopenharmony_ci						mask);
224c72fcc34Sopenharmony_ci	snd_pcm_access_mask_free(mask);
225c72fcc34Sopenharmony_ci
226c72fcc34Sopenharmony_ci	return err;
227c72fcc34Sopenharmony_ci}
228c72fcc34Sopenharmony_ci
229c72fcc34Sopenharmony_cistatic int disable_period_wakeup(struct libasound_state *state)
230c72fcc34Sopenharmony_ci{
231c72fcc34Sopenharmony_ci	int err;
232c72fcc34Sopenharmony_ci
233c72fcc34Sopenharmony_ci	if (snd_pcm_type(state->handle) != SND_PCM_TYPE_HW) {
234c72fcc34Sopenharmony_ci		logging(state,
235c72fcc34Sopenharmony_ci			"Timer-based scheduling is only available for 'hw' "
236c72fcc34Sopenharmony_ci			"PCM plugin.\n");
237c72fcc34Sopenharmony_ci		return -ENXIO;
238c72fcc34Sopenharmony_ci	}
239c72fcc34Sopenharmony_ci
240c72fcc34Sopenharmony_ci	if (!snd_pcm_hw_params_can_disable_period_wakeup(state->hw_params)) {
241c72fcc34Sopenharmony_ci		logging(state,
242c72fcc34Sopenharmony_ci			"This hardware doesn't support the mode of no-period-"
243c72fcc34Sopenharmony_ci			"wakeup. In this case, timer-based scheduling is not "
244c72fcc34Sopenharmony_ci			"available.\n");
245c72fcc34Sopenharmony_ci		return -EIO;
246c72fcc34Sopenharmony_ci	}
247c72fcc34Sopenharmony_ci
248c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_set_period_wakeup(state->handle,
249c72fcc34Sopenharmony_ci						  state->hw_params, 0);
250c72fcc34Sopenharmony_ci	if (err < 0) {
251c72fcc34Sopenharmony_ci		logging(state,
252c72fcc34Sopenharmony_ci			"Fail to disable period wakeup so that the hardware "
253c72fcc34Sopenharmony_ci			"generates no IRQs during transmission of data "
254c72fcc34Sopenharmony_ci			"frames.\n");
255c72fcc34Sopenharmony_ci	}
256c72fcc34Sopenharmony_ci
257c72fcc34Sopenharmony_ci	return err;
258c72fcc34Sopenharmony_ci}
259c72fcc34Sopenharmony_ci
260c72fcc34Sopenharmony_cistatic int open_handle(struct xfer_context *xfer)
261c72fcc34Sopenharmony_ci{
262c72fcc34Sopenharmony_ci	struct libasound_state *state = xfer->private_data;
263c72fcc34Sopenharmony_ci	int mode = 0;
264c72fcc34Sopenharmony_ci	int err;
265c72fcc34Sopenharmony_ci
266c72fcc34Sopenharmony_ci	if (state->nonblock)
267c72fcc34Sopenharmony_ci		mode |= SND_PCM_NONBLOCK;
268c72fcc34Sopenharmony_ci	if (state->no_auto_resample)
269c72fcc34Sopenharmony_ci		mode |= SND_PCM_NO_AUTO_RESAMPLE;
270c72fcc34Sopenharmony_ci	if (state->no_auto_channels)
271c72fcc34Sopenharmony_ci		mode |= SND_PCM_NO_AUTO_CHANNELS;
272c72fcc34Sopenharmony_ci	if (state->no_auto_format)
273c72fcc34Sopenharmony_ci		mode |= SND_PCM_NO_AUTO_FORMAT;
274c72fcc34Sopenharmony_ci	if (state->no_softvol)
275c72fcc34Sopenharmony_ci		mode |= SND_PCM_NO_SOFTVOL;
276c72fcc34Sopenharmony_ci
277c72fcc34Sopenharmony_ci	err = snd_pcm_open(&state->handle, state->node_literal, xfer->direction,
278c72fcc34Sopenharmony_ci			   mode);
279c72fcc34Sopenharmony_ci	if (err < 0) {
280c72fcc34Sopenharmony_ci		logging(state, "Fail to open libasound PCM node for %s: %s\n",
281c72fcc34Sopenharmony_ci			snd_pcm_stream_name(xfer->direction),
282c72fcc34Sopenharmony_ci			state->node_literal);
283c72fcc34Sopenharmony_ci		return err;
284c72fcc34Sopenharmony_ci	}
285c72fcc34Sopenharmony_ci
286c72fcc34Sopenharmony_ci	if ((state->nonblock || state->mmap) && !state->test_nowait)
287c72fcc34Sopenharmony_ci		state->use_waiter = true;
288c72fcc34Sopenharmony_ci
289c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_any(state->handle, state->hw_params);
290c72fcc34Sopenharmony_ci	if (err < 0)
291c72fcc34Sopenharmony_ci		return err;
292c72fcc34Sopenharmony_ci
293c72fcc34Sopenharmony_ci	if (state->sched_model == SCHED_MODEL_TIMER) {
294c72fcc34Sopenharmony_ci		err = disable_period_wakeup(state);
295c72fcc34Sopenharmony_ci		if (err < 0)
296c72fcc34Sopenharmony_ci			return err;
297c72fcc34Sopenharmony_ci	}
298c72fcc34Sopenharmony_ci
299c72fcc34Sopenharmony_ci	if (xfer->dump_hw_params) {
300c72fcc34Sopenharmony_ci		logging(state, "Available HW Params of node: %s\n",
301c72fcc34Sopenharmony_ci			snd_pcm_name(state->handle));
302c72fcc34Sopenharmony_ci		snd_pcm_hw_params_dump(state->hw_params, state->log);
303c72fcc34Sopenharmony_ci		// TODO: there're more parameters which are not dumped by
304c72fcc34Sopenharmony_ci		// alsa-lib.
305c72fcc34Sopenharmony_ci		return 0;
306c72fcc34Sopenharmony_ci	}
307c72fcc34Sopenharmony_ci
308c72fcc34Sopenharmony_ci	return set_access_hw_param(state);
309c72fcc34Sopenharmony_ci}
310c72fcc34Sopenharmony_ci
311c72fcc34Sopenharmony_cistatic int prepare_waiter(struct libasound_state *state)
312c72fcc34Sopenharmony_ci{
313c72fcc34Sopenharmony_ci	unsigned int pfd_count;
314c72fcc34Sopenharmony_ci	int err;
315c72fcc34Sopenharmony_ci
316c72fcc34Sopenharmony_ci	// Nothing to do for dafault waiter (=snd_pcm_wait()).
317c72fcc34Sopenharmony_ci	if (state->waiter_type == WAITER_TYPE_DEFAULT)
318c72fcc34Sopenharmony_ci		return 0;
319c72fcc34Sopenharmony_ci
320c72fcc34Sopenharmony_ci	err = snd_pcm_poll_descriptors_count(state->handle);
321c72fcc34Sopenharmony_ci	if (err < 0)
322c72fcc34Sopenharmony_ci		return err;
323c72fcc34Sopenharmony_ci	if (err == 0)
324c72fcc34Sopenharmony_ci		return -ENXIO;
325c72fcc34Sopenharmony_ci	pfd_count = (unsigned int)err;
326c72fcc34Sopenharmony_ci
327c72fcc34Sopenharmony_ci	state->waiter = malloc(sizeof(*state->waiter));
328c72fcc34Sopenharmony_ci	if (state->waiter == NULL)
329c72fcc34Sopenharmony_ci		return -ENOMEM;
330c72fcc34Sopenharmony_ci
331c72fcc34Sopenharmony_ci	err = waiter_context_init(state->waiter, state->waiter_type, pfd_count);
332c72fcc34Sopenharmony_ci	if (err < 0)
333c72fcc34Sopenharmony_ci		return err;
334c72fcc34Sopenharmony_ci
335c72fcc34Sopenharmony_ci	err = snd_pcm_poll_descriptors(state->handle, state->waiter->pfds,
336c72fcc34Sopenharmony_ci				       pfd_count);
337c72fcc34Sopenharmony_ci	if (err < 0)
338c72fcc34Sopenharmony_ci		return err;
339c72fcc34Sopenharmony_ci
340c72fcc34Sopenharmony_ci	return waiter_context_prepare(state->waiter);
341c72fcc34Sopenharmony_ci}
342c72fcc34Sopenharmony_ci
343c72fcc34Sopenharmony_ciint xfer_libasound_wait_event(struct libasound_state *state, int timeout_msec,
344c72fcc34Sopenharmony_ci			      unsigned short *revents)
345c72fcc34Sopenharmony_ci{
346c72fcc34Sopenharmony_ci	int count;
347c72fcc34Sopenharmony_ci
348c72fcc34Sopenharmony_ci	if (state->waiter_type != WAITER_TYPE_DEFAULT) {
349c72fcc34Sopenharmony_ci		struct waiter_context *waiter = state->waiter;
350c72fcc34Sopenharmony_ci		int err;
351c72fcc34Sopenharmony_ci
352c72fcc34Sopenharmony_ci		count = waiter_context_wait_event(waiter, timeout_msec);
353c72fcc34Sopenharmony_ci		if (count < 0)
354c72fcc34Sopenharmony_ci			return count;
355c72fcc34Sopenharmony_ci		if (count == 0 && timeout_msec > 0)
356c72fcc34Sopenharmony_ci			return -ETIMEDOUT;
357c72fcc34Sopenharmony_ci
358c72fcc34Sopenharmony_ci		err = snd_pcm_poll_descriptors_revents(state->handle,
359c72fcc34Sopenharmony_ci				waiter->pfds, waiter->pfd_count, revents);
360c72fcc34Sopenharmony_ci		if (err < 0)
361c72fcc34Sopenharmony_ci			return err;
362c72fcc34Sopenharmony_ci	} else {
363c72fcc34Sopenharmony_ci		count = snd_pcm_wait(state->handle, timeout_msec);
364c72fcc34Sopenharmony_ci		if (count < 0)
365c72fcc34Sopenharmony_ci			return count;
366c72fcc34Sopenharmony_ci		if (count == 0 && timeout_msec > 0)
367c72fcc34Sopenharmony_ci			return -ETIMEDOUT;
368c72fcc34Sopenharmony_ci
369c72fcc34Sopenharmony_ci		if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_PLAYBACK)
370c72fcc34Sopenharmony_ci			*revents = POLLOUT;
371c72fcc34Sopenharmony_ci		else
372c72fcc34Sopenharmony_ci			*revents = POLLIN;
373c72fcc34Sopenharmony_ci	}
374c72fcc34Sopenharmony_ci
375c72fcc34Sopenharmony_ci	return 0;
376c72fcc34Sopenharmony_ci}
377c72fcc34Sopenharmony_ci
378c72fcc34Sopenharmony_cistatic int configure_hw_params(struct libasound_state *state,
379c72fcc34Sopenharmony_ci			       snd_pcm_format_t format,
380c72fcc34Sopenharmony_ci			       unsigned int samples_per_frame,
381c72fcc34Sopenharmony_ci			       unsigned int frames_per_second,
382c72fcc34Sopenharmony_ci			       unsigned int msec_per_period,
383c72fcc34Sopenharmony_ci			       unsigned int msec_per_buffer,
384c72fcc34Sopenharmony_ci			       snd_pcm_uframes_t frames_per_period,
385c72fcc34Sopenharmony_ci			       snd_pcm_uframes_t frames_per_buffer)
386c72fcc34Sopenharmony_ci{
387c72fcc34Sopenharmony_ci	int err;
388c72fcc34Sopenharmony_ci
389c72fcc34Sopenharmony_ci	// Configure sample format.
390c72fcc34Sopenharmony_ci	if (format == SND_PCM_FORMAT_UNKNOWN) {
391c72fcc34Sopenharmony_ci		snd_pcm_format_mask_t *mask;
392c72fcc34Sopenharmony_ci
393c72fcc34Sopenharmony_ci		err = snd_pcm_format_mask_malloc(&mask);
394c72fcc34Sopenharmony_ci		if (err < 0)
395c72fcc34Sopenharmony_ci			return err;
396c72fcc34Sopenharmony_ci		snd_pcm_hw_params_get_format_mask(state->hw_params, mask);
397c72fcc34Sopenharmony_ci		for (format = 0; format <= SND_PCM_FORMAT_LAST; ++format) {
398c72fcc34Sopenharmony_ci			if (snd_pcm_format_mask_test(mask, format))
399c72fcc34Sopenharmony_ci				break;
400c72fcc34Sopenharmony_ci		}
401c72fcc34Sopenharmony_ci		snd_pcm_format_mask_free(mask);
402c72fcc34Sopenharmony_ci		if (format > SND_PCM_FORMAT_LAST) {
403c72fcc34Sopenharmony_ci			logging(state,
404c72fcc34Sopenharmony_ci				"Any sample format is not available.\n");
405c72fcc34Sopenharmony_ci			return -EINVAL;
406c72fcc34Sopenharmony_ci		}
407c72fcc34Sopenharmony_ci	}
408c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_set_format(state->handle, state->hw_params,
409c72fcc34Sopenharmony_ci					   format);
410c72fcc34Sopenharmony_ci	if (err < 0) {
411c72fcc34Sopenharmony_ci		logging(state,
412c72fcc34Sopenharmony_ci			"Sample format '%s' is not available: %s\n",
413c72fcc34Sopenharmony_ci			snd_pcm_format_name(format), snd_strerror(err));
414c72fcc34Sopenharmony_ci		return err;
415c72fcc34Sopenharmony_ci	}
416c72fcc34Sopenharmony_ci
417c72fcc34Sopenharmony_ci	// Configure channels.
418c72fcc34Sopenharmony_ci	if (samples_per_frame == 0) {
419c72fcc34Sopenharmony_ci		err = snd_pcm_hw_params_get_channels_min(state->hw_params,
420c72fcc34Sopenharmony_ci							 &samples_per_frame);
421c72fcc34Sopenharmony_ci		if (err < 0) {
422c72fcc34Sopenharmony_ci			logging(state,
423c72fcc34Sopenharmony_ci				"Any channel number is not available.\n");
424c72fcc34Sopenharmony_ci			return err;
425c72fcc34Sopenharmony_ci		}
426c72fcc34Sopenharmony_ci	}
427c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_set_channels(state->handle, state->hw_params,
428c72fcc34Sopenharmony_ci					     samples_per_frame);
429c72fcc34Sopenharmony_ci	if (err < 0) {
430c72fcc34Sopenharmony_ci		logging(state,
431c72fcc34Sopenharmony_ci			"Channels count '%u' is not available: %s\n",
432c72fcc34Sopenharmony_ci			samples_per_frame, snd_strerror(err));
433c72fcc34Sopenharmony_ci		return err;
434c72fcc34Sopenharmony_ci	}
435c72fcc34Sopenharmony_ci
436c72fcc34Sopenharmony_ci	// Configure rate.
437c72fcc34Sopenharmony_ci	if (frames_per_second == 0) {
438c72fcc34Sopenharmony_ci		err = snd_pcm_hw_params_get_rate_min(state->hw_params,
439c72fcc34Sopenharmony_ci						     &frames_per_second, NULL);
440c72fcc34Sopenharmony_ci		if (err < 0) {
441c72fcc34Sopenharmony_ci			logging(state,
442c72fcc34Sopenharmony_ci				"Any rate is not available.\n");
443c72fcc34Sopenharmony_ci			return err;
444c72fcc34Sopenharmony_ci		}
445c72fcc34Sopenharmony_ci
446c72fcc34Sopenharmony_ci	}
447c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_set_rate(state->handle, state->hw_params,
448c72fcc34Sopenharmony_ci					 frames_per_second, 0);
449c72fcc34Sopenharmony_ci	if (err < 0) {
450c72fcc34Sopenharmony_ci		logging(state,
451c72fcc34Sopenharmony_ci			"Sampling rate '%u' is not available: %s\n",
452c72fcc34Sopenharmony_ci			frames_per_second, snd_strerror(err));
453c72fcc34Sopenharmony_ci		return err;
454c72fcc34Sopenharmony_ci	}
455c72fcc34Sopenharmony_ci
456c72fcc34Sopenharmony_ci	// Keep one of 'frames_per_buffer' and 'msec_per_buffer'.
457c72fcc34Sopenharmony_ci	if (frames_per_buffer == 0) {
458c72fcc34Sopenharmony_ci		if (msec_per_buffer == 0) {
459c72fcc34Sopenharmony_ci			err = snd_pcm_hw_params_get_buffer_time_max(
460c72fcc34Sopenharmony_ci				state->hw_params, &msec_per_buffer, NULL);
461c72fcc34Sopenharmony_ci			if (err < 0) {
462c72fcc34Sopenharmony_ci				logging(state,
463c72fcc34Sopenharmony_ci					"The maximum msec per buffer is not "
464c72fcc34Sopenharmony_ci					"available.\n");
465c72fcc34Sopenharmony_ci				return err;
466c72fcc34Sopenharmony_ci			}
467c72fcc34Sopenharmony_ci			if (msec_per_buffer > 500000)
468c72fcc34Sopenharmony_ci				msec_per_buffer = 500000;
469c72fcc34Sopenharmony_ci		}
470c72fcc34Sopenharmony_ci	} else if (msec_per_buffer > 0) {
471c72fcc34Sopenharmony_ci		uint64_t msec;
472c72fcc34Sopenharmony_ci
473c72fcc34Sopenharmony_ci		msec = 1000000 * frames_per_buffer / frames_per_second;
474c72fcc34Sopenharmony_ci		if (msec < msec_per_buffer)
475c72fcc34Sopenharmony_ci			msec_per_buffer = 0;
476c72fcc34Sopenharmony_ci	}
477c72fcc34Sopenharmony_ci
478c72fcc34Sopenharmony_ci	// Keep one of 'frames_per_period' and 'msec_per_period'.
479c72fcc34Sopenharmony_ci	if (frames_per_period == 0) {
480c72fcc34Sopenharmony_ci		if (msec_per_period == 0) {
481c72fcc34Sopenharmony_ci			if (msec_per_buffer > 0)
482c72fcc34Sopenharmony_ci				msec_per_period = msec_per_buffer / 4;
483c72fcc34Sopenharmony_ci			else
484c72fcc34Sopenharmony_ci				frames_per_period = frames_per_buffer / 4;
485c72fcc34Sopenharmony_ci		}
486c72fcc34Sopenharmony_ci	} else if (msec_per_period > 0) {
487c72fcc34Sopenharmony_ci		uint64_t msec;
488c72fcc34Sopenharmony_ci
489c72fcc34Sopenharmony_ci		msec = 1000000 * frames_per_period / frames_per_second;
490c72fcc34Sopenharmony_ci		if (msec < msec_per_period)
491c72fcc34Sopenharmony_ci			msec_per_period = 0;
492c72fcc34Sopenharmony_ci	}
493c72fcc34Sopenharmony_ci
494c72fcc34Sopenharmony_ci	if (msec_per_period) {
495c72fcc34Sopenharmony_ci		err = snd_pcm_hw_params_set_period_time_near(state->handle,
496c72fcc34Sopenharmony_ci				state->hw_params, &msec_per_period, NULL);
497c72fcc34Sopenharmony_ci		if (err < 0) {
498c72fcc34Sopenharmony_ci			logging(state,
499c72fcc34Sopenharmony_ci				"Fail to configure period time: %u msec\n",
500c72fcc34Sopenharmony_ci				msec_per_period);
501c72fcc34Sopenharmony_ci			return err;
502c72fcc34Sopenharmony_ci		}
503c72fcc34Sopenharmony_ci	} else {
504c72fcc34Sopenharmony_ci		err = snd_pcm_hw_params_set_period_size_near(state->handle,
505c72fcc34Sopenharmony_ci				state->hw_params, &frames_per_period, NULL);
506c72fcc34Sopenharmony_ci		if (err < 0) {
507c72fcc34Sopenharmony_ci			logging(state,
508c72fcc34Sopenharmony_ci				"Fail to configure period size: %lu frames\n",
509c72fcc34Sopenharmony_ci				frames_per_period);
510c72fcc34Sopenharmony_ci			return err;
511c72fcc34Sopenharmony_ci		}
512c72fcc34Sopenharmony_ci	}
513c72fcc34Sopenharmony_ci
514c72fcc34Sopenharmony_ci	if (msec_per_buffer) {
515c72fcc34Sopenharmony_ci		err = snd_pcm_hw_params_set_buffer_time_near(state->handle,
516c72fcc34Sopenharmony_ci				state->hw_params, &msec_per_buffer, NULL);
517c72fcc34Sopenharmony_ci		if (err < 0) {
518c72fcc34Sopenharmony_ci			logging(state,
519c72fcc34Sopenharmony_ci				"Fail to configure buffer time: %u msec\n",
520c72fcc34Sopenharmony_ci				msec_per_buffer);
521c72fcc34Sopenharmony_ci			return err;
522c72fcc34Sopenharmony_ci		}
523c72fcc34Sopenharmony_ci	} else {
524c72fcc34Sopenharmony_ci		err = snd_pcm_hw_params_set_buffer_size_near(state->handle,
525c72fcc34Sopenharmony_ci					state->hw_params, &frames_per_buffer);
526c72fcc34Sopenharmony_ci		if (err < 0) {
527c72fcc34Sopenharmony_ci			logging(state,
528c72fcc34Sopenharmony_ci				"Fail to configure buffer size: %lu frames\n",
529c72fcc34Sopenharmony_ci				frames_per_buffer);
530c72fcc34Sopenharmony_ci			return err;
531c72fcc34Sopenharmony_ci		}
532c72fcc34Sopenharmony_ci	}
533c72fcc34Sopenharmony_ci
534c72fcc34Sopenharmony_ci	return snd_pcm_hw_params(state->handle, state->hw_params);
535c72fcc34Sopenharmony_ci}
536c72fcc34Sopenharmony_ci
537c72fcc34Sopenharmony_cistatic int retrieve_actual_hw_params(snd_pcm_hw_params_t *hw_params,
538c72fcc34Sopenharmony_ci				     snd_pcm_format_t *format,
539c72fcc34Sopenharmony_ci				     unsigned int *samples_per_frame,
540c72fcc34Sopenharmony_ci				     unsigned int *frames_per_second,
541c72fcc34Sopenharmony_ci				     snd_pcm_access_t *access,
542c72fcc34Sopenharmony_ci				     snd_pcm_uframes_t *frames_per_buffer)
543c72fcc34Sopenharmony_ci{
544c72fcc34Sopenharmony_ci	int err;
545c72fcc34Sopenharmony_ci
546c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_get_format(hw_params, format);
547c72fcc34Sopenharmony_ci	if (err < 0)
548c72fcc34Sopenharmony_ci		return err;
549c72fcc34Sopenharmony_ci
550c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_get_channels(hw_params,
551c72fcc34Sopenharmony_ci					     samples_per_frame);
552c72fcc34Sopenharmony_ci	if (err < 0)
553c72fcc34Sopenharmony_ci		return err;
554c72fcc34Sopenharmony_ci
555c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_get_rate(hw_params, frames_per_second,
556c72fcc34Sopenharmony_ci					 NULL);
557c72fcc34Sopenharmony_ci	if (err < 0)
558c72fcc34Sopenharmony_ci		return err;
559c72fcc34Sopenharmony_ci
560c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_get_access(hw_params, access);
561c72fcc34Sopenharmony_ci	if (err < 0)
562c72fcc34Sopenharmony_ci		return err;
563c72fcc34Sopenharmony_ci
564c72fcc34Sopenharmony_ci	return snd_pcm_hw_params_get_buffer_size(hw_params, frames_per_buffer);
565c72fcc34Sopenharmony_ci}
566c72fcc34Sopenharmony_ci
567c72fcc34Sopenharmony_cistatic int configure_sw_params(struct libasound_state *state,
568c72fcc34Sopenharmony_ci			       unsigned int frames_per_second,
569c72fcc34Sopenharmony_ci			       unsigned int frames_per_buffer,
570c72fcc34Sopenharmony_ci			       unsigned int msec_for_avail_min,
571c72fcc34Sopenharmony_ci			       unsigned int msec_for_start_threshold,
572c72fcc34Sopenharmony_ci			       unsigned int msec_for_stop_threshold)
573c72fcc34Sopenharmony_ci{
574c72fcc34Sopenharmony_ci	snd_pcm_uframes_t frame_count;
575c72fcc34Sopenharmony_ci	int err;
576c72fcc34Sopenharmony_ci
577c72fcc34Sopenharmony_ci	if (msec_for_avail_min > 0) {
578c72fcc34Sopenharmony_ci		frame_count = msec_for_avail_min * frames_per_second / 1000000;
579c72fcc34Sopenharmony_ci		if (frame_count == 0 || frame_count > frames_per_buffer) {
580c72fcc34Sopenharmony_ci			logging(state,
581c72fcc34Sopenharmony_ci				"The msec for 'avail_min' is too %s: %u "
582c72fcc34Sopenharmony_ci				"msec (%lu frames at %u).\n",
583c72fcc34Sopenharmony_ci				frame_count == 0 ? "small" : "large",
584c72fcc34Sopenharmony_ci				msec_for_avail_min, frame_count,
585c72fcc34Sopenharmony_ci				frames_per_second);
586c72fcc34Sopenharmony_ci			return -EINVAL;
587c72fcc34Sopenharmony_ci		}
588c72fcc34Sopenharmony_ci		err = snd_pcm_sw_params_set_avail_min(state->handle,
589c72fcc34Sopenharmony_ci						state->sw_params, frame_count);
590c72fcc34Sopenharmony_ci		if (err < 0) {
591c72fcc34Sopenharmony_ci			logging(state,
592c72fcc34Sopenharmony_ci				"Fail to configure 'avail-min'.\n");
593c72fcc34Sopenharmony_ci			return -EINVAL;
594c72fcc34Sopenharmony_ci		}
595c72fcc34Sopenharmony_ci	}
596c72fcc34Sopenharmony_ci
597c72fcc34Sopenharmony_ci	if (msec_for_start_threshold > 0) {
598c72fcc34Sopenharmony_ci		frame_count = msec_for_start_threshold * frames_per_second /
599c72fcc34Sopenharmony_ci			      1000000;
600c72fcc34Sopenharmony_ci		if (frame_count == 0 || frame_count > frames_per_buffer) {
601c72fcc34Sopenharmony_ci			logging(state,
602c72fcc34Sopenharmony_ci				"The msec for 'start-delay' is too %s: %u "
603c72fcc34Sopenharmony_ci				"msec (%lu frames at %u).\n",
604c72fcc34Sopenharmony_ci				frame_count == 0 ? "small" : "large",
605c72fcc34Sopenharmony_ci				msec_for_start_threshold, frame_count,
606c72fcc34Sopenharmony_ci				frames_per_second);
607c72fcc34Sopenharmony_ci			return -EINVAL;
608c72fcc34Sopenharmony_ci		}
609c72fcc34Sopenharmony_ci		err = snd_pcm_sw_params_set_start_threshold(state->handle,
610c72fcc34Sopenharmony_ci						state->sw_params, frame_count);
611c72fcc34Sopenharmony_ci		if (err < 0) {
612c72fcc34Sopenharmony_ci			logging(state,
613c72fcc34Sopenharmony_ci				"Fail to configure 'start-delay'.\n");
614c72fcc34Sopenharmony_ci			return -EINVAL;
615c72fcc34Sopenharmony_ci		}
616c72fcc34Sopenharmony_ci	}
617c72fcc34Sopenharmony_ci
618c72fcc34Sopenharmony_ci	if (msec_for_stop_threshold > 0) {
619c72fcc34Sopenharmony_ci		frame_count = msec_for_stop_threshold * frames_per_second /
620c72fcc34Sopenharmony_ci			      1000000;
621c72fcc34Sopenharmony_ci		if (frame_count == 0 || frame_count > frames_per_buffer) {
622c72fcc34Sopenharmony_ci			logging(state,
623c72fcc34Sopenharmony_ci				"The msec for 'stop-delay' is too %s: %u "
624c72fcc34Sopenharmony_ci				"msec (%lu frames at %u).\n",
625c72fcc34Sopenharmony_ci				frame_count == 0 ? "small" : "large",
626c72fcc34Sopenharmony_ci				msec_for_stop_threshold, frame_count,
627c72fcc34Sopenharmony_ci				frames_per_second);
628c72fcc34Sopenharmony_ci			return -EINVAL;
629c72fcc34Sopenharmony_ci		}
630c72fcc34Sopenharmony_ci		err = snd_pcm_sw_params_set_stop_threshold(state->handle,
631c72fcc34Sopenharmony_ci						state->sw_params, frame_count);
632c72fcc34Sopenharmony_ci		if (err < 0) {
633c72fcc34Sopenharmony_ci			logging(state,
634c72fcc34Sopenharmony_ci				"Fail to configure 'stop-delay'.\n");
635c72fcc34Sopenharmony_ci			return -EINVAL;
636c72fcc34Sopenharmony_ci		}
637c72fcc34Sopenharmony_ci	}
638c72fcc34Sopenharmony_ci
639c72fcc34Sopenharmony_ci	return snd_pcm_sw_params(state->handle, state->sw_params);
640c72fcc34Sopenharmony_ci}
641c72fcc34Sopenharmony_ci
642c72fcc34Sopenharmony_cistatic int xfer_libasound_pre_process(struct xfer_context *xfer,
643c72fcc34Sopenharmony_ci				      snd_pcm_format_t *format,
644c72fcc34Sopenharmony_ci				      unsigned int *samples_per_frame,
645c72fcc34Sopenharmony_ci				      unsigned int *frames_per_second,
646c72fcc34Sopenharmony_ci				      snd_pcm_access_t *access,
647c72fcc34Sopenharmony_ci				      snd_pcm_uframes_t *frames_per_buffer)
648c72fcc34Sopenharmony_ci{
649c72fcc34Sopenharmony_ci	struct libasound_state *state = xfer->private_data;
650c72fcc34Sopenharmony_ci	unsigned int flag;
651c72fcc34Sopenharmony_ci	int err;
652c72fcc34Sopenharmony_ci
653c72fcc34Sopenharmony_ci	err = open_handle(xfer);
654c72fcc34Sopenharmony_ci	if (err < 0)
655c72fcc34Sopenharmony_ci		return -ENXIO;
656c72fcc34Sopenharmony_ci
657c72fcc34Sopenharmony_ci	err = configure_hw_params(state, *format, *samples_per_frame,
658c72fcc34Sopenharmony_ci				  *frames_per_second,
659c72fcc34Sopenharmony_ci				  state->msec_per_period,
660c72fcc34Sopenharmony_ci				  state->msec_per_buffer,
661c72fcc34Sopenharmony_ci				  state->frames_per_period,
662c72fcc34Sopenharmony_ci				  state->frames_per_buffer);
663c72fcc34Sopenharmony_ci	if (err < 0) {
664c72fcc34Sopenharmony_ci		logging(state, "Current hardware parameters:\n");
665c72fcc34Sopenharmony_ci		snd_pcm_hw_params_dump(state->hw_params, state->log);
666c72fcc34Sopenharmony_ci		return err;
667c72fcc34Sopenharmony_ci	}
668c72fcc34Sopenharmony_ci
669c72fcc34Sopenharmony_ci	// Retrieve actual parameters.
670c72fcc34Sopenharmony_ci	err = retrieve_actual_hw_params(state->hw_params, format,
671c72fcc34Sopenharmony_ci					samples_per_frame, frames_per_second,
672c72fcc34Sopenharmony_ci					access, frames_per_buffer);
673c72fcc34Sopenharmony_ci	if (err < 0)
674c72fcc34Sopenharmony_ci		return err;
675c72fcc34Sopenharmony_ci
676c72fcc34Sopenharmony_ci	// Query software parameters.
677c72fcc34Sopenharmony_ci	err = snd_pcm_sw_params_current(state->handle, state->sw_params);
678c72fcc34Sopenharmony_ci	if (err < 0)
679c72fcc34Sopenharmony_ci		return err;
680c72fcc34Sopenharmony_ci
681c72fcc34Sopenharmony_ci	// Assign I/O operation.
682c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_get_period_wakeup(state->handle,
683c72fcc34Sopenharmony_ci						  state->hw_params, &flag);
684c72fcc34Sopenharmony_ci	if (err < 0)
685c72fcc34Sopenharmony_ci		return err;
686c72fcc34Sopenharmony_ci
687c72fcc34Sopenharmony_ci	if (flag) {
688c72fcc34Sopenharmony_ci		if (*access == SND_PCM_ACCESS_RW_INTERLEAVED ||
689c72fcc34Sopenharmony_ci		    *access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
690c72fcc34Sopenharmony_ci			state->ops = &xfer_libasound_irq_rw_ops;
691c72fcc34Sopenharmony_ci		} else if (*access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
692c72fcc34Sopenharmony_ci			   *access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) {
693c72fcc34Sopenharmony_ci			if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE)
694c72fcc34Sopenharmony_ci				state->ops = &xfer_libasound_irq_mmap_r_ops;
695c72fcc34Sopenharmony_ci			else
696c72fcc34Sopenharmony_ci				state->ops = &xfer_libasound_irq_mmap_w_ops;
697c72fcc34Sopenharmony_ci		} else {
698c72fcc34Sopenharmony_ci			return -ENXIO;
699c72fcc34Sopenharmony_ci		}
700c72fcc34Sopenharmony_ci	} else {
701c72fcc34Sopenharmony_ci		if (*access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
702c72fcc34Sopenharmony_ci		    *access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) {
703c72fcc34Sopenharmony_ci			if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE)
704c72fcc34Sopenharmony_ci				state->ops = &xfer_libasound_timer_mmap_r_ops;
705c72fcc34Sopenharmony_ci			else
706c72fcc34Sopenharmony_ci				state->ops = &xfer_libasound_timer_mmap_w_ops;
707c72fcc34Sopenharmony_ci		} else {
708c72fcc34Sopenharmony_ci			return -ENXIO;
709c72fcc34Sopenharmony_ci		}
710c72fcc34Sopenharmony_ci	}
711c72fcc34Sopenharmony_ci
712c72fcc34Sopenharmony_ci	if (state->ops->private_size > 0) {
713c72fcc34Sopenharmony_ci		state->private_data = malloc(state->ops->private_size);
714c72fcc34Sopenharmony_ci		if (state->private_data == NULL)
715c72fcc34Sopenharmony_ci			return -ENOMEM;
716c72fcc34Sopenharmony_ci		memset(state->private_data, 0, state->ops->private_size);
717c72fcc34Sopenharmony_ci	}
718c72fcc34Sopenharmony_ci
719c72fcc34Sopenharmony_ci	err = state->ops->pre_process(state);
720c72fcc34Sopenharmony_ci	if (err < 0)
721c72fcc34Sopenharmony_ci		return err;
722c72fcc34Sopenharmony_ci
723c72fcc34Sopenharmony_ci	err = configure_sw_params(state, *frames_per_second,
724c72fcc34Sopenharmony_ci				  *frames_per_buffer,
725c72fcc34Sopenharmony_ci				  state->msec_for_avail_min,
726c72fcc34Sopenharmony_ci				  state->msec_for_start_threshold,
727c72fcc34Sopenharmony_ci				  state->msec_for_stop_threshold);
728c72fcc34Sopenharmony_ci	if (err < 0) {
729c72fcc34Sopenharmony_ci		logging(state, "Current software parameters:\n");
730c72fcc34Sopenharmony_ci		snd_pcm_sw_params_dump(state->sw_params, state->log);
731c72fcc34Sopenharmony_ci		return err;
732c72fcc34Sopenharmony_ci	}
733c72fcc34Sopenharmony_ci
734c72fcc34Sopenharmony_ci	if (xfer->verbose > 0) {
735c72fcc34Sopenharmony_ci		snd_pcm_dump(state->handle, state->log);
736c72fcc34Sopenharmony_ci		logging(state, "Scheduling model:\n");
737c72fcc34Sopenharmony_ci		logging(state, "  %s\n", sched_model_labels[state->sched_model]);
738c72fcc34Sopenharmony_ci	}
739c72fcc34Sopenharmony_ci
740c72fcc34Sopenharmony_ci	if (state->use_waiter) {
741c72fcc34Sopenharmony_ci		// NOTE: This should be after configuring sw_params due to
742c72fcc34Sopenharmony_ci		// timer descriptor for time-based scheduling model.
743c72fcc34Sopenharmony_ci		err = prepare_waiter(state);
744c72fcc34Sopenharmony_ci		if (err < 0)
745c72fcc34Sopenharmony_ci			return err;
746c72fcc34Sopenharmony_ci
747c72fcc34Sopenharmony_ci		if (xfer->verbose > 0) {
748c72fcc34Sopenharmony_ci			logging(state, "Waiter type:\n");
749c72fcc34Sopenharmony_ci			logging(state,
750c72fcc34Sopenharmony_ci				"  %s\n",
751c72fcc34Sopenharmony_ci				waiter_label_from_type(state->waiter_type));
752c72fcc34Sopenharmony_ci		}
753c72fcc34Sopenharmony_ci	}
754c72fcc34Sopenharmony_ci
755c72fcc34Sopenharmony_ci	return 0;
756c72fcc34Sopenharmony_ci}
757c72fcc34Sopenharmony_ci
758c72fcc34Sopenharmony_cistatic int xfer_libasound_process_frames(struct xfer_context *xfer,
759c72fcc34Sopenharmony_ci					 unsigned int *frame_count,
760c72fcc34Sopenharmony_ci					 struct mapper_context *mapper,
761c72fcc34Sopenharmony_ci					 struct container_context *cntrs)
762c72fcc34Sopenharmony_ci{
763c72fcc34Sopenharmony_ci	struct libasound_state *state = xfer->private_data;
764c72fcc34Sopenharmony_ci	int err;
765c72fcc34Sopenharmony_ci
766c72fcc34Sopenharmony_ci	if (state->handle == NULL)
767c72fcc34Sopenharmony_ci		return -ENXIO;
768c72fcc34Sopenharmony_ci
769c72fcc34Sopenharmony_ci	err = state->ops->process_frames(state, frame_count, mapper, cntrs);
770c72fcc34Sopenharmony_ci	if (err < 0) {
771c72fcc34Sopenharmony_ci		if (err == -EAGAIN)
772c72fcc34Sopenharmony_ci			return err;
773c72fcc34Sopenharmony_ci		if (err == -EPIPE && !state->finish_at_xrun) {
774c72fcc34Sopenharmony_ci			// Recover the stream and continue processing
775c72fcc34Sopenharmony_ci			// immediately. In this program -EPIPE comes from
776c72fcc34Sopenharmony_ci			// libasound implementation instead of file I/O.
777c72fcc34Sopenharmony_ci			err = snd_pcm_prepare(state->handle);
778c72fcc34Sopenharmony_ci		}
779c72fcc34Sopenharmony_ci
780c72fcc34Sopenharmony_ci		if (err < 0) {
781c72fcc34Sopenharmony_ci			// TODO: -EIO from libasound for hw PCM node means
782c72fcc34Sopenharmony_ci			// that IRQ disorder. This should be reported to help
783c72fcc34Sopenharmony_ci			// developers for drivers.
784c72fcc34Sopenharmony_ci			logging(state, "Fail to process frames: %s\n",
785c72fcc34Sopenharmony_ci				snd_strerror(err));
786c72fcc34Sopenharmony_ci		}
787c72fcc34Sopenharmony_ci	}
788c72fcc34Sopenharmony_ci
789c72fcc34Sopenharmony_ci	return err;
790c72fcc34Sopenharmony_ci}
791c72fcc34Sopenharmony_ci
792c72fcc34Sopenharmony_cistatic void xfer_libasound_pause(struct xfer_context *xfer, bool enable)
793c72fcc34Sopenharmony_ci{
794c72fcc34Sopenharmony_ci	struct libasound_state *state = xfer->private_data;
795c72fcc34Sopenharmony_ci	snd_pcm_state_t s = snd_pcm_state(state->handle);
796c72fcc34Sopenharmony_ci	int err;
797c72fcc34Sopenharmony_ci
798c72fcc34Sopenharmony_ci	if (state->handle == NULL)
799c72fcc34Sopenharmony_ci		return;
800c72fcc34Sopenharmony_ci
801c72fcc34Sopenharmony_ci	if (enable) {
802c72fcc34Sopenharmony_ci		if (s != SND_PCM_STATE_RUNNING)
803c72fcc34Sopenharmony_ci			return;
804c72fcc34Sopenharmony_ci	} else {
805c72fcc34Sopenharmony_ci		if (s != SND_PCM_STATE_PAUSED)
806c72fcc34Sopenharmony_ci			return;
807c72fcc34Sopenharmony_ci	}
808c72fcc34Sopenharmony_ci
809c72fcc34Sopenharmony_ci	// Not supported. Leave the substream to enter XRUN state.
810c72fcc34Sopenharmony_ci	if (!snd_pcm_hw_params_can_pause(state->hw_params))
811c72fcc34Sopenharmony_ci		return;
812c72fcc34Sopenharmony_ci
813c72fcc34Sopenharmony_ci	err = snd_pcm_pause(state->handle, enable);
814c72fcc34Sopenharmony_ci	if (err < 0 && state->verbose) {
815c72fcc34Sopenharmony_ci		logging(state, "snd_pcm_pause(): %s\n", snd_strerror(err));
816c72fcc34Sopenharmony_ci	}
817c72fcc34Sopenharmony_ci}
818c72fcc34Sopenharmony_ci
819c72fcc34Sopenharmony_cistatic void xfer_libasound_post_process(struct xfer_context *xfer)
820c72fcc34Sopenharmony_ci{
821c72fcc34Sopenharmony_ci	struct libasound_state *state = xfer->private_data;
822c72fcc34Sopenharmony_ci	snd_pcm_state_t pcm_state;
823c72fcc34Sopenharmony_ci	int err;
824c72fcc34Sopenharmony_ci
825c72fcc34Sopenharmony_ci	if (state->handle == NULL)
826c72fcc34Sopenharmony_ci		return;
827c72fcc34Sopenharmony_ci
828c72fcc34Sopenharmony_ci	pcm_state = snd_pcm_state(state->handle);
829c72fcc34Sopenharmony_ci	if (pcm_state != SND_PCM_STATE_OPEN &&
830c72fcc34Sopenharmony_ci	    pcm_state != SND_PCM_STATE_DISCONNECTED) {
831c72fcc34Sopenharmony_ci		if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE ||
832c72fcc34Sopenharmony_ci		    state->ops == &xfer_libasound_timer_mmap_w_ops) {
833c72fcc34Sopenharmony_ci			err = snd_pcm_drop(state->handle);
834c72fcc34Sopenharmony_ci			if (err < 0)
835c72fcc34Sopenharmony_ci				logging(state, "snd_pcm_drop(): %s\n",
836c72fcc34Sopenharmony_ci				       snd_strerror(err));
837c72fcc34Sopenharmony_ci		} else {
838c72fcc34Sopenharmony_ci			// TODO: this is a bug in kernel land.
839c72fcc34Sopenharmony_ci			if (state->nonblock)
840c72fcc34Sopenharmony_ci				snd_pcm_nonblock(state->handle, 0);
841c72fcc34Sopenharmony_ci			err = snd_pcm_drain(state->handle);
842c72fcc34Sopenharmony_ci			if (state->nonblock)
843c72fcc34Sopenharmony_ci				snd_pcm_nonblock(state->handle, 1);
844c72fcc34Sopenharmony_ci			if (err < 0)
845c72fcc34Sopenharmony_ci				logging(state, "snd_pcm_drain(): %s\n",
846c72fcc34Sopenharmony_ci				       snd_strerror(err));
847c72fcc34Sopenharmony_ci		}
848c72fcc34Sopenharmony_ci	}
849c72fcc34Sopenharmony_ci
850c72fcc34Sopenharmony_ci	err = snd_pcm_hw_free(state->handle);
851c72fcc34Sopenharmony_ci	if (err < 0)
852c72fcc34Sopenharmony_ci		logging(state, "snd_pcm_hw_free(): %s\n", snd_strerror(err));
853c72fcc34Sopenharmony_ci
854c72fcc34Sopenharmony_ci	snd_pcm_close(state->handle);
855c72fcc34Sopenharmony_ci	state->handle = NULL;
856c72fcc34Sopenharmony_ci
857c72fcc34Sopenharmony_ci	if (state->ops && state->ops->post_process)
858c72fcc34Sopenharmony_ci		state->ops->post_process(state);
859c72fcc34Sopenharmony_ci	free(state->private_data);
860c72fcc34Sopenharmony_ci	state->private_data = NULL;
861c72fcc34Sopenharmony_ci
862c72fcc34Sopenharmony_ci	// Free cache of content for configuration files so that memory leaks
863c72fcc34Sopenharmony_ci	// are not detected.
864c72fcc34Sopenharmony_ci	snd_config_update_free_global();
865c72fcc34Sopenharmony_ci}
866c72fcc34Sopenharmony_ci
867c72fcc34Sopenharmony_cistatic void xfer_libasound_destroy(struct xfer_context *xfer)
868c72fcc34Sopenharmony_ci{
869c72fcc34Sopenharmony_ci	struct libasound_state *state = xfer->private_data;
870c72fcc34Sopenharmony_ci
871c72fcc34Sopenharmony_ci	free(state->node_literal);
872c72fcc34Sopenharmony_ci	free(state->waiter_type_literal);
873c72fcc34Sopenharmony_ci	free(state->sched_model_literal);
874c72fcc34Sopenharmony_ci	state->node_literal = NULL;
875c72fcc34Sopenharmony_ci	state->waiter_type_literal = NULL;
876c72fcc34Sopenharmony_ci	state->sched_model_literal = NULL;
877c72fcc34Sopenharmony_ci
878c72fcc34Sopenharmony_ci	if (state->hw_params)
879c72fcc34Sopenharmony_ci		snd_pcm_hw_params_free(state->hw_params);
880c72fcc34Sopenharmony_ci	if (state->sw_params)
881c72fcc34Sopenharmony_ci		snd_pcm_sw_params_free(state->sw_params);
882c72fcc34Sopenharmony_ci	state->hw_params = NULL;
883c72fcc34Sopenharmony_ci	state->sw_params = NULL;
884c72fcc34Sopenharmony_ci
885c72fcc34Sopenharmony_ci	if (state->log)
886c72fcc34Sopenharmony_ci		snd_output_close(state->log);
887c72fcc34Sopenharmony_ci	state->log = NULL;
888c72fcc34Sopenharmony_ci}
889c72fcc34Sopenharmony_ci
890c72fcc34Sopenharmony_cistatic void xfer_libasound_help(struct xfer_context *xfer ATTRIBUTE_UNUSED)
891c72fcc34Sopenharmony_ci{
892c72fcc34Sopenharmony_ci	printf(
893c72fcc34Sopenharmony_ci"      [BASICS]\n"
894c72fcc34Sopenharmony_ci"        -D, --device          select node by name in coniguration space\n"
895c72fcc34Sopenharmony_ci"        -N, --nonblock        nonblocking mode\n"
896c72fcc34Sopenharmony_ci"        -M, --mmap            use mmap(2) for zero copying technique\n"
897c72fcc34Sopenharmony_ci"        -F, --period-time     interval between interrupts (msec unit)\n"
898c72fcc34Sopenharmony_ci"        --period-size         interval between interrupts (frame unit)\n"
899c72fcc34Sopenharmony_ci"        -B, --buffer-time     size of buffer for frame(msec unit)\n"
900c72fcc34Sopenharmony_ci"        --buffer-size         size of buffer for frame(frame unit)\n"
901c72fcc34Sopenharmony_ci"        --waiter-type         type of waiter to handle available frames\n"
902c72fcc34Sopenharmony_ci"        --sched-model         model of process scheduling\n"
903c72fcc34Sopenharmony_ci"      [SOFTWARE FEATURES]\n"
904c72fcc34Sopenharmony_ci"        -A, --avail-min       threshold of frames to wake up process\n"
905c72fcc34Sopenharmony_ci"        -R, --start-delay     threshold of frames to start PCM substream\n"
906c72fcc34Sopenharmony_ci"        -T, --stop-delay      threshold of frames to stop PCM substream\n"
907c72fcc34Sopenharmony_ci"      [LIBASOUND PLUGIN OPTIONS]\n"
908c72fcc34Sopenharmony_ci"        --disable-resample    disable rate conversion for plug plugin\n"
909c72fcc34Sopenharmony_ci"        --disable-channels    disable channel conversion for plug plugin\n"
910c72fcc34Sopenharmony_ci"        --disable-format      disable format conversion for plug plugin\n"
911c72fcc34Sopenharmony_ci"        --disable-softvol     disable software volume for sofvol plugin\n"
912c72fcc34Sopenharmony_ci"      [DEBUG ASSISTANT]\n"
913c72fcc34Sopenharmony_ci"        --fatal-errors        finish at XRUN\n"
914c72fcc34Sopenharmony_ci"        --test-nowait         busy poll without any waiter\n"
915c72fcc34Sopenharmony_ci	);
916c72fcc34Sopenharmony_ci}
917c72fcc34Sopenharmony_ci
918c72fcc34Sopenharmony_ciconst struct xfer_data xfer_libasound = {
919c72fcc34Sopenharmony_ci	.s_opts = S_OPTS,
920c72fcc34Sopenharmony_ci	.l_opts = l_opts,
921c72fcc34Sopenharmony_ci	.l_opts_count = ARRAY_SIZE(l_opts),
922c72fcc34Sopenharmony_ci	.ops = {
923c72fcc34Sopenharmony_ci		.init		= xfer_libasound_init,
924c72fcc34Sopenharmony_ci		.parse_opt	= xfer_libasound_parse_opt,
925c72fcc34Sopenharmony_ci		.validate_opts	= xfer_libasound_validate_opts,
926c72fcc34Sopenharmony_ci		.pre_process	= xfer_libasound_pre_process,
927c72fcc34Sopenharmony_ci		.process_frames	= xfer_libasound_process_frames,
928c72fcc34Sopenharmony_ci		.pause		= xfer_libasound_pause,
929c72fcc34Sopenharmony_ci		.post_process	= xfer_libasound_post_process,
930c72fcc34Sopenharmony_ci		.destroy	= xfer_libasound_destroy,
931c72fcc34Sopenharmony_ci		.help		= xfer_libasound_help,
932c72fcc34Sopenharmony_ci	},
933c72fcc34Sopenharmony_ci	.private_size = sizeof(struct libasound_state),
934c72fcc34Sopenharmony_ci};
935