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