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