1c72fcc34Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 2c72fcc34Sopenharmony_ci// 3c72fcc34Sopenharmony_ci// xfer-options.c - a parser of commandline options for xfer. 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 "misc.h" 11c72fcc34Sopenharmony_ci 12c72fcc34Sopenharmony_ci#include <getopt.h> 13c72fcc34Sopenharmony_ci#include <math.h> 14c72fcc34Sopenharmony_ci#include <limits.h> 15c72fcc34Sopenharmony_ci 16c72fcc34Sopenharmony_cienum no_short_opts { 17c72fcc34Sopenharmony_ci // 128 or later belong to non us-ascii character set. 18c72fcc34Sopenharmony_ci OPT_XFER_TYPE = 128, 19c72fcc34Sopenharmony_ci OPT_DUMP_HW_PARAMS, 20c72fcc34Sopenharmony_ci OPT_PERIOD_SIZE, 21c72fcc34Sopenharmony_ci OPT_BUFFER_SIZE, 22c72fcc34Sopenharmony_ci // Obsoleted. 23c72fcc34Sopenharmony_ci OPT_MAX_FILE_TIME, 24c72fcc34Sopenharmony_ci OPT_USE_STRFTIME, 25c72fcc34Sopenharmony_ci OPT_PROCESS_ID_FILE, 26c72fcc34Sopenharmony_ci}; 27c72fcc34Sopenharmony_ci 28c72fcc34Sopenharmony_cistatic void print_help() 29c72fcc34Sopenharmony_ci{ 30c72fcc34Sopenharmony_ci printf( 31c72fcc34Sopenharmony_ci"Usage:\n" 32c72fcc34Sopenharmony_ci" axfer transfer DIRECTION [ COMMON-OPTIONS ] [ BACKEND-OPTIONS ]\n" 33c72fcc34Sopenharmony_ci"\n" 34c72fcc34Sopenharmony_ci" where:\n" 35c72fcc34Sopenharmony_ci" DIRECTION = capture | playback\n" 36c72fcc34Sopenharmony_ci" COMMON-OPTIONS =\n" 37c72fcc34Sopenharmony_ci" -h, --help help\n" 38c72fcc34Sopenharmony_ci" -v, --verbose verbose\n" 39c72fcc34Sopenharmony_ci" -q, --quiet quiet mode\n" 40c72fcc34Sopenharmony_ci" -d, --duration=# interrupt after # seconds\n" 41c72fcc34Sopenharmony_ci" -s, --samples=# interrupt after # frames\n" 42c72fcc34Sopenharmony_ci" -f, --format=FORMAT sample format (case-insensitive)\n" 43c72fcc34Sopenharmony_ci" -c, --channels=# channels\n" 44c72fcc34Sopenharmony_ci" -r, --rate=# numeric sample rate in unit of Hz or kHz\n" 45c72fcc34Sopenharmony_ci" -t, --file-type=TYPE file type (wav, au, sparc, voc or raw, case-insentive)\n" 46c72fcc34Sopenharmony_ci" -I, --separate-channels one file for each channel\n" 47c72fcc34Sopenharmony_ci" --dump-hw-params dump hw_params of the device\n" 48c72fcc34Sopenharmony_ci" --xfer-type=BACKEND backend type (libasound, libffado)\n" 49c72fcc34Sopenharmony_ci ); 50c72fcc34Sopenharmony_ci} 51c72fcc34Sopenharmony_ci 52c72fcc34Sopenharmony_cistatic int allocate_paths(struct xfer_context *xfer, char *const *paths, 53c72fcc34Sopenharmony_ci unsigned int count) 54c72fcc34Sopenharmony_ci{ 55c72fcc34Sopenharmony_ci bool stdio = false; 56c72fcc34Sopenharmony_ci unsigned int i; 57c72fcc34Sopenharmony_ci 58c72fcc34Sopenharmony_ci if (count == 0) { 59c72fcc34Sopenharmony_ci stdio = true; 60c72fcc34Sopenharmony_ci count = 1; 61c72fcc34Sopenharmony_ci } 62c72fcc34Sopenharmony_ci 63c72fcc34Sopenharmony_ci xfer->paths = calloc(count, sizeof(xfer->paths[0])); 64c72fcc34Sopenharmony_ci if (xfer->paths == NULL) 65c72fcc34Sopenharmony_ci return -ENOMEM; 66c72fcc34Sopenharmony_ci xfer->path_count = count; 67c72fcc34Sopenharmony_ci 68c72fcc34Sopenharmony_ci if (stdio) { 69c72fcc34Sopenharmony_ci xfer->paths[0] = strdup("-"); 70c72fcc34Sopenharmony_ci if (xfer->paths[0] == NULL) 71c72fcc34Sopenharmony_ci return -ENOMEM; 72c72fcc34Sopenharmony_ci } else { 73c72fcc34Sopenharmony_ci for (i = 0; i < count; ++i) { 74c72fcc34Sopenharmony_ci xfer->paths[i] = strndup(paths[i], PATH_MAX); 75c72fcc34Sopenharmony_ci if (xfer->paths[i] == NULL) 76c72fcc34Sopenharmony_ci return -ENOMEM; 77c72fcc34Sopenharmony_ci } 78c72fcc34Sopenharmony_ci } 79c72fcc34Sopenharmony_ci 80c72fcc34Sopenharmony_ci return 0; 81c72fcc34Sopenharmony_ci} 82c72fcc34Sopenharmony_ci 83c72fcc34Sopenharmony_cistatic int verify_cntr_format(struct xfer_context *xfer) 84c72fcc34Sopenharmony_ci{ 85c72fcc34Sopenharmony_ci static const struct { 86c72fcc34Sopenharmony_ci const char *const literal; 87c72fcc34Sopenharmony_ci enum container_format cntr_format; 88c72fcc34Sopenharmony_ci } *entry, entries[] = { 89c72fcc34Sopenharmony_ci {"raw", CONTAINER_FORMAT_RAW}, 90c72fcc34Sopenharmony_ci {"voc", CONTAINER_FORMAT_VOC}, 91c72fcc34Sopenharmony_ci {"wav", CONTAINER_FORMAT_RIFF_WAVE}, 92c72fcc34Sopenharmony_ci {"au", CONTAINER_FORMAT_AU}, 93c72fcc34Sopenharmony_ci {"sparc", CONTAINER_FORMAT_AU}, 94c72fcc34Sopenharmony_ci }; 95c72fcc34Sopenharmony_ci int i; 96c72fcc34Sopenharmony_ci 97c72fcc34Sopenharmony_ci for (i = 0; i < (int)ARRAY_SIZE(entries); ++i) { 98c72fcc34Sopenharmony_ci entry = &entries[i]; 99c72fcc34Sopenharmony_ci if (strcasecmp(xfer->cntr_format_literal, entry->literal)) 100c72fcc34Sopenharmony_ci continue; 101c72fcc34Sopenharmony_ci 102c72fcc34Sopenharmony_ci xfer->cntr_format = entry->cntr_format; 103c72fcc34Sopenharmony_ci return 0; 104c72fcc34Sopenharmony_ci } 105c72fcc34Sopenharmony_ci 106c72fcc34Sopenharmony_ci fprintf(stderr, "unrecognized file format '%s'\n", 107c72fcc34Sopenharmony_ci xfer->cntr_format_literal); 108c72fcc34Sopenharmony_ci 109c72fcc34Sopenharmony_ci return -EINVAL; 110c72fcc34Sopenharmony_ci} 111c72fcc34Sopenharmony_ci 112c72fcc34Sopenharmony_ci// This should be called after 'verify_cntr_format()'. 113c72fcc34Sopenharmony_cistatic int verify_sample_format(struct xfer_context *xfer) 114c72fcc34Sopenharmony_ci{ 115c72fcc34Sopenharmony_ci static const struct { 116c72fcc34Sopenharmony_ci const char *const literal; 117c72fcc34Sopenharmony_ci unsigned int frames_per_second; 118c72fcc34Sopenharmony_ci unsigned int samples_per_frame; 119c72fcc34Sopenharmony_ci snd_pcm_format_t le_format; 120c72fcc34Sopenharmony_ci snd_pcm_format_t be_format; 121c72fcc34Sopenharmony_ci } *entry, entries[] = { 122c72fcc34Sopenharmony_ci {"cd", 44100, 2, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE}, 123c72fcc34Sopenharmony_ci {"cdr", 44100, 2, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE}, 124c72fcc34Sopenharmony_ci {"dat", 48000, 2, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE}, 125c72fcc34Sopenharmony_ci }; 126c72fcc34Sopenharmony_ci unsigned int i; 127c72fcc34Sopenharmony_ci 128c72fcc34Sopenharmony_ci xfer->sample_format = snd_pcm_format_value(xfer->sample_format_literal); 129c72fcc34Sopenharmony_ci if (xfer->sample_format != SND_PCM_FORMAT_UNKNOWN) 130c72fcc34Sopenharmony_ci return 0; 131c72fcc34Sopenharmony_ci 132c72fcc34Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(entries); ++i) { 133c72fcc34Sopenharmony_ci entry = &entries[i]; 134c72fcc34Sopenharmony_ci if (strcmp(entry->literal, xfer->sample_format_literal)) 135c72fcc34Sopenharmony_ci continue; 136c72fcc34Sopenharmony_ci 137c72fcc34Sopenharmony_ci if (xfer->frames_per_second > 0 && 138c72fcc34Sopenharmony_ci xfer->frames_per_second != entry->frames_per_second) { 139c72fcc34Sopenharmony_ci fprintf(stderr, 140c72fcc34Sopenharmony_ci "'%s' format can't be used with rate except " 141c72fcc34Sopenharmony_ci "for %u.\n", 142c72fcc34Sopenharmony_ci entry->literal, entry->frames_per_second); 143c72fcc34Sopenharmony_ci return -EINVAL; 144c72fcc34Sopenharmony_ci } 145c72fcc34Sopenharmony_ci 146c72fcc34Sopenharmony_ci if (xfer->samples_per_frame > 0 && 147c72fcc34Sopenharmony_ci xfer->samples_per_frame != entry->samples_per_frame) { 148c72fcc34Sopenharmony_ci fprintf(stderr, 149c72fcc34Sopenharmony_ci "'%s' format can't be used with channel except " 150c72fcc34Sopenharmony_ci "for %u.\n", 151c72fcc34Sopenharmony_ci entry->literal, entry->samples_per_frame); 152c72fcc34Sopenharmony_ci return -EINVAL; 153c72fcc34Sopenharmony_ci } 154c72fcc34Sopenharmony_ci 155c72fcc34Sopenharmony_ci xfer->frames_per_second = entry->frames_per_second; 156c72fcc34Sopenharmony_ci xfer->samples_per_frame = entry->samples_per_frame; 157c72fcc34Sopenharmony_ci if (xfer->cntr_format == CONTAINER_FORMAT_AU) 158c72fcc34Sopenharmony_ci xfer->sample_format = entry->be_format; 159c72fcc34Sopenharmony_ci else 160c72fcc34Sopenharmony_ci xfer->sample_format = entry->le_format; 161c72fcc34Sopenharmony_ci 162c72fcc34Sopenharmony_ci return 0; 163c72fcc34Sopenharmony_ci } 164c72fcc34Sopenharmony_ci 165c72fcc34Sopenharmony_ci fprintf(stderr, "wrong extended format '%s'\n", 166c72fcc34Sopenharmony_ci xfer->sample_format_literal); 167c72fcc34Sopenharmony_ci 168c72fcc34Sopenharmony_ci return -EINVAL; 169c72fcc34Sopenharmony_ci} 170c72fcc34Sopenharmony_ci 171c72fcc34Sopenharmony_cistatic int validate_options(struct xfer_context *xfer) 172c72fcc34Sopenharmony_ci{ 173c72fcc34Sopenharmony_ci unsigned int val; 174c72fcc34Sopenharmony_ci int err = 0; 175c72fcc34Sopenharmony_ci 176c72fcc34Sopenharmony_ci if (xfer->cntr_format_literal == NULL) { 177c72fcc34Sopenharmony_ci if (xfer->direction == SND_PCM_STREAM_CAPTURE) { 178c72fcc34Sopenharmony_ci // To stdout. 179c72fcc34Sopenharmony_ci if (xfer->path_count == 1 && 180c72fcc34Sopenharmony_ci !strcmp(xfer->paths[0], "-")) { 181c72fcc34Sopenharmony_ci xfer->cntr_format = CONTAINER_FORMAT_RAW; 182c72fcc34Sopenharmony_ci } else { 183c72fcc34Sopenharmony_ci // Use first path as a representative. 184c72fcc34Sopenharmony_ci xfer->cntr_format = container_format_from_path( 185c72fcc34Sopenharmony_ci xfer->paths[0]); 186c72fcc34Sopenharmony_ci } 187c72fcc34Sopenharmony_ci } 188c72fcc34Sopenharmony_ci // For playback, perform auto-detection. 189c72fcc34Sopenharmony_ci } else { 190c72fcc34Sopenharmony_ci err = verify_cntr_format(xfer); 191c72fcc34Sopenharmony_ci } 192c72fcc34Sopenharmony_ci if (err < 0) 193c72fcc34Sopenharmony_ci return err; 194c72fcc34Sopenharmony_ci 195c72fcc34Sopenharmony_ci if (xfer->multiple_cntrs) { 196c72fcc34Sopenharmony_ci if (!strcmp(xfer->paths[0], "-")) { 197c72fcc34Sopenharmony_ci fprintf(stderr, 198c72fcc34Sopenharmony_ci "An option for separated channels is not " 199c72fcc34Sopenharmony_ci "available with stdin/stdout.\n"); 200c72fcc34Sopenharmony_ci return -EINVAL; 201c72fcc34Sopenharmony_ci } 202c72fcc34Sopenharmony_ci 203c72fcc34Sopenharmony_ci // For captured PCM frames, even if one path is given for 204c72fcc34Sopenharmony_ci // container files, it can be used to generate several paths. 205c72fcc34Sopenharmony_ci // For this purpose, please see 206c72fcc34Sopenharmony_ci // 'xfer_options_fixup_paths()'. 207c72fcc34Sopenharmony_ci if (xfer->direction == SND_PCM_STREAM_PLAYBACK) { 208c72fcc34Sopenharmony_ci // Require several paths for containers. 209c72fcc34Sopenharmony_ci if (xfer->path_count == 1) { 210c72fcc34Sopenharmony_ci fprintf(stderr, 211c72fcc34Sopenharmony_ci "An option for separated channels " 212c72fcc34Sopenharmony_ci "requires several files to playback " 213c72fcc34Sopenharmony_ci "PCM frames.\n"); 214c72fcc34Sopenharmony_ci return -EINVAL; 215c72fcc34Sopenharmony_ci } 216c72fcc34Sopenharmony_ci } 217c72fcc34Sopenharmony_ci } else { 218c72fcc34Sopenharmony_ci // A single path is available only. 219c72fcc34Sopenharmony_ci if (xfer->path_count > 1) { 220c72fcc34Sopenharmony_ci fprintf(stderr, 221c72fcc34Sopenharmony_ci "When using several files, an option for " 222c72fcc34Sopenharmony_ci "sepatated channels is used with.\n"); 223c72fcc34Sopenharmony_ci return -EINVAL; 224c72fcc34Sopenharmony_ci } 225c72fcc34Sopenharmony_ci } 226c72fcc34Sopenharmony_ci 227c72fcc34Sopenharmony_ci xfer->sample_format = SND_PCM_FORMAT_UNKNOWN; 228c72fcc34Sopenharmony_ci if (xfer->sample_format_literal) { 229c72fcc34Sopenharmony_ci err = verify_sample_format(xfer); 230c72fcc34Sopenharmony_ci if (err < 0) 231c72fcc34Sopenharmony_ci return err; 232c72fcc34Sopenharmony_ci } 233c72fcc34Sopenharmony_ci 234c72fcc34Sopenharmony_ci val = xfer->frames_per_second; 235c72fcc34Sopenharmony_ci if (xfer->frames_per_second == 0) 236c72fcc34Sopenharmony_ci xfer->frames_per_second = 8000; 237c72fcc34Sopenharmony_ci if (xfer->frames_per_second < 1000) 238c72fcc34Sopenharmony_ci xfer->frames_per_second *= 1000; 239c72fcc34Sopenharmony_ci if (xfer->frames_per_second < 2000 || 240c72fcc34Sopenharmony_ci xfer->frames_per_second > 192000) { 241c72fcc34Sopenharmony_ci fprintf(stderr, "bad speed value '%u'\n", val); 242c72fcc34Sopenharmony_ci return -EINVAL; 243c72fcc34Sopenharmony_ci } 244c72fcc34Sopenharmony_ci 245c72fcc34Sopenharmony_ci if (xfer->samples_per_frame > 0) { 246c72fcc34Sopenharmony_ci if (xfer->samples_per_frame < 1 || 247c72fcc34Sopenharmony_ci xfer->samples_per_frame > 256) { 248c72fcc34Sopenharmony_ci fprintf(stderr, "invalid channels argument '%u'\n", 249c72fcc34Sopenharmony_ci xfer->samples_per_frame); 250c72fcc34Sopenharmony_ci return -EINVAL; 251c72fcc34Sopenharmony_ci } 252c72fcc34Sopenharmony_ci } 253c72fcc34Sopenharmony_ci 254c72fcc34Sopenharmony_ci return err; 255c72fcc34Sopenharmony_ci} 256c72fcc34Sopenharmony_ci 257c72fcc34Sopenharmony_ciint xfer_options_parse_args(struct xfer_context *xfer, 258c72fcc34Sopenharmony_ci const struct xfer_data *data, int argc, 259c72fcc34Sopenharmony_ci char *const *argv) 260c72fcc34Sopenharmony_ci{ 261c72fcc34Sopenharmony_ci static const char *short_opts = "CPhvqd:s:f:c:r:t:IV:i"; 262c72fcc34Sopenharmony_ci static const struct option long_opts[] = { 263c72fcc34Sopenharmony_ci // For generic purposes. 264c72fcc34Sopenharmony_ci {"capture", 0, 0, 'C'}, 265c72fcc34Sopenharmony_ci {"playback", 0, 0, 'P'}, 266c72fcc34Sopenharmony_ci {"xfer-type", 1, 0, OPT_XFER_TYPE}, 267c72fcc34Sopenharmony_ci {"help", 0, 0, 'h'}, 268c72fcc34Sopenharmony_ci {"verbose", 0, 0, 'v'}, 269c72fcc34Sopenharmony_ci {"quiet", 0, 0, 'q'}, 270c72fcc34Sopenharmony_ci {"duration", 1, 0, 'd'}, 271c72fcc34Sopenharmony_ci {"samples", 1, 0, 's'}, 272c72fcc34Sopenharmony_ci // For transfer backend. 273c72fcc34Sopenharmony_ci {"format", 1, 0, 'f'}, 274c72fcc34Sopenharmony_ci {"channels", 1, 0, 'c'}, 275c72fcc34Sopenharmony_ci {"rate", 1, 0, 'r'}, 276c72fcc34Sopenharmony_ci // For containers. 277c72fcc34Sopenharmony_ci {"file-type", 1, 0, 't'}, 278c72fcc34Sopenharmony_ci // For mapper. 279c72fcc34Sopenharmony_ci {"separate-channels", 0, 0, 'I'}, 280c72fcc34Sopenharmony_ci // For debugging. 281c72fcc34Sopenharmony_ci {"dump-hw-params", 0, 0, OPT_DUMP_HW_PARAMS}, 282c72fcc34Sopenharmony_ci // Obsoleted. 283c72fcc34Sopenharmony_ci {"max-file-time", 1, 0, OPT_MAX_FILE_TIME}, 284c72fcc34Sopenharmony_ci {"use-strftime", 0, 0, OPT_USE_STRFTIME}, 285c72fcc34Sopenharmony_ci {"process-id-file", 1, 0, OPT_PROCESS_ID_FILE}, 286c72fcc34Sopenharmony_ci {"vumeter", 1, 0, 'V'}, 287c72fcc34Sopenharmony_ci {"interactive", 0, 0, 'i'}, 288c72fcc34Sopenharmony_ci }; 289c72fcc34Sopenharmony_ci char *s_opts; 290c72fcc34Sopenharmony_ci struct option *l_opts; 291c72fcc34Sopenharmony_ci int l_index; 292c72fcc34Sopenharmony_ci int key; 293c72fcc34Sopenharmony_ci int err = 0; 294c72fcc34Sopenharmony_ci 295c72fcc34Sopenharmony_ci // Concatenate short options. 296c72fcc34Sopenharmony_ci s_opts = malloc(strlen(data->s_opts) + strlen(short_opts) + 1); 297c72fcc34Sopenharmony_ci if (s_opts == NULL) 298c72fcc34Sopenharmony_ci return -ENOMEM; 299c72fcc34Sopenharmony_ci strcpy(s_opts, data->s_opts); 300c72fcc34Sopenharmony_ci strcpy(s_opts + strlen(s_opts), short_opts); 301c72fcc34Sopenharmony_ci s_opts[strlen(data->s_opts) + strlen(short_opts)] = '\0'; 302c72fcc34Sopenharmony_ci 303c72fcc34Sopenharmony_ci // Concatenate long options, including a sentinel. 304c72fcc34Sopenharmony_ci l_opts = calloc(ARRAY_SIZE(long_opts) * data->l_opts_count + 1, 305c72fcc34Sopenharmony_ci sizeof(*l_opts)); 306c72fcc34Sopenharmony_ci if (l_opts == NULL) { 307c72fcc34Sopenharmony_ci free(s_opts); 308c72fcc34Sopenharmony_ci return -ENOMEM; 309c72fcc34Sopenharmony_ci } 310c72fcc34Sopenharmony_ci memcpy(l_opts, long_opts, ARRAY_SIZE(long_opts) * sizeof(*l_opts)); 311c72fcc34Sopenharmony_ci memcpy(&l_opts[ARRAY_SIZE(long_opts)], data->l_opts, 312c72fcc34Sopenharmony_ci data->l_opts_count * sizeof(*l_opts)); 313c72fcc34Sopenharmony_ci 314c72fcc34Sopenharmony_ci // Parse options. 315c72fcc34Sopenharmony_ci l_index = 0; 316c72fcc34Sopenharmony_ci optarg = NULL; 317c72fcc34Sopenharmony_ci optind = 1; 318c72fcc34Sopenharmony_ci opterr = 1; // use error output. 319c72fcc34Sopenharmony_ci optopt = 0; 320c72fcc34Sopenharmony_ci while (1) { 321c72fcc34Sopenharmony_ci key = getopt_long(argc, argv, s_opts, l_opts, &l_index); 322c72fcc34Sopenharmony_ci if (key < 0) 323c72fcc34Sopenharmony_ci break; 324c72fcc34Sopenharmony_ci else if (key == 'C') 325c72fcc34Sopenharmony_ci ; // already parsed. 326c72fcc34Sopenharmony_ci else if (key == 'P') 327c72fcc34Sopenharmony_ci ; // already parsed. 328c72fcc34Sopenharmony_ci else if (key == OPT_XFER_TYPE) 329c72fcc34Sopenharmony_ci ; // already parsed. 330c72fcc34Sopenharmony_ci else if (key == 'h') 331c72fcc34Sopenharmony_ci xfer->help = true; 332c72fcc34Sopenharmony_ci else if (key == 'v') 333c72fcc34Sopenharmony_ci ++xfer->verbose; 334c72fcc34Sopenharmony_ci else if (key == 'q') 335c72fcc34Sopenharmony_ci xfer->quiet = true; 336c72fcc34Sopenharmony_ci else if (key == 'd') 337c72fcc34Sopenharmony_ci xfer->duration_seconds = arg_parse_decimal_num(optarg, &err); 338c72fcc34Sopenharmony_ci else if (key == 's') 339c72fcc34Sopenharmony_ci xfer->duration_frames = arg_parse_decimal_num(optarg, &err); 340c72fcc34Sopenharmony_ci else if (key == 'f') 341c72fcc34Sopenharmony_ci xfer->sample_format_literal = arg_duplicate_string(optarg, &err); 342c72fcc34Sopenharmony_ci else if (key == 'c') 343c72fcc34Sopenharmony_ci xfer->samples_per_frame = arg_parse_decimal_num(optarg, &err); 344c72fcc34Sopenharmony_ci else if (key == 'r') 345c72fcc34Sopenharmony_ci xfer->frames_per_second = arg_parse_decimal_num(optarg, &err); 346c72fcc34Sopenharmony_ci else if (key == 't') 347c72fcc34Sopenharmony_ci xfer->cntr_format_literal = arg_duplicate_string(optarg, &err); 348c72fcc34Sopenharmony_ci else if (key == 'I') 349c72fcc34Sopenharmony_ci xfer->multiple_cntrs = true; 350c72fcc34Sopenharmony_ci else if (key == OPT_DUMP_HW_PARAMS) 351c72fcc34Sopenharmony_ci xfer->dump_hw_params = true; 352c72fcc34Sopenharmony_ci else if (key == '?') { 353c72fcc34Sopenharmony_ci free(l_opts); 354c72fcc34Sopenharmony_ci free(s_opts); 355c72fcc34Sopenharmony_ci return -EINVAL; 356c72fcc34Sopenharmony_ci } 357c72fcc34Sopenharmony_ci else if (key == OPT_MAX_FILE_TIME || 358c72fcc34Sopenharmony_ci key == OPT_USE_STRFTIME || 359c72fcc34Sopenharmony_ci key == OPT_PROCESS_ID_FILE || 360c72fcc34Sopenharmony_ci key == 'V' || 361c72fcc34Sopenharmony_ci key == 'i') { 362c72fcc34Sopenharmony_ci fprintf(stderr, 363c72fcc34Sopenharmony_ci "An option '--%s' is obsoleted and has no " 364c72fcc34Sopenharmony_ci "effect.\n", 365c72fcc34Sopenharmony_ci l_opts[l_index].name); 366c72fcc34Sopenharmony_ci err = -EINVAL; 367c72fcc34Sopenharmony_ci } else { 368c72fcc34Sopenharmony_ci err = xfer->ops->parse_opt(xfer, key, optarg); 369c72fcc34Sopenharmony_ci if (err < 0 && err != -ENXIO) 370c72fcc34Sopenharmony_ci break; 371c72fcc34Sopenharmony_ci } 372c72fcc34Sopenharmony_ci } 373c72fcc34Sopenharmony_ci 374c72fcc34Sopenharmony_ci free(l_opts); 375c72fcc34Sopenharmony_ci free(s_opts); 376c72fcc34Sopenharmony_ci 377c72fcc34Sopenharmony_ci if (xfer->help) { 378c72fcc34Sopenharmony_ci print_help(); 379c72fcc34Sopenharmony_ci if (xfer->ops->help) { 380c72fcc34Sopenharmony_ci printf("\n"); 381c72fcc34Sopenharmony_ci printf(" BACKEND-OPTIONS (%s) =\n", 382c72fcc34Sopenharmony_ci xfer_label_from_type(xfer->type)); 383c72fcc34Sopenharmony_ci xfer->ops->help(xfer); 384c72fcc34Sopenharmony_ci } 385c72fcc34Sopenharmony_ci return 0; 386c72fcc34Sopenharmony_ci } 387c72fcc34Sopenharmony_ci 388c72fcc34Sopenharmony_ci err = allocate_paths(xfer, argv + optind, argc - optind); 389c72fcc34Sopenharmony_ci if (err < 0) 390c72fcc34Sopenharmony_ci return err; 391c72fcc34Sopenharmony_ci 392c72fcc34Sopenharmony_ci return validate_options(xfer); 393c72fcc34Sopenharmony_ci} 394c72fcc34Sopenharmony_ci 395c72fcc34Sopenharmony_civoid xfer_options_calculate_duration(struct xfer_context *xfer, 396c72fcc34Sopenharmony_ci uint64_t *total_frame_count) 397c72fcc34Sopenharmony_ci{ 398c72fcc34Sopenharmony_ci uint64_t frame_count; 399c72fcc34Sopenharmony_ci 400c72fcc34Sopenharmony_ci if (xfer->duration_seconds > 0) { 401c72fcc34Sopenharmony_ci frame_count = (uint64_t)xfer->duration_seconds * (uint64_t)xfer->frames_per_second; 402c72fcc34Sopenharmony_ci if (frame_count < *total_frame_count) 403c72fcc34Sopenharmony_ci *total_frame_count = frame_count; 404c72fcc34Sopenharmony_ci } 405c72fcc34Sopenharmony_ci 406c72fcc34Sopenharmony_ci if (xfer->duration_frames > 0) { 407c72fcc34Sopenharmony_ci frame_count = xfer->duration_frames; 408c72fcc34Sopenharmony_ci if (frame_count < *total_frame_count) 409c72fcc34Sopenharmony_ci *total_frame_count = frame_count; 410c72fcc34Sopenharmony_ci } 411c72fcc34Sopenharmony_ci} 412c72fcc34Sopenharmony_ci 413c72fcc34Sopenharmony_cistatic const char *const allowed_duplication[] = { 414c72fcc34Sopenharmony_ci "/dev/null", 415c72fcc34Sopenharmony_ci "/dev/zero", 416c72fcc34Sopenharmony_ci "/dev/full", 417c72fcc34Sopenharmony_ci "/dev/random", 418c72fcc34Sopenharmony_ci "/dev/urandom", 419c72fcc34Sopenharmony_ci}; 420c72fcc34Sopenharmony_ci 421c72fcc34Sopenharmony_cistatic int generate_path_with_suffix(struct xfer_context *xfer, 422c72fcc34Sopenharmony_ci const char *template, unsigned int index, 423c72fcc34Sopenharmony_ci const char *suffix) 424c72fcc34Sopenharmony_ci{ 425c72fcc34Sopenharmony_ci static const char *const single_format = "%s%s"; 426c72fcc34Sopenharmony_ci static const char *const multiple_format = "%s-%i%s"; 427c72fcc34Sopenharmony_ci unsigned int len; 428c72fcc34Sopenharmony_ci 429c72fcc34Sopenharmony_ci len = strlen(template) + strlen(suffix) + 1; 430c72fcc34Sopenharmony_ci if (xfer->path_count > 1) 431c72fcc34Sopenharmony_ci len += (unsigned int)log10(xfer->path_count) + 2; 432c72fcc34Sopenharmony_ci 433c72fcc34Sopenharmony_ci xfer->paths[index] = malloc(len); 434c72fcc34Sopenharmony_ci if (xfer->paths[index] == NULL) 435c72fcc34Sopenharmony_ci return -ENOMEM; 436c72fcc34Sopenharmony_ci 437c72fcc34Sopenharmony_ci if (xfer->path_count == 1) { 438c72fcc34Sopenharmony_ci snprintf(xfer->paths[index], len, single_format, template, 439c72fcc34Sopenharmony_ci suffix); 440c72fcc34Sopenharmony_ci } else { 441c72fcc34Sopenharmony_ci snprintf(xfer->paths[index], len, multiple_format, template, 442c72fcc34Sopenharmony_ci index, suffix); 443c72fcc34Sopenharmony_ci } 444c72fcc34Sopenharmony_ci 445c72fcc34Sopenharmony_ci return 0; 446c72fcc34Sopenharmony_ci} 447c72fcc34Sopenharmony_ci 448c72fcc34Sopenharmony_cistatic int generate_path_without_suffix(struct xfer_context *xfer, 449c72fcc34Sopenharmony_ci const char *template, 450c72fcc34Sopenharmony_ci unsigned int index, 451c72fcc34Sopenharmony_ci const char *suffix ATTRIBUTE_UNUSED) 452c72fcc34Sopenharmony_ci{ 453c72fcc34Sopenharmony_ci static const char *const single_format = "%s"; 454c72fcc34Sopenharmony_ci static const char *const multiple_format = "%s-%i"; 455c72fcc34Sopenharmony_ci unsigned int len; 456c72fcc34Sopenharmony_ci 457c72fcc34Sopenharmony_ci len = strlen(template) + 1; 458c72fcc34Sopenharmony_ci if (xfer->path_count > 1) 459c72fcc34Sopenharmony_ci len += (unsigned int)log10(xfer->path_count) + 2; 460c72fcc34Sopenharmony_ci 461c72fcc34Sopenharmony_ci xfer->paths[index] = malloc(len); 462c72fcc34Sopenharmony_ci if (xfer->paths[index] == NULL) 463c72fcc34Sopenharmony_ci return -ENOMEM; 464c72fcc34Sopenharmony_ci 465c72fcc34Sopenharmony_ci if (xfer->path_count == 1) { 466c72fcc34Sopenharmony_ci snprintf(xfer->paths[index], len, single_format, template); 467c72fcc34Sopenharmony_ci } else { 468c72fcc34Sopenharmony_ci snprintf(xfer->paths[index], len, multiple_format, template, 469c72fcc34Sopenharmony_ci index); 470c72fcc34Sopenharmony_ci } 471c72fcc34Sopenharmony_ci 472c72fcc34Sopenharmony_ci return 0; 473c72fcc34Sopenharmony_ci} 474c72fcc34Sopenharmony_ci 475c72fcc34Sopenharmony_cistatic int generate_path(struct xfer_context *xfer, char *template, 476c72fcc34Sopenharmony_ci unsigned int index, const char *suffix) 477c72fcc34Sopenharmony_ci{ 478c72fcc34Sopenharmony_ci int (*generator)(struct xfer_context *xfer, const char *template, 479c72fcc34Sopenharmony_ci unsigned int index, const char *suffix); 480c72fcc34Sopenharmony_ci char *pos; 481c72fcc34Sopenharmony_ci 482c72fcc34Sopenharmony_ci if (strlen(suffix) > 0) { 483c72fcc34Sopenharmony_ci pos = template + strlen(template) - strlen(suffix); 484c72fcc34Sopenharmony_ci // Separate filename and suffix. 485c72fcc34Sopenharmony_ci if (!strcmp(pos, suffix)) 486c72fcc34Sopenharmony_ci *pos = '\0'; 487c72fcc34Sopenharmony_ci } 488c72fcc34Sopenharmony_ci 489c72fcc34Sopenharmony_ci // Select handlers. 490c72fcc34Sopenharmony_ci if (strlen(suffix) > 0) 491c72fcc34Sopenharmony_ci generator = generate_path_with_suffix; 492c72fcc34Sopenharmony_ci else 493c72fcc34Sopenharmony_ci generator = generate_path_without_suffix; 494c72fcc34Sopenharmony_ci 495c72fcc34Sopenharmony_ci return generator(xfer, template, index, suffix); 496c72fcc34Sopenharmony_ci} 497c72fcc34Sopenharmony_ci 498c72fcc34Sopenharmony_cistatic int create_paths(struct xfer_context *xfer, unsigned int path_count) 499c72fcc34Sopenharmony_ci{ 500c72fcc34Sopenharmony_ci char *template; 501c72fcc34Sopenharmony_ci const char *suffix; 502c72fcc34Sopenharmony_ci unsigned int i, j; 503c72fcc34Sopenharmony_ci int err = 0; 504c72fcc34Sopenharmony_ci 505c72fcc34Sopenharmony_ci // Can cause memory leak. 506c72fcc34Sopenharmony_ci assert(xfer->path_count == 1); 507c72fcc34Sopenharmony_ci assert(xfer->paths); 508c72fcc34Sopenharmony_ci assert(xfer->paths[0]); 509c72fcc34Sopenharmony_ci assert(xfer->paths[0][0] != '\0'); 510c72fcc34Sopenharmony_ci 511c72fcc34Sopenharmony_ci // Release at first. 512c72fcc34Sopenharmony_ci template = xfer->paths[0]; 513c72fcc34Sopenharmony_ci free(xfer->paths); 514c72fcc34Sopenharmony_ci xfer->paths = NULL; 515c72fcc34Sopenharmony_ci 516c72fcc34Sopenharmony_ci // Allocate again. 517c72fcc34Sopenharmony_ci xfer->paths = calloc(path_count, sizeof(*xfer->paths)); 518c72fcc34Sopenharmony_ci if (xfer->paths == NULL) { 519c72fcc34Sopenharmony_ci err = -ENOMEM; 520c72fcc34Sopenharmony_ci goto end; 521c72fcc34Sopenharmony_ci } 522c72fcc34Sopenharmony_ci xfer->path_count = path_count; 523c72fcc34Sopenharmony_ci 524c72fcc34Sopenharmony_ci suffix = container_suffix_from_format(xfer->cntr_format); 525c72fcc34Sopenharmony_ci 526c72fcc34Sopenharmony_ci for (i = 0; i < xfer->path_count; ++i) { 527c72fcc34Sopenharmony_ci // Some file names are allowed to be duplicated. 528c72fcc34Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(allowed_duplication); ++j) { 529c72fcc34Sopenharmony_ci if (!strcmp(template, allowed_duplication[j])) 530c72fcc34Sopenharmony_ci break; 531c72fcc34Sopenharmony_ci } 532c72fcc34Sopenharmony_ci if (j < ARRAY_SIZE(allowed_duplication)) 533c72fcc34Sopenharmony_ci continue; 534c72fcc34Sopenharmony_ci 535c72fcc34Sopenharmony_ci err = generate_path(xfer, template, i, suffix); 536c72fcc34Sopenharmony_ci if (err < 0) 537c72fcc34Sopenharmony_ci break; 538c72fcc34Sopenharmony_ci } 539c72fcc34Sopenharmony_ciend: 540c72fcc34Sopenharmony_ci free(template); 541c72fcc34Sopenharmony_ci 542c72fcc34Sopenharmony_ci return err; 543c72fcc34Sopenharmony_ci} 544c72fcc34Sopenharmony_ci 545c72fcc34Sopenharmony_cistatic int fixup_paths(struct xfer_context *xfer) 546c72fcc34Sopenharmony_ci{ 547c72fcc34Sopenharmony_ci const char *suffix; 548c72fcc34Sopenharmony_ci char *template; 549c72fcc34Sopenharmony_ci unsigned int i, j; 550c72fcc34Sopenharmony_ci int err = 0; 551c72fcc34Sopenharmony_ci 552c72fcc34Sopenharmony_ci suffix = container_suffix_from_format(xfer->cntr_format); 553c72fcc34Sopenharmony_ci 554c72fcc34Sopenharmony_ci for (i = 0; i < xfer->path_count; ++i) { 555c72fcc34Sopenharmony_ci // Some file names are allowed to be duplicated. 556c72fcc34Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(allowed_duplication); ++j) { 557c72fcc34Sopenharmony_ci if (!strcmp(xfer->paths[i], allowed_duplication[j])) 558c72fcc34Sopenharmony_ci break; 559c72fcc34Sopenharmony_ci } 560c72fcc34Sopenharmony_ci if (j < ARRAY_SIZE(allowed_duplication)) 561c72fcc34Sopenharmony_ci continue; 562c72fcc34Sopenharmony_ci 563c72fcc34Sopenharmony_ci template = xfer->paths[i]; 564c72fcc34Sopenharmony_ci xfer->paths[i] = NULL; 565c72fcc34Sopenharmony_ci err = generate_path(xfer, template, i, suffix); 566c72fcc34Sopenharmony_ci free(template); 567c72fcc34Sopenharmony_ci if (err < 0) 568c72fcc34Sopenharmony_ci break; 569c72fcc34Sopenharmony_ci } 570c72fcc34Sopenharmony_ci 571c72fcc34Sopenharmony_ci return err; 572c72fcc34Sopenharmony_ci} 573c72fcc34Sopenharmony_ci 574c72fcc34Sopenharmony_ciint xfer_options_fixup_paths(struct xfer_context *xfer) 575c72fcc34Sopenharmony_ci{ 576c72fcc34Sopenharmony_ci unsigned int i, j; 577c72fcc34Sopenharmony_ci int err; 578c72fcc34Sopenharmony_ci 579c72fcc34Sopenharmony_ci if (xfer->path_count == 1) { 580c72fcc34Sopenharmony_ci // Nothing to do for sign of stdin/stdout. 581c72fcc34Sopenharmony_ci if (!strcmp(xfer->paths[0], "-")) 582c72fcc34Sopenharmony_ci return 0; 583c72fcc34Sopenharmony_ci if (!xfer->multiple_cntrs) 584c72fcc34Sopenharmony_ci err = fixup_paths(xfer); 585c72fcc34Sopenharmony_ci else 586c72fcc34Sopenharmony_ci err = create_paths(xfer, xfer->samples_per_frame); 587c72fcc34Sopenharmony_ci } else { 588c72fcc34Sopenharmony_ci if (!xfer->multiple_cntrs) 589c72fcc34Sopenharmony_ci return -EINVAL; 590c72fcc34Sopenharmony_ci if (xfer->path_count != xfer->samples_per_frame) 591c72fcc34Sopenharmony_ci return -EINVAL; 592c72fcc34Sopenharmony_ci else 593c72fcc34Sopenharmony_ci err = fixup_paths(xfer); 594c72fcc34Sopenharmony_ci } 595c72fcc34Sopenharmony_ci if (err < 0) 596c72fcc34Sopenharmony_ci return err; 597c72fcc34Sopenharmony_ci 598c72fcc34Sopenharmony_ci // Check duplication of the paths. 599c72fcc34Sopenharmony_ci for (i = 0; i < xfer->path_count - 1; ++i) { 600c72fcc34Sopenharmony_ci // Some file names are allowed to be duplicated. 601c72fcc34Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(allowed_duplication); ++j) { 602c72fcc34Sopenharmony_ci if (!strcmp(xfer->paths[i], allowed_duplication[j])) 603c72fcc34Sopenharmony_ci break; 604c72fcc34Sopenharmony_ci } 605c72fcc34Sopenharmony_ci if (j < ARRAY_SIZE(allowed_duplication)) 606c72fcc34Sopenharmony_ci continue; 607c72fcc34Sopenharmony_ci 608c72fcc34Sopenharmony_ci for (j = i + 1; j < xfer->path_count; ++j) { 609c72fcc34Sopenharmony_ci if (!strcmp(xfer->paths[i], xfer->paths[j])) { 610c72fcc34Sopenharmony_ci fprintf(stderr, 611c72fcc34Sopenharmony_ci "Detect duplicated file names:\n"); 612c72fcc34Sopenharmony_ci err = -EINVAL; 613c72fcc34Sopenharmony_ci break; 614c72fcc34Sopenharmony_ci } 615c72fcc34Sopenharmony_ci } 616c72fcc34Sopenharmony_ci if (j < xfer->path_count) 617c72fcc34Sopenharmony_ci break; 618c72fcc34Sopenharmony_ci } 619c72fcc34Sopenharmony_ci 620c72fcc34Sopenharmony_ci if (xfer->verbose > 1) 621c72fcc34Sopenharmony_ci fprintf(stderr, "Handled file names:\n"); 622c72fcc34Sopenharmony_ci if (err < 0 || xfer->verbose > 1) { 623c72fcc34Sopenharmony_ci for (i = 0; i < xfer->path_count; ++i) 624c72fcc34Sopenharmony_ci fprintf(stderr, " %d: %s\n", i, xfer->paths[i]); 625c72fcc34Sopenharmony_ci } 626c72fcc34Sopenharmony_ci 627c72fcc34Sopenharmony_ci return err; 628c72fcc34Sopenharmony_ci} 629