1c72fcc34Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
2c72fcc34Sopenharmony_ci//
3c72fcc34Sopenharmony_ci// subcmd-transfer.c - operations for transfer sub command.
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.h"
10c72fcc34Sopenharmony_ci#include "subcmd.h"
11c72fcc34Sopenharmony_ci#include "misc.h"
12c72fcc34Sopenharmony_ci
13c72fcc34Sopenharmony_ci#include <signal.h>
14c72fcc34Sopenharmony_ci#include <inttypes.h>
15c72fcc34Sopenharmony_ci
16c72fcc34Sopenharmony_cistruct context {
17c72fcc34Sopenharmony_ci	struct xfer_context xfer;
18c72fcc34Sopenharmony_ci	struct mapper_context mapper;
19c72fcc34Sopenharmony_ci	struct container_context *cntrs;
20c72fcc34Sopenharmony_ci	unsigned int cntr_count;
21c72fcc34Sopenharmony_ci
22c72fcc34Sopenharmony_ci	int *cntr_fds;
23c72fcc34Sopenharmony_ci
24c72fcc34Sopenharmony_ci	// NOTE: To handling Unix signal.
25c72fcc34Sopenharmony_ci	bool interrupted;
26c72fcc34Sopenharmony_ci	int signal;
27c72fcc34Sopenharmony_ci};
28c72fcc34Sopenharmony_ci
29c72fcc34Sopenharmony_ci// NOTE: To handling Unix signal.
30c72fcc34Sopenharmony_cistatic struct context *ctx_ptr;
31c72fcc34Sopenharmony_ci
32c72fcc34Sopenharmony_cistatic void handle_unix_signal_for_finish(int sig)
33c72fcc34Sopenharmony_ci{
34c72fcc34Sopenharmony_ci	unsigned int i;
35c72fcc34Sopenharmony_ci
36c72fcc34Sopenharmony_ci	for (i = 0; i < ctx_ptr->cntr_count; ++i)
37c72fcc34Sopenharmony_ci		ctx_ptr->cntrs[i].interrupted = true;
38c72fcc34Sopenharmony_ci
39c72fcc34Sopenharmony_ci	ctx_ptr->signal = sig;
40c72fcc34Sopenharmony_ci	ctx_ptr->interrupted = true;
41c72fcc34Sopenharmony_ci}
42c72fcc34Sopenharmony_ci
43c72fcc34Sopenharmony_cistatic void handle_unix_signal_for_suspend(int sig ATTRIBUTE_UNUSED)
44c72fcc34Sopenharmony_ci{
45c72fcc34Sopenharmony_ci	sigset_t curr, prev;
46c72fcc34Sopenharmony_ci	struct sigaction sa = {0};
47c72fcc34Sopenharmony_ci
48c72fcc34Sopenharmony_ci	// 1. suspend substream.
49c72fcc34Sopenharmony_ci	xfer_context_pause(&ctx_ptr->xfer, true);
50c72fcc34Sopenharmony_ci
51c72fcc34Sopenharmony_ci	// 2. Prepare for default handler(SIG_DFL) of SIGTSTP to stop this
52c72fcc34Sopenharmony_ci	// process.
53c72fcc34Sopenharmony_ci	if (sigaction(SIGTSTP, NULL, &sa) < 0) {
54c72fcc34Sopenharmony_ci		fprintf(stderr, "sigaction(2)\n");
55c72fcc34Sopenharmony_ci		exit(EXIT_FAILURE);
56c72fcc34Sopenharmony_ci	}
57c72fcc34Sopenharmony_ci	if (sa.sa_handler == SIG_ERR)
58c72fcc34Sopenharmony_ci		exit(EXIT_FAILURE);
59c72fcc34Sopenharmony_ci	if (sa.sa_handler == handle_unix_signal_for_suspend)
60c72fcc34Sopenharmony_ci		sa.sa_handler = SIG_DFL;
61c72fcc34Sopenharmony_ci	if (sigaction(SIGTSTP, &sa, NULL) < 0) {
62c72fcc34Sopenharmony_ci		fprintf(stderr, "sigaction(2)\n");
63c72fcc34Sopenharmony_ci		exit(EXIT_FAILURE);
64c72fcc34Sopenharmony_ci	}
65c72fcc34Sopenharmony_ci
66c72fcc34Sopenharmony_ci	// Queue SIGTSTP.
67c72fcc34Sopenharmony_ci	raise(SIGTSTP);
68c72fcc34Sopenharmony_ci
69c72fcc34Sopenharmony_ci	// Release the queued signal from being blocked. This causes an
70c72fcc34Sopenharmony_ci	// additional interrupt for the default handler.
71c72fcc34Sopenharmony_ci	sigemptyset(&curr);
72c72fcc34Sopenharmony_ci	sigaddset(&curr, SIGTSTP);
73c72fcc34Sopenharmony_ci	if (sigprocmask(SIG_UNBLOCK, &curr, &prev) < 0) {
74c72fcc34Sopenharmony_ci		fprintf(stderr, "sigprocmask(2)\n");
75c72fcc34Sopenharmony_ci		exit(EXIT_FAILURE);
76c72fcc34Sopenharmony_ci	}
77c72fcc34Sopenharmony_ci
78c72fcc34Sopenharmony_ci	// 3. SIGCONT is cought and rescheduled. Recover blocking status of
79c72fcc34Sopenharmony_ci	// UNIX signals.
80c72fcc34Sopenharmony_ci	if (sigprocmask(SIG_SETMASK, &prev, NULL) < 0) {
81c72fcc34Sopenharmony_ci		fprintf(stderr, "sigprocmask(2)\n");
82c72fcc34Sopenharmony_ci		exit(EXIT_FAILURE);
83c72fcc34Sopenharmony_ci	}
84c72fcc34Sopenharmony_ci
85c72fcc34Sopenharmony_ci	// Reconfigure this handler for SIGTSTP, instead of default one.
86c72fcc34Sopenharmony_ci	sigemptyset(&sa.sa_mask);
87c72fcc34Sopenharmony_ci	sa.sa_flags = SA_RESTART;
88c72fcc34Sopenharmony_ci	sa.sa_handler = handle_unix_signal_for_suspend;
89c72fcc34Sopenharmony_ci	if (sigaction(SIGTSTP, &sa, NULL) < 0) {
90c72fcc34Sopenharmony_ci		fprintf(stderr, "sigaction(2)\n");
91c72fcc34Sopenharmony_ci		exit(EXIT_FAILURE);
92c72fcc34Sopenharmony_ci	}
93c72fcc34Sopenharmony_ci
94c72fcc34Sopenharmony_ci	// 4. Continue the PCM substream.
95c72fcc34Sopenharmony_ci	xfer_context_pause(&ctx_ptr->xfer, false);
96c72fcc34Sopenharmony_ci}
97c72fcc34Sopenharmony_ci
98c72fcc34Sopenharmony_cistatic int prepare_signal_handler(struct context *ctx)
99c72fcc34Sopenharmony_ci{
100c72fcc34Sopenharmony_ci	struct sigaction sa = {0};
101c72fcc34Sopenharmony_ci
102c72fcc34Sopenharmony_ci	sigemptyset(&sa.sa_mask);
103c72fcc34Sopenharmony_ci	sa.sa_flags = 0;
104c72fcc34Sopenharmony_ci	sa.sa_handler = handle_unix_signal_for_finish;
105c72fcc34Sopenharmony_ci
106c72fcc34Sopenharmony_ci	if (sigaction(SIGINT, &sa, NULL) < 0)
107c72fcc34Sopenharmony_ci		return -errno;
108c72fcc34Sopenharmony_ci	if (sigaction(SIGTERM, &sa, NULL) < 0)
109c72fcc34Sopenharmony_ci		return -errno;
110c72fcc34Sopenharmony_ci
111c72fcc34Sopenharmony_ci	sigemptyset(&sa.sa_mask);
112c72fcc34Sopenharmony_ci	sa.sa_flags = 0;
113c72fcc34Sopenharmony_ci	sa.sa_handler = handle_unix_signal_for_suspend;
114c72fcc34Sopenharmony_ci	if (sigaction(SIGTSTP, &sa, NULL) < 0)
115c72fcc34Sopenharmony_ci		return -errno;
116c72fcc34Sopenharmony_ci
117c72fcc34Sopenharmony_ci	ctx_ptr = ctx;
118c72fcc34Sopenharmony_ci
119c72fcc34Sopenharmony_ci	return 0;
120c72fcc34Sopenharmony_ci}
121c72fcc34Sopenharmony_ci
122c72fcc34Sopenharmony_cistatic int context_init(struct context *ctx, snd_pcm_stream_t direction,
123c72fcc34Sopenharmony_ci			int argc, char *const *argv)
124c72fcc34Sopenharmony_ci{
125c72fcc34Sopenharmony_ci	const char *xfer_type_literal;
126c72fcc34Sopenharmony_ci	enum xfer_type xfer_type;
127c72fcc34Sopenharmony_ci	int i;
128c72fcc34Sopenharmony_ci
129c72fcc34Sopenharmony_ci	// Decide transfer backend before option parser runs.
130c72fcc34Sopenharmony_ci	xfer_type_literal = NULL;
131c72fcc34Sopenharmony_ci	for (i = 0; i < argc; ++i) {
132c72fcc34Sopenharmony_ci		if (strstr(argv[i], "--xfer-type") != argv[i])
133c72fcc34Sopenharmony_ci			continue;
134c72fcc34Sopenharmony_ci		xfer_type_literal = argv[i] + 12;
135c72fcc34Sopenharmony_ci	}
136c72fcc34Sopenharmony_ci	if (xfer_type_literal == NULL) {
137c72fcc34Sopenharmony_ci		xfer_type = XFER_TYPE_LIBASOUND;
138c72fcc34Sopenharmony_ci	} else {
139c72fcc34Sopenharmony_ci		xfer_type = xfer_type_from_label(xfer_type_literal);
140c72fcc34Sopenharmony_ci		if (xfer_type == XFER_TYPE_UNSUPPORTED) {
141c72fcc34Sopenharmony_ci			fprintf(stderr, "The '%s' xfer type is not supported\n",
142c72fcc34Sopenharmony_ci				xfer_type_literal);
143c72fcc34Sopenharmony_ci			return -EINVAL;
144c72fcc34Sopenharmony_ci		}
145c72fcc34Sopenharmony_ci	}
146c72fcc34Sopenharmony_ci
147c72fcc34Sopenharmony_ci	// Initialize transfer.
148c72fcc34Sopenharmony_ci	return xfer_context_init(&ctx->xfer, xfer_type, direction, argc, argv);
149c72fcc34Sopenharmony_ci}
150c72fcc34Sopenharmony_ci
151c72fcc34Sopenharmony_cistatic int allocate_containers(struct context *ctx, unsigned int count)
152c72fcc34Sopenharmony_ci{
153c72fcc34Sopenharmony_ci	ctx->cntrs = calloc(count, sizeof(*ctx->cntrs));
154c72fcc34Sopenharmony_ci	if (ctx->cntrs == NULL)
155c72fcc34Sopenharmony_ci		return -ENOMEM;
156c72fcc34Sopenharmony_ci	ctx->cntr_count = count;
157c72fcc34Sopenharmony_ci
158c72fcc34Sopenharmony_ci	ctx->cntr_fds = calloc(count, sizeof(*ctx->cntr_fds));
159c72fcc34Sopenharmony_ci	if (ctx->cntr_fds == NULL)
160c72fcc34Sopenharmony_ci		return -ENOMEM;
161c72fcc34Sopenharmony_ci
162c72fcc34Sopenharmony_ci	return 0;
163c72fcc34Sopenharmony_ci}
164c72fcc34Sopenharmony_ci
165c72fcc34Sopenharmony_cistatic int capture_pre_process(struct context *ctx, snd_pcm_access_t *access,
166c72fcc34Sopenharmony_ci			       snd_pcm_uframes_t *frames_per_buffer,
167c72fcc34Sopenharmony_ci			       uint64_t *total_frame_count)
168c72fcc34Sopenharmony_ci{
169c72fcc34Sopenharmony_ci	snd_pcm_format_t sample_format = SND_PCM_FORMAT_UNKNOWN;
170c72fcc34Sopenharmony_ci	unsigned int samples_per_frame = 0;
171c72fcc34Sopenharmony_ci	unsigned int frames_per_second = 0;
172c72fcc34Sopenharmony_ci	unsigned int channels;
173c72fcc34Sopenharmony_ci	unsigned int i;
174c72fcc34Sopenharmony_ci	int err;
175c72fcc34Sopenharmony_ci
176c72fcc34Sopenharmony_ci	err = xfer_context_pre_process(&ctx->xfer, &sample_format,
177c72fcc34Sopenharmony_ci				       &samples_per_frame, &frames_per_second,
178c72fcc34Sopenharmony_ci				       access, frames_per_buffer);
179c72fcc34Sopenharmony_ci	if (err < 0)
180c72fcc34Sopenharmony_ci		return err;
181c72fcc34Sopenharmony_ci
182c72fcc34Sopenharmony_ci	// Prepare for containers.
183c72fcc34Sopenharmony_ci	err = allocate_containers(ctx, ctx->xfer.path_count);
184c72fcc34Sopenharmony_ci	if (err < 0)
185c72fcc34Sopenharmony_ci		return err;
186c72fcc34Sopenharmony_ci
187c72fcc34Sopenharmony_ci	if (ctx->cntr_count > 1)
188c72fcc34Sopenharmony_ci		channels = 1;
189c72fcc34Sopenharmony_ci	else
190c72fcc34Sopenharmony_ci		channels = samples_per_frame;
191c72fcc34Sopenharmony_ci
192c72fcc34Sopenharmony_ci	*total_frame_count = 0;
193c72fcc34Sopenharmony_ci	for (i = 0; i < ctx->cntr_count; ++i) {
194c72fcc34Sopenharmony_ci		const char *path = ctx->xfer.paths[i];
195c72fcc34Sopenharmony_ci		int fd;
196c72fcc34Sopenharmony_ci		uint64_t frame_count;
197c72fcc34Sopenharmony_ci
198c72fcc34Sopenharmony_ci		if (!strcmp(path, "-")) {
199c72fcc34Sopenharmony_ci			fd = fileno(stdout);
200c72fcc34Sopenharmony_ci		} else {
201c72fcc34Sopenharmony_ci			fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
202c72fcc34Sopenharmony_ci			if (fd < 0)
203c72fcc34Sopenharmony_ci				return -errno;
204c72fcc34Sopenharmony_ci		}
205c72fcc34Sopenharmony_ci		ctx->cntr_fds[i] = fd;
206c72fcc34Sopenharmony_ci
207c72fcc34Sopenharmony_ci		err = container_builder_init(ctx->cntrs + i, ctx->cntr_fds[i],
208c72fcc34Sopenharmony_ci					     ctx->xfer.cntr_format,
209c72fcc34Sopenharmony_ci					     ctx->xfer.verbose > 1);
210c72fcc34Sopenharmony_ci		if (err < 0)
211c72fcc34Sopenharmony_ci			return err;
212c72fcc34Sopenharmony_ci
213c72fcc34Sopenharmony_ci		err = container_context_pre_process(ctx->cntrs + i,
214c72fcc34Sopenharmony_ci						    &sample_format, &channels,
215c72fcc34Sopenharmony_ci						    &frames_per_second,
216c72fcc34Sopenharmony_ci						    &frame_count);
217c72fcc34Sopenharmony_ci		if (err < 0)
218c72fcc34Sopenharmony_ci			return err;
219c72fcc34Sopenharmony_ci
220c72fcc34Sopenharmony_ci		if (*total_frame_count == 0)
221c72fcc34Sopenharmony_ci			*total_frame_count = frame_count;
222c72fcc34Sopenharmony_ci		if (frame_count < *total_frame_count)
223c72fcc34Sopenharmony_ci			*total_frame_count = frame_count;
224c72fcc34Sopenharmony_ci	}
225c72fcc34Sopenharmony_ci
226c72fcc34Sopenharmony_ci	return 0;
227c72fcc34Sopenharmony_ci}
228c72fcc34Sopenharmony_ci
229c72fcc34Sopenharmony_cistatic int playback_pre_process(struct context *ctx, snd_pcm_access_t *access,
230c72fcc34Sopenharmony_ci				snd_pcm_uframes_t *frames_per_buffer,
231c72fcc34Sopenharmony_ci				uint64_t *total_frame_count)
232c72fcc34Sopenharmony_ci{
233c72fcc34Sopenharmony_ci	snd_pcm_format_t sample_format = SND_PCM_FORMAT_UNKNOWN;
234c72fcc34Sopenharmony_ci	unsigned int samples_per_frame = 0;
235c72fcc34Sopenharmony_ci	unsigned int frames_per_second = 0;
236c72fcc34Sopenharmony_ci	unsigned int i;
237c72fcc34Sopenharmony_ci	int err;
238c72fcc34Sopenharmony_ci
239c72fcc34Sopenharmony_ci	// Prepare for containers.
240c72fcc34Sopenharmony_ci	err = allocate_containers(ctx, ctx->xfer.path_count);
241c72fcc34Sopenharmony_ci	if (err < 0)
242c72fcc34Sopenharmony_ci		return err;
243c72fcc34Sopenharmony_ci
244c72fcc34Sopenharmony_ci	for (i = 0; i < ctx->cntr_count; ++i) {
245c72fcc34Sopenharmony_ci		const char *path = ctx->xfer.paths[i];
246c72fcc34Sopenharmony_ci		int fd;
247c72fcc34Sopenharmony_ci		snd_pcm_format_t format;
248c72fcc34Sopenharmony_ci		unsigned int channels;
249c72fcc34Sopenharmony_ci		unsigned int rate;
250c72fcc34Sopenharmony_ci		uint64_t frame_count;
251c72fcc34Sopenharmony_ci
252c72fcc34Sopenharmony_ci		if (!strcmp(path, "-")) {
253c72fcc34Sopenharmony_ci			fd = fileno(stdin);
254c72fcc34Sopenharmony_ci		} else {
255c72fcc34Sopenharmony_ci			fd = open(path, O_RDONLY);
256c72fcc34Sopenharmony_ci			if (fd < 0)
257c72fcc34Sopenharmony_ci				return -errno;
258c72fcc34Sopenharmony_ci		}
259c72fcc34Sopenharmony_ci		ctx->cntr_fds[i] = fd;
260c72fcc34Sopenharmony_ci
261c72fcc34Sopenharmony_ci		err = container_parser_init(ctx->cntrs + i, ctx->cntr_fds[i],
262c72fcc34Sopenharmony_ci					    ctx->xfer.verbose > 1);
263c72fcc34Sopenharmony_ci		if (err < 0)
264c72fcc34Sopenharmony_ci			return err;
265c72fcc34Sopenharmony_ci
266c72fcc34Sopenharmony_ci		if (i == 0) {
267c72fcc34Sopenharmony_ci			// For a raw container.
268c72fcc34Sopenharmony_ci			format = ctx->xfer.sample_format;
269c72fcc34Sopenharmony_ci			channels = ctx->xfer.samples_per_frame;
270c72fcc34Sopenharmony_ci			rate = ctx->xfer.frames_per_second;
271c72fcc34Sopenharmony_ci		} else {
272c72fcc34Sopenharmony_ci			format = sample_format;
273c72fcc34Sopenharmony_ci			channels = samples_per_frame;
274c72fcc34Sopenharmony_ci			rate = frames_per_second;
275c72fcc34Sopenharmony_ci		}
276c72fcc34Sopenharmony_ci
277c72fcc34Sopenharmony_ci		err = container_context_pre_process(ctx->cntrs + i, &format,
278c72fcc34Sopenharmony_ci						    &channels, &rate,
279c72fcc34Sopenharmony_ci						    &frame_count);
280c72fcc34Sopenharmony_ci		if (err < 0)
281c72fcc34Sopenharmony_ci			return err;
282c72fcc34Sopenharmony_ci
283c72fcc34Sopenharmony_ci		if (format == SND_PCM_FORMAT_UNKNOWN || channels == 0 ||
284c72fcc34Sopenharmony_ci		    rate == 0) {
285c72fcc34Sopenharmony_ci			fprintf(stderr,
286c72fcc34Sopenharmony_ci				"Sample format, channels and rate should be "
287c72fcc34Sopenharmony_ci				"indicated for given files.\n");
288c72fcc34Sopenharmony_ci			return -EINVAL;
289c72fcc34Sopenharmony_ci		}
290c72fcc34Sopenharmony_ci
291c72fcc34Sopenharmony_ci		if (i == 0) {
292c72fcc34Sopenharmony_ci			sample_format = format;
293c72fcc34Sopenharmony_ci			samples_per_frame = channels;
294c72fcc34Sopenharmony_ci			frames_per_second = rate;
295c72fcc34Sopenharmony_ci			*total_frame_count = frame_count;
296c72fcc34Sopenharmony_ci		} else {
297c72fcc34Sopenharmony_ci			if (format != sample_format) {
298c72fcc34Sopenharmony_ci				fprintf(stderr,
299c72fcc34Sopenharmony_ci					"When using several files, they "
300c72fcc34Sopenharmony_ci					"should include the same sample "
301c72fcc34Sopenharmony_ci					"format.\n");
302c72fcc34Sopenharmony_ci				return -EINVAL;
303c72fcc34Sopenharmony_ci			}
304c72fcc34Sopenharmony_ci
305c72fcc34Sopenharmony_ci			// No need to check channels to handle multiple
306c72fcc34Sopenharmony_ci			// containers.
307c72fcc34Sopenharmony_ci			if (rate != frames_per_second) {
308c72fcc34Sopenharmony_ci				fprintf(stderr,
309c72fcc34Sopenharmony_ci					"When using several files, they "
310c72fcc34Sopenharmony_ci					"should include samples at the same "
311c72fcc34Sopenharmony_ci					"sampling rate.\n");
312c72fcc34Sopenharmony_ci				return -EINVAL;
313c72fcc34Sopenharmony_ci			}
314c72fcc34Sopenharmony_ci			if (frame_count < *total_frame_count)
315c72fcc34Sopenharmony_ci				*total_frame_count = frame_count;
316c72fcc34Sopenharmony_ci		}
317c72fcc34Sopenharmony_ci	}
318c72fcc34Sopenharmony_ci
319c72fcc34Sopenharmony_ci	if (ctx->cntr_count > 1)
320c72fcc34Sopenharmony_ci		samples_per_frame = ctx->cntr_count;
321c72fcc34Sopenharmony_ci
322c72fcc34Sopenharmony_ci	// Configure hardware with these parameters.
323c72fcc34Sopenharmony_ci	return xfer_context_pre_process(&ctx->xfer, &sample_format,
324c72fcc34Sopenharmony_ci					&samples_per_frame, &frames_per_second,
325c72fcc34Sopenharmony_ci					access, frames_per_buffer);
326c72fcc34Sopenharmony_ci}
327c72fcc34Sopenharmony_ci
328c72fcc34Sopenharmony_cistatic int context_pre_process(struct context *ctx, snd_pcm_stream_t direction,
329c72fcc34Sopenharmony_ci			       uint64_t *total_frame_count)
330c72fcc34Sopenharmony_ci{
331c72fcc34Sopenharmony_ci	snd_pcm_access_t access;
332c72fcc34Sopenharmony_ci	snd_pcm_uframes_t frames_per_buffer = 0;
333c72fcc34Sopenharmony_ci	unsigned int bytes_per_sample = 0;
334c72fcc34Sopenharmony_ci	enum mapper_type mapper_type;
335c72fcc34Sopenharmony_ci	int err;
336c72fcc34Sopenharmony_ci
337c72fcc34Sopenharmony_ci	if (direction == SND_PCM_STREAM_CAPTURE) {
338c72fcc34Sopenharmony_ci		mapper_type = MAPPER_TYPE_DEMUXER;
339c72fcc34Sopenharmony_ci		err = capture_pre_process(ctx, &access, &frames_per_buffer,
340c72fcc34Sopenharmony_ci					  total_frame_count);
341c72fcc34Sopenharmony_ci	} else {
342c72fcc34Sopenharmony_ci		mapper_type = MAPPER_TYPE_MUXER;
343c72fcc34Sopenharmony_ci		err = playback_pre_process(ctx, &access, &frames_per_buffer,
344c72fcc34Sopenharmony_ci					   total_frame_count);
345c72fcc34Sopenharmony_ci	}
346c72fcc34Sopenharmony_ci	if (err < 0)
347c72fcc34Sopenharmony_ci		return err;
348c72fcc34Sopenharmony_ci
349c72fcc34Sopenharmony_ci	// Prepare for mapper.
350c72fcc34Sopenharmony_ci	err = mapper_context_init(&ctx->mapper, mapper_type, ctx->cntr_count,
351c72fcc34Sopenharmony_ci				  ctx->xfer.verbose > 1);
352c72fcc34Sopenharmony_ci	if (err < 0)
353c72fcc34Sopenharmony_ci		return err;
354c72fcc34Sopenharmony_ci
355c72fcc34Sopenharmony_ci	bytes_per_sample =
356c72fcc34Sopenharmony_ci		snd_pcm_format_physical_width(ctx->xfer.sample_format) / 8;
357c72fcc34Sopenharmony_ci	if (bytes_per_sample <= 0)
358c72fcc34Sopenharmony_ci		return -ENXIO;
359c72fcc34Sopenharmony_ci	err = mapper_context_pre_process(&ctx->mapper, access, bytes_per_sample,
360c72fcc34Sopenharmony_ci					 ctx->xfer.samples_per_frame,
361c72fcc34Sopenharmony_ci					 frames_per_buffer, ctx->cntrs);
362c72fcc34Sopenharmony_ci	if (err < 0)
363c72fcc34Sopenharmony_ci		return err;
364c72fcc34Sopenharmony_ci
365c72fcc34Sopenharmony_ci	xfer_options_calculate_duration(&ctx->xfer, total_frame_count);
366c72fcc34Sopenharmony_ci
367c72fcc34Sopenharmony_ci	return 0;
368c72fcc34Sopenharmony_ci}
369c72fcc34Sopenharmony_ci
370c72fcc34Sopenharmony_cistatic int context_process_frames(struct context *ctx,
371c72fcc34Sopenharmony_ci				  snd_pcm_stream_t direction,
372c72fcc34Sopenharmony_ci				  uint64_t expected_frame_count,
373c72fcc34Sopenharmony_ci				  uint64_t *actual_frame_count)
374c72fcc34Sopenharmony_ci{
375c72fcc34Sopenharmony_ci	bool verbose = ctx->xfer.verbose > 2;
376c72fcc34Sopenharmony_ci	unsigned int frame_count;
377c72fcc34Sopenharmony_ci	unsigned int i;
378c72fcc34Sopenharmony_ci	int err = 0;
379c72fcc34Sopenharmony_ci
380c72fcc34Sopenharmony_ci	if (!ctx->xfer.quiet) {
381c72fcc34Sopenharmony_ci		fprintf(stderr,
382c72fcc34Sopenharmony_ci			"%s: Format '%s', Rate %u Hz, Channels ",
383c72fcc34Sopenharmony_ci			snd_pcm_stream_name(direction),
384c72fcc34Sopenharmony_ci			snd_pcm_format_description(ctx->xfer.sample_format),
385c72fcc34Sopenharmony_ci			ctx->xfer.frames_per_second);
386c72fcc34Sopenharmony_ci		if (ctx->xfer.samples_per_frame == 1)
387c72fcc34Sopenharmony_ci			fprintf(stderr, "'monaural'");
388c72fcc34Sopenharmony_ci		else if (ctx->xfer.samples_per_frame == 2)
389c72fcc34Sopenharmony_ci			fprintf(stderr, "'Stereo'");
390c72fcc34Sopenharmony_ci		else
391c72fcc34Sopenharmony_ci			fprintf(stderr, "%u", ctx->xfer.samples_per_frame);
392c72fcc34Sopenharmony_ci		fprintf(stderr, "\n");
393c72fcc34Sopenharmony_ci	}
394c72fcc34Sopenharmony_ci
395c72fcc34Sopenharmony_ci	*actual_frame_count = 0;
396c72fcc34Sopenharmony_ci	while (!ctx->interrupted) {
397c72fcc34Sopenharmony_ci		struct container_context *cntr;
398c72fcc34Sopenharmony_ci
399c72fcc34Sopenharmony_ci		// Tell remains to expected frame count.
400c72fcc34Sopenharmony_ci		frame_count = expected_frame_count - *actual_frame_count;
401c72fcc34Sopenharmony_ci		err = xfer_context_process_frames(&ctx->xfer, &ctx->mapper,
402c72fcc34Sopenharmony_ci						  ctx->cntrs, &frame_count);
403c72fcc34Sopenharmony_ci		if (err < 0) {
404c72fcc34Sopenharmony_ci			if (err == -EAGAIN || err == -EINTR)
405c72fcc34Sopenharmony_ci				continue;
406c72fcc34Sopenharmony_ci			break;
407c72fcc34Sopenharmony_ci		}
408c72fcc34Sopenharmony_ci		if (verbose) {
409c72fcc34Sopenharmony_ci			fprintf(stderr,
410c72fcc34Sopenharmony_ci				"  handled: %u\n", frame_count);
411c72fcc34Sopenharmony_ci		}
412c72fcc34Sopenharmony_ci		for (i = 0; i < ctx->cntr_count; ++i) {
413c72fcc34Sopenharmony_ci			cntr = &ctx->cntrs[i];
414c72fcc34Sopenharmony_ci			if (cntr->eof)
415c72fcc34Sopenharmony_ci				break;
416c72fcc34Sopenharmony_ci		}
417c72fcc34Sopenharmony_ci		if (i < ctx->cntr_count)
418c72fcc34Sopenharmony_ci			break;
419c72fcc34Sopenharmony_ci
420c72fcc34Sopenharmony_ci		*actual_frame_count += frame_count;
421c72fcc34Sopenharmony_ci		if (*actual_frame_count >= expected_frame_count)
422c72fcc34Sopenharmony_ci			break;
423c72fcc34Sopenharmony_ci	}
424c72fcc34Sopenharmony_ci
425c72fcc34Sopenharmony_ci	if (!ctx->xfer.quiet) {
426c72fcc34Sopenharmony_ci		fprintf(stderr,
427c72fcc34Sopenharmony_ci			"%s: Expected %" PRIu64 "frames, "
428c72fcc34Sopenharmony_ci			"Actual %" PRIu64 "frames\n",
429c72fcc34Sopenharmony_ci			snd_pcm_stream_name(direction), expected_frame_count,
430c72fcc34Sopenharmony_ci			*actual_frame_count);
431c72fcc34Sopenharmony_ci		if (ctx->interrupted) {
432c72fcc34Sopenharmony_ci			fprintf(stderr, "Aborted by signal: %s\n",
433c72fcc34Sopenharmony_ci			       strsignal(ctx->signal));
434c72fcc34Sopenharmony_ci			return 0;
435c72fcc34Sopenharmony_ci		}
436c72fcc34Sopenharmony_ci	}
437c72fcc34Sopenharmony_ci
438c72fcc34Sopenharmony_ci	return err;
439c72fcc34Sopenharmony_ci}
440c72fcc34Sopenharmony_ci
441c72fcc34Sopenharmony_cistatic void context_post_process(struct context *ctx,
442c72fcc34Sopenharmony_ci				 uint64_t accumulated_frame_count ATTRIBUTE_UNUSED)
443c72fcc34Sopenharmony_ci{
444c72fcc34Sopenharmony_ci	uint64_t total_frame_count;
445c72fcc34Sopenharmony_ci	unsigned int i;
446c72fcc34Sopenharmony_ci
447c72fcc34Sopenharmony_ci	xfer_context_post_process(&ctx->xfer);
448c72fcc34Sopenharmony_ci
449c72fcc34Sopenharmony_ci	if (ctx->cntrs) {
450c72fcc34Sopenharmony_ci		for (i = 0; i < ctx->cntr_count; ++i) {
451c72fcc34Sopenharmony_ci			container_context_post_process(ctx->cntrs + i,
452c72fcc34Sopenharmony_ci						       &total_frame_count);
453c72fcc34Sopenharmony_ci			container_context_destroy(ctx->cntrs + i);
454c72fcc34Sopenharmony_ci		}
455c72fcc34Sopenharmony_ci		free(ctx->cntrs);
456c72fcc34Sopenharmony_ci	}
457c72fcc34Sopenharmony_ci
458c72fcc34Sopenharmony_ci	if (ctx->cntr_fds) {
459c72fcc34Sopenharmony_ci		for (i = 0; i < ctx->cntr_count; ++i)
460c72fcc34Sopenharmony_ci			close(ctx->cntr_fds[i]);
461c72fcc34Sopenharmony_ci		free(ctx->cntr_fds);
462c72fcc34Sopenharmony_ci	}
463c72fcc34Sopenharmony_ci
464c72fcc34Sopenharmony_ci	mapper_context_post_process(&ctx->mapper);
465c72fcc34Sopenharmony_ci	mapper_context_destroy(&ctx->mapper);
466c72fcc34Sopenharmony_ci}
467c72fcc34Sopenharmony_ci
468c72fcc34Sopenharmony_cistatic void context_destroy(struct context *ctx)
469c72fcc34Sopenharmony_ci{
470c72fcc34Sopenharmony_ci	xfer_context_destroy(&ctx->xfer);
471c72fcc34Sopenharmony_ci}
472c72fcc34Sopenharmony_ci
473c72fcc34Sopenharmony_ciint subcmd_transfer(int argc, char *const *argv, snd_pcm_stream_t direction)
474c72fcc34Sopenharmony_ci{
475c72fcc34Sopenharmony_ci	static struct context ctx = {0};
476c72fcc34Sopenharmony_ci	uint64_t expected_frame_count = 0;
477c72fcc34Sopenharmony_ci	uint64_t actual_frame_count = 0;
478c72fcc34Sopenharmony_ci	int err = 0;
479c72fcc34Sopenharmony_ci
480c72fcc34Sopenharmony_ci	err = prepare_signal_handler(&ctx);
481c72fcc34Sopenharmony_ci	if (err < 0)
482c72fcc34Sopenharmony_ci		return err;
483c72fcc34Sopenharmony_ci
484c72fcc34Sopenharmony_ci	err = context_init(&ctx, direction, argc, argv);
485c72fcc34Sopenharmony_ci	if (err < 0)
486c72fcc34Sopenharmony_ci		goto end;
487c72fcc34Sopenharmony_ci	if (ctx.xfer.help || ctx.xfer.dump_hw_params)
488c72fcc34Sopenharmony_ci		goto end;
489c72fcc34Sopenharmony_ci
490c72fcc34Sopenharmony_ci	err = context_pre_process(&ctx, direction, &expected_frame_count);
491c72fcc34Sopenharmony_ci	if (err < 0)
492c72fcc34Sopenharmony_ci		goto end;
493c72fcc34Sopenharmony_ci
494c72fcc34Sopenharmony_ci	err = context_process_frames(&ctx, direction, expected_frame_count,
495c72fcc34Sopenharmony_ci				     &actual_frame_count);
496c72fcc34Sopenharmony_ciend:
497c72fcc34Sopenharmony_ci	context_post_process(&ctx, actual_frame_count);
498c72fcc34Sopenharmony_ci
499c72fcc34Sopenharmony_ci	context_destroy(&ctx);
500c72fcc34Sopenharmony_ci
501c72fcc34Sopenharmony_ci	return err;
502c72fcc34Sopenharmony_ci}
503