1c72fcc34Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 2c72fcc34Sopenharmony_ci// 3c72fcc34Sopenharmony_ci// xfer-libffado.c - receive/transmit frames by libffado. 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 "frame-cache.h" 13c72fcc34Sopenharmony_ci 14c72fcc34Sopenharmony_ci#include <stdio.h> 15c72fcc34Sopenharmony_ci#include <sched.h> 16c72fcc34Sopenharmony_ci 17c72fcc34Sopenharmony_ci#include <libffado/ffado.h> 18c72fcc34Sopenharmony_ci 19c72fcc34Sopenharmony_cistruct libffado_state { 20c72fcc34Sopenharmony_ci ffado_device_t *handle; 21c72fcc34Sopenharmony_ci enum ffado_direction direction; 22c72fcc34Sopenharmony_ci 23c72fcc34Sopenharmony_ci char *port_literal; 24c72fcc34Sopenharmony_ci char *node_literal; 25c72fcc34Sopenharmony_ci char *guid_literal; 26c72fcc34Sopenharmony_ci unsigned int frames_per_period; 27c72fcc34Sopenharmony_ci unsigned int periods_per_buffer; 28c72fcc34Sopenharmony_ci unsigned int sched_priority; 29c72fcc34Sopenharmony_ci bool slave_mode:1; 30c72fcc34Sopenharmony_ci bool snoop_mode:1; 31c72fcc34Sopenharmony_ci 32c72fcc34Sopenharmony_ci unsigned int data_ch_count; 33c72fcc34Sopenharmony_ci ffado_streaming_stream_type *data_ch_map; 34c72fcc34Sopenharmony_ci 35c72fcc34Sopenharmony_ci int (*process_frames)(struct xfer_context *xfer, 36c72fcc34Sopenharmony_ci unsigned int *frame_count, 37c72fcc34Sopenharmony_ci struct mapper_context *mapper, 38c72fcc34Sopenharmony_ci struct container_context *cntrs); 39c72fcc34Sopenharmony_ci 40c72fcc34Sopenharmony_ci struct frame_cache cache; 41c72fcc34Sopenharmony_ci}; 42c72fcc34Sopenharmony_ci 43c72fcc34Sopenharmony_cienum no_short_opts { 44c72fcc34Sopenharmony_ci OPT_FRAMES_PER_PERIOD = 200, 45c72fcc34Sopenharmony_ci OPT_PERIODS_PER_BUFFER, 46c72fcc34Sopenharmony_ci OPT_SLAVE_MODE, 47c72fcc34Sopenharmony_ci OPT_SNOOP_MODE, 48c72fcc34Sopenharmony_ci OPT_SCHED_PRIORITY, 49c72fcc34Sopenharmony_ci}; 50c72fcc34Sopenharmony_ci 51c72fcc34Sopenharmony_ci#define S_OPTS "p:n:g:" 52c72fcc34Sopenharmony_cistatic const struct option l_opts[] = { 53c72fcc34Sopenharmony_ci {"port", 1, 0, 'p'}, 54c72fcc34Sopenharmony_ci {"node", 1, 0, 'n'}, 55c72fcc34Sopenharmony_ci {"guid", 1, 0, 'g'}, 56c72fcc34Sopenharmony_ci {"frames-per-period", 1, 0, OPT_FRAMES_PER_PERIOD}, 57c72fcc34Sopenharmony_ci {"periods-per-buffer", 1, 0, OPT_PERIODS_PER_BUFFER}, 58c72fcc34Sopenharmony_ci {"slave", 0, 0, OPT_SLAVE_MODE}, 59c72fcc34Sopenharmony_ci {"snoop", 0, 0, OPT_SNOOP_MODE}, 60c72fcc34Sopenharmony_ci {"sched-priority", 1, 0, OPT_SCHED_PRIORITY}, // to SCHED_FIFO 61c72fcc34Sopenharmony_ci}; 62c72fcc34Sopenharmony_ci 63c72fcc34Sopenharmony_cistatic int xfer_libffado_init(struct xfer_context *xfer, 64c72fcc34Sopenharmony_ci snd_pcm_stream_t direction) 65c72fcc34Sopenharmony_ci{ 66c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 67c72fcc34Sopenharmony_ci 68c72fcc34Sopenharmony_ci if (direction == SND_PCM_STREAM_CAPTURE) 69c72fcc34Sopenharmony_ci state->direction = FFADO_CAPTURE; 70c72fcc34Sopenharmony_ci else if (direction == SND_PCM_STREAM_PLAYBACK) 71c72fcc34Sopenharmony_ci state->direction = FFADO_PLAYBACK; 72c72fcc34Sopenharmony_ci else 73c72fcc34Sopenharmony_ci return -EINVAL; 74c72fcc34Sopenharmony_ci 75c72fcc34Sopenharmony_ci return 0; 76c72fcc34Sopenharmony_ci} 77c72fcc34Sopenharmony_ci 78c72fcc34Sopenharmony_cistatic int xfer_libffado_parse_opt(struct xfer_context *xfer, int key, 79c72fcc34Sopenharmony_ci const char *optarg) 80c72fcc34Sopenharmony_ci{ 81c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 82c72fcc34Sopenharmony_ci int err; 83c72fcc34Sopenharmony_ci 84c72fcc34Sopenharmony_ci if (key == 'p') 85c72fcc34Sopenharmony_ci state->port_literal = arg_duplicate_string(optarg, &err); 86c72fcc34Sopenharmony_ci else if (key == 'n') 87c72fcc34Sopenharmony_ci state->node_literal = arg_duplicate_string(optarg, &err); 88c72fcc34Sopenharmony_ci else if (key == 'g') 89c72fcc34Sopenharmony_ci state->guid_literal = arg_duplicate_string(optarg, &err); 90c72fcc34Sopenharmony_ci else if (key == OPT_FRAMES_PER_PERIOD) 91c72fcc34Sopenharmony_ci state->frames_per_period = arg_parse_decimal_num(optarg, &err); 92c72fcc34Sopenharmony_ci else if (key == OPT_PERIODS_PER_BUFFER) 93c72fcc34Sopenharmony_ci state->periods_per_buffer = arg_parse_decimal_num(optarg, &err); 94c72fcc34Sopenharmony_ci else if (key == OPT_SLAVE_MODE) 95c72fcc34Sopenharmony_ci state->slave_mode = true; 96c72fcc34Sopenharmony_ci else if (key == OPT_SNOOP_MODE) 97c72fcc34Sopenharmony_ci state->snoop_mode = true; 98c72fcc34Sopenharmony_ci else if (key == OPT_SCHED_PRIORITY) 99c72fcc34Sopenharmony_ci state->sched_priority = arg_parse_decimal_num(optarg, &err); 100c72fcc34Sopenharmony_ci else 101c72fcc34Sopenharmony_ci err = -ENXIO; 102c72fcc34Sopenharmony_ci 103c72fcc34Sopenharmony_ci return err; 104c72fcc34Sopenharmony_ci} 105c72fcc34Sopenharmony_ci 106c72fcc34Sopenharmony_cistatic int validate_sched_priority(struct libffado_state *state) 107c72fcc34Sopenharmony_ci{ 108c72fcc34Sopenharmony_ci int val; 109c72fcc34Sopenharmony_ci 110c72fcc34Sopenharmony_ci val = sched_get_priority_max(SCHED_FIFO); 111c72fcc34Sopenharmony_ci if (val < 0) 112c72fcc34Sopenharmony_ci return -errno; 113c72fcc34Sopenharmony_ci if (state->sched_priority > val) 114c72fcc34Sopenharmony_ci return -EINVAL; 115c72fcc34Sopenharmony_ci 116c72fcc34Sopenharmony_ci val = sched_get_priority_min(SCHED_FIFO); 117c72fcc34Sopenharmony_ci if (val < 0) 118c72fcc34Sopenharmony_ci return -errno; 119c72fcc34Sopenharmony_ci if (state->sched_priority < val) 120c72fcc34Sopenharmony_ci return -EINVAL; 121c72fcc34Sopenharmony_ci 122c72fcc34Sopenharmony_ci return 0; 123c72fcc34Sopenharmony_ci} 124c72fcc34Sopenharmony_ci 125c72fcc34Sopenharmony_cistatic int xfer_libffado_validate_opts(struct xfer_context *xfer) 126c72fcc34Sopenharmony_ci{ 127c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 128c72fcc34Sopenharmony_ci int err; 129c72fcc34Sopenharmony_ci 130c72fcc34Sopenharmony_ci if (state->node_literal != NULL) { 131c72fcc34Sopenharmony_ci if (state->port_literal == NULL) { 132c72fcc34Sopenharmony_ci fprintf(stderr, 133c72fcc34Sopenharmony_ci "'n' option should correspond 'p' option.\n"); 134c72fcc34Sopenharmony_ci return -EINVAL; 135c72fcc34Sopenharmony_ci } 136c72fcc34Sopenharmony_ci } 137c72fcc34Sopenharmony_ci 138c72fcc34Sopenharmony_ci if (state->port_literal != NULL && state->guid_literal != NULL) { 139c72fcc34Sopenharmony_ci fprintf(stderr, 140c72fcc34Sopenharmony_ci "Neither 'p' option nor 'g' option is available at the " 141c72fcc34Sopenharmony_ci "same time.\n"); 142c72fcc34Sopenharmony_ci return -EINVAL; 143c72fcc34Sopenharmony_ci } 144c72fcc34Sopenharmony_ci 145c72fcc34Sopenharmony_ci if (state->guid_literal != NULL) { 146c72fcc34Sopenharmony_ci if (strstr(state->guid_literal, "0x") != state->guid_literal) { 147c72fcc34Sopenharmony_ci fprintf(stderr, 148c72fcc34Sopenharmony_ci "A value of 'g' option should have '0x' as its " 149c72fcc34Sopenharmony_ci "prefix for hexadecimal number.\n"); 150c72fcc34Sopenharmony_ci return -EINVAL; 151c72fcc34Sopenharmony_ci } 152c72fcc34Sopenharmony_ci } 153c72fcc34Sopenharmony_ci 154c72fcc34Sopenharmony_ci if (state->slave_mode && state->snoop_mode) { 155c72fcc34Sopenharmony_ci fprintf(stderr, "Neither slave mode nor snoop mode is available" 156c72fcc34Sopenharmony_ci "at the same time.\n"); 157c72fcc34Sopenharmony_ci return -EINVAL; 158c72fcc34Sopenharmony_ci } 159c72fcc34Sopenharmony_ci 160c72fcc34Sopenharmony_ci if (state->sched_priority > 0) { 161c72fcc34Sopenharmony_ci err = validate_sched_priority(state); 162c72fcc34Sopenharmony_ci if (err < 0) 163c72fcc34Sopenharmony_ci return err; 164c72fcc34Sopenharmony_ci } 165c72fcc34Sopenharmony_ci 166c72fcc34Sopenharmony_ci if (state->frames_per_period == 0) 167c72fcc34Sopenharmony_ci state->frames_per_period = 512; 168c72fcc34Sopenharmony_ci if (state->periods_per_buffer == 0) 169c72fcc34Sopenharmony_ci state->periods_per_buffer = 2; 170c72fcc34Sopenharmony_ci 171c72fcc34Sopenharmony_ci return 0; 172c72fcc34Sopenharmony_ci} 173c72fcc34Sopenharmony_ci 174c72fcc34Sopenharmony_cistatic int r_process_frames(struct xfer_context *xfer, 175c72fcc34Sopenharmony_ci unsigned int *frame_count, 176c72fcc34Sopenharmony_ci struct mapper_context *mapper, 177c72fcc34Sopenharmony_ci struct container_context *cntrs) 178c72fcc34Sopenharmony_ci{ 179c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 180c72fcc34Sopenharmony_ci unsigned int avail_count; 181c72fcc34Sopenharmony_ci unsigned int bytes_per_frame; 182c72fcc34Sopenharmony_ci unsigned int consumed_count; 183c72fcc34Sopenharmony_ci int err; 184c72fcc34Sopenharmony_ci 185c72fcc34Sopenharmony_ci // Trim up to expected frame count. 186c72fcc34Sopenharmony_ci avail_count = state->frames_per_period; 187c72fcc34Sopenharmony_ci if (*frame_count < avail_count) 188c72fcc34Sopenharmony_ci avail_count = *frame_count; 189c72fcc34Sopenharmony_ci 190c72fcc34Sopenharmony_ci // Cache required amount of frames. 191c72fcc34Sopenharmony_ci if (avail_count > frame_cache_get_count(&state->cache)) { 192c72fcc34Sopenharmony_ci int ch; 193c72fcc34Sopenharmony_ci int pos; 194c72fcc34Sopenharmony_ci 195c72fcc34Sopenharmony_ci // Register buffers. 196c72fcc34Sopenharmony_ci pos = 0; 197c72fcc34Sopenharmony_ci bytes_per_frame = state->cache.bytes_per_sample * 198c72fcc34Sopenharmony_ci state->cache.samples_per_frame; 199c72fcc34Sopenharmony_ci for (ch = 0; ch < state->data_ch_count; ++ch) { 200c72fcc34Sopenharmony_ci char *buf; 201c72fcc34Sopenharmony_ci 202c72fcc34Sopenharmony_ci if (state->data_ch_map[ch] != ffado_stream_type_audio) 203c72fcc34Sopenharmony_ci continue; 204c72fcc34Sopenharmony_ci 205c72fcc34Sopenharmony_ci buf = state->cache.buf_ptr; 206c72fcc34Sopenharmony_ci buf += ch * bytes_per_frame; 207c72fcc34Sopenharmony_ci if (ffado_streaming_set_capture_stream_buffer(state->handle, 208c72fcc34Sopenharmony_ci ch, buf)) 209c72fcc34Sopenharmony_ci return -EIO; 210c72fcc34Sopenharmony_ci ++pos; 211c72fcc34Sopenharmony_ci } 212c72fcc34Sopenharmony_ci 213c72fcc34Sopenharmony_ci assert(pos == xfer->samples_per_frame); 214c72fcc34Sopenharmony_ci 215c72fcc34Sopenharmony_ci // Move data to the buffer from intermediate buffer. 216c72fcc34Sopenharmony_ci if (!ffado_streaming_transfer_buffers(state->handle)) 217c72fcc34Sopenharmony_ci return -EIO; 218c72fcc34Sopenharmony_ci 219c72fcc34Sopenharmony_ci frame_cache_increase_count(&state->cache, 220c72fcc34Sopenharmony_ci state->frames_per_period); 221c72fcc34Sopenharmony_ci } 222c72fcc34Sopenharmony_ci 223c72fcc34Sopenharmony_ci // Write out to file descriptors. 224c72fcc34Sopenharmony_ci consumed_count = frame_cache_get_count(&state->cache); 225c72fcc34Sopenharmony_ci err = mapper_context_process_frames(mapper, state->cache.buf, 226c72fcc34Sopenharmony_ci &consumed_count, cntrs); 227c72fcc34Sopenharmony_ci if (err < 0) 228c72fcc34Sopenharmony_ci return err; 229c72fcc34Sopenharmony_ci 230c72fcc34Sopenharmony_ci frame_cache_reduce(&state->cache, consumed_count); 231c72fcc34Sopenharmony_ci 232c72fcc34Sopenharmony_ci *frame_count = consumed_count; 233c72fcc34Sopenharmony_ci 234c72fcc34Sopenharmony_ci return 0; 235c72fcc34Sopenharmony_ci} 236c72fcc34Sopenharmony_ci 237c72fcc34Sopenharmony_cistatic int w_process_frames(struct xfer_context *xfer, 238c72fcc34Sopenharmony_ci unsigned int *frame_count, 239c72fcc34Sopenharmony_ci struct mapper_context *mapper, 240c72fcc34Sopenharmony_ci struct container_context *cntrs) 241c72fcc34Sopenharmony_ci{ 242c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 243c72fcc34Sopenharmony_ci unsigned int avail_count; 244c72fcc34Sopenharmony_ci int pos; 245c72fcc34Sopenharmony_ci int ch; 246c72fcc34Sopenharmony_ci unsigned int bytes_per_frame; 247c72fcc34Sopenharmony_ci unsigned int consumed_count; 248c72fcc34Sopenharmony_ci int err; 249c72fcc34Sopenharmony_ci 250c72fcc34Sopenharmony_ci // Trim up to expected frame_count. 251c72fcc34Sopenharmony_ci avail_count = state->frames_per_period; 252c72fcc34Sopenharmony_ci if (*frame_count < avail_count) 253c72fcc34Sopenharmony_ci avail_count = *frame_count; 254c72fcc34Sopenharmony_ci 255c72fcc34Sopenharmony_ci // Cache required amount of frames. 256c72fcc34Sopenharmony_ci if (avail_count > frame_cache_get_count(&state->cache)) { 257c72fcc34Sopenharmony_ci avail_count -= frame_cache_get_count(&state->cache); 258c72fcc34Sopenharmony_ci 259c72fcc34Sopenharmony_ci err = mapper_context_process_frames(mapper, state->cache.buf_ptr, 260c72fcc34Sopenharmony_ci &avail_count, cntrs); 261c72fcc34Sopenharmony_ci if (err < 0) 262c72fcc34Sopenharmony_ci return err; 263c72fcc34Sopenharmony_ci frame_cache_increase_count(&state->cache, avail_count); 264c72fcc34Sopenharmony_ci avail_count = state->cache.remained_count; 265c72fcc34Sopenharmony_ci } 266c72fcc34Sopenharmony_ci 267c72fcc34Sopenharmony_ci // Register buffers. 268c72fcc34Sopenharmony_ci pos = 0; 269c72fcc34Sopenharmony_ci bytes_per_frame = state->cache.bytes_per_sample * 270c72fcc34Sopenharmony_ci state->cache.samples_per_frame; 271c72fcc34Sopenharmony_ci for (ch = 0; ch < state->data_ch_count; ++ch) { 272c72fcc34Sopenharmony_ci char *buf; 273c72fcc34Sopenharmony_ci 274c72fcc34Sopenharmony_ci if (state->data_ch_map[ch] != ffado_stream_type_audio) 275c72fcc34Sopenharmony_ci continue; 276c72fcc34Sopenharmony_ci 277c72fcc34Sopenharmony_ci buf = state->cache.buf; 278c72fcc34Sopenharmony_ci buf += bytes_per_frame; 279c72fcc34Sopenharmony_ci if (ffado_streaming_set_playback_stream_buffer(state->handle, 280c72fcc34Sopenharmony_ci ch, buf)) 281c72fcc34Sopenharmony_ci return -EIO; 282c72fcc34Sopenharmony_ci ++pos; 283c72fcc34Sopenharmony_ci } 284c72fcc34Sopenharmony_ci 285c72fcc34Sopenharmony_ci assert(pos == xfer->samples_per_frame); 286c72fcc34Sopenharmony_ci 287c72fcc34Sopenharmony_ci // Move data on the buffer for transmission. 288c72fcc34Sopenharmony_ci if (!ffado_streaming_transfer_buffers(state->handle)) 289c72fcc34Sopenharmony_ci return -EIO; 290c72fcc34Sopenharmony_ci consumed_count = state->frames_per_period; 291c72fcc34Sopenharmony_ci 292c72fcc34Sopenharmony_ci frame_cache_reduce(&state->cache, consumed_count); 293c72fcc34Sopenharmony_ci 294c72fcc34Sopenharmony_ci *frame_count = consumed_count; 295c72fcc34Sopenharmony_ci 296c72fcc34Sopenharmony_ci return 0; 297c72fcc34Sopenharmony_ci} 298c72fcc34Sopenharmony_ci 299c72fcc34Sopenharmony_cistatic int open_handle(struct xfer_context *xfer, 300c72fcc34Sopenharmony_ci unsigned int frames_per_second) 301c72fcc34Sopenharmony_ci{ 302c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 303c72fcc34Sopenharmony_ci ffado_options_t options = {0}; 304c72fcc34Sopenharmony_ci ffado_device_info_t info = {0}; 305c72fcc34Sopenharmony_ci 306c72fcc34Sopenharmony_ci char str[32] = {0}; 307c72fcc34Sopenharmony_ci char *strings[1]; 308c72fcc34Sopenharmony_ci 309c72fcc34Sopenharmony_ci // Set target unit if given. 310c72fcc34Sopenharmony_ci if (state->port_literal != NULL) { 311c72fcc34Sopenharmony_ci if (state->node_literal != NULL) { 312c72fcc34Sopenharmony_ci snprintf(str, sizeof(str), "hw:%s,%s", 313c72fcc34Sopenharmony_ci state->port_literal, state->node_literal); 314c72fcc34Sopenharmony_ci } else { 315c72fcc34Sopenharmony_ci snprintf(str, sizeof(str), "hw:%s", 316c72fcc34Sopenharmony_ci state->port_literal); 317c72fcc34Sopenharmony_ci } 318c72fcc34Sopenharmony_ci } else if (state->guid_literal != NULL) { 319c72fcc34Sopenharmony_ci snprintf(str, sizeof(str), "guid:%s", state->guid_literal); 320c72fcc34Sopenharmony_ci } 321c72fcc34Sopenharmony_ci if (str[0] != '\0') { 322c72fcc34Sopenharmony_ci info.nb_device_spec_strings = 1; 323c72fcc34Sopenharmony_ci strings[0] = str; 324c72fcc34Sopenharmony_ci info.device_spec_strings = strings; 325c72fcc34Sopenharmony_ci } 326c72fcc34Sopenharmony_ci 327c72fcc34Sopenharmony_ci // Set common options. 328c72fcc34Sopenharmony_ci options.sample_rate = frames_per_second; 329c72fcc34Sopenharmony_ci options.period_size = state->frames_per_period; 330c72fcc34Sopenharmony_ci options.nb_buffers = state->periods_per_buffer; 331c72fcc34Sopenharmony_ci options.realtime = !!(state->sched_priority > 0); 332c72fcc34Sopenharmony_ci options.packetizer_priority = state->sched_priority; 333c72fcc34Sopenharmony_ci options.slave_mode = state->slave_mode; 334c72fcc34Sopenharmony_ci options.snoop_mode = state->snoop_mode; 335c72fcc34Sopenharmony_ci options.verbose = xfer->verbose; 336c72fcc34Sopenharmony_ci 337c72fcc34Sopenharmony_ci state->handle = ffado_streaming_init(info, options); 338c72fcc34Sopenharmony_ci if (state->handle == NULL) 339c72fcc34Sopenharmony_ci return -EINVAL; 340c72fcc34Sopenharmony_ci 341c72fcc34Sopenharmony_ci return 0; 342c72fcc34Sopenharmony_ci} 343c72fcc34Sopenharmony_ci 344c72fcc34Sopenharmony_cistatic int enable_mbla_data_ch(struct libffado_state *state, 345c72fcc34Sopenharmony_ci unsigned int *samples_per_frame) 346c72fcc34Sopenharmony_ci{ 347c72fcc34Sopenharmony_ci int (*func_type)(ffado_device_t *handle, int pos); 348c72fcc34Sopenharmony_ci int (*func_onoff)(ffado_device_t *handle, int pos, int on); 349c72fcc34Sopenharmony_ci int count; 350c72fcc34Sopenharmony_ci int ch; 351c72fcc34Sopenharmony_ci 352c72fcc34Sopenharmony_ci if (state->direction == FFADO_CAPTURE) { 353c72fcc34Sopenharmony_ci func_type = ffado_streaming_get_capture_stream_type; 354c72fcc34Sopenharmony_ci func_onoff = ffado_streaming_capture_stream_onoff; 355c72fcc34Sopenharmony_ci count = ffado_streaming_get_nb_capture_streams(state->handle); 356c72fcc34Sopenharmony_ci } else { 357c72fcc34Sopenharmony_ci func_type = ffado_streaming_get_playback_stream_type; 358c72fcc34Sopenharmony_ci func_onoff = ffado_streaming_playback_stream_onoff; 359c72fcc34Sopenharmony_ci count = ffado_streaming_get_nb_playback_streams(state->handle); 360c72fcc34Sopenharmony_ci } 361c72fcc34Sopenharmony_ci if (count <= 0) 362c72fcc34Sopenharmony_ci return -EIO; 363c72fcc34Sopenharmony_ci 364c72fcc34Sopenharmony_ci state->data_ch_map = calloc(count, sizeof(*state->data_ch_map)); 365c72fcc34Sopenharmony_ci if (state->data_ch_map == NULL) 366c72fcc34Sopenharmony_ci return -ENOMEM; 367c72fcc34Sopenharmony_ci state->data_ch_count = count; 368c72fcc34Sopenharmony_ci 369c72fcc34Sopenharmony_ci // When a data ch is off, data in the ch is truncated. This helps to 370c72fcc34Sopenharmony_ci // align PCM frames in interleaved order. 371c72fcc34Sopenharmony_ci *samples_per_frame = 0; 372c72fcc34Sopenharmony_ci for (ch = 0; ch < count; ++ch) { 373c72fcc34Sopenharmony_ci int on; 374c72fcc34Sopenharmony_ci 375c72fcc34Sopenharmony_ci state->data_ch_map[ch] = func_type(state->handle, ch); 376c72fcc34Sopenharmony_ci 377c72fcc34Sopenharmony_ci on = !!(state->data_ch_map[ch] == ffado_stream_type_audio); 378c72fcc34Sopenharmony_ci if (func_onoff(state->handle, ch, on)) 379c72fcc34Sopenharmony_ci return -EIO; 380c72fcc34Sopenharmony_ci if (on) 381c72fcc34Sopenharmony_ci ++(*samples_per_frame); 382c72fcc34Sopenharmony_ci } 383c72fcc34Sopenharmony_ci 384c72fcc34Sopenharmony_ci return 0; 385c72fcc34Sopenharmony_ci} 386c72fcc34Sopenharmony_ci 387c72fcc34Sopenharmony_cistatic int xfer_libffado_pre_process(struct xfer_context *xfer, 388c72fcc34Sopenharmony_ci snd_pcm_format_t *format, 389c72fcc34Sopenharmony_ci unsigned int *samples_per_frame, 390c72fcc34Sopenharmony_ci unsigned int *frames_per_second, 391c72fcc34Sopenharmony_ci snd_pcm_access_t *access, 392c72fcc34Sopenharmony_ci snd_pcm_uframes_t *frames_per_buffer) 393c72fcc34Sopenharmony_ci{ 394c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 395c72fcc34Sopenharmony_ci unsigned int channels; 396c72fcc34Sopenharmony_ci int err; 397c72fcc34Sopenharmony_ci 398c72fcc34Sopenharmony_ci // Supported format of sample is 24 bit multi bit linear audio in 399c72fcc34Sopenharmony_ci // AM824 format or the others. 400c72fcc34Sopenharmony_ci if (state->direction == FFADO_CAPTURE) { 401c72fcc34Sopenharmony_ci if (*format == SND_PCM_FORMAT_UNKNOWN) 402c72fcc34Sopenharmony_ci *format = SND_PCM_FORMAT_S24; 403c72fcc34Sopenharmony_ci } 404c72fcc34Sopenharmony_ci if (*format != SND_PCM_FORMAT_S24) { 405c72fcc34Sopenharmony_ci fprintf(stderr, 406c72fcc34Sopenharmony_ci "A libffado backend supports S24 only.\n"); 407c72fcc34Sopenharmony_ci return -EINVAL; 408c72fcc34Sopenharmony_ci } 409c72fcc34Sopenharmony_ci 410c72fcc34Sopenharmony_ci // The backend requires the number of frames per second for its 411c72fcc34Sopenharmony_ci // initialization. 412c72fcc34Sopenharmony_ci if (state->direction == FFADO_CAPTURE) { 413c72fcc34Sopenharmony_ci if (*frames_per_second == 0) 414c72fcc34Sopenharmony_ci *frames_per_second = 48000; 415c72fcc34Sopenharmony_ci } 416c72fcc34Sopenharmony_ci if (*frames_per_second < 32000 || *frames_per_second > 192000) { 417c72fcc34Sopenharmony_ci fprintf(stderr, 418c72fcc34Sopenharmony_ci "A libffado backend supports sampling rate between " 419c72fcc34Sopenharmony_ci "32000 and 192000, discretely.\n"); 420c72fcc34Sopenharmony_ci return -EINVAL; 421c72fcc34Sopenharmony_ci } 422c72fcc34Sopenharmony_ci 423c72fcc34Sopenharmony_ci err = open_handle(xfer, *frames_per_second); 424c72fcc34Sopenharmony_ci if (err < 0) 425c72fcc34Sopenharmony_ci return err; 426c72fcc34Sopenharmony_ci 427c72fcc34Sopenharmony_ci if (ffado_streaming_set_audio_datatype(state->handle, 428c72fcc34Sopenharmony_ci ffado_audio_datatype_int24)) 429c72fcc34Sopenharmony_ci return -EINVAL; 430c72fcc34Sopenharmony_ci 431c72fcc34Sopenharmony_ci // Decide buffer layout. 432c72fcc34Sopenharmony_ci err = enable_mbla_data_ch(state, &channels); 433c72fcc34Sopenharmony_ci if (err < 0) 434c72fcc34Sopenharmony_ci return err; 435c72fcc34Sopenharmony_ci 436c72fcc34Sopenharmony_ci // This backend doesn't support resampling. 437c72fcc34Sopenharmony_ci if (state->direction == FFADO_CAPTURE) { 438c72fcc34Sopenharmony_ci if (*samples_per_frame == 0) 439c72fcc34Sopenharmony_ci *samples_per_frame = channels; 440c72fcc34Sopenharmony_ci } 441c72fcc34Sopenharmony_ci if (*samples_per_frame != channels) { 442c72fcc34Sopenharmony_ci fprintf(stderr, 443c72fcc34Sopenharmony_ci "The number of samples per frame should be %u.\n", 444c72fcc34Sopenharmony_ci channels); 445c72fcc34Sopenharmony_ci return -EINVAL; 446c72fcc34Sopenharmony_ci } 447c72fcc34Sopenharmony_ci 448c72fcc34Sopenharmony_ci // A buffer has interleaved-aligned PCM frames. 449c72fcc34Sopenharmony_ci *access = SND_PCM_ACCESS_RW_INTERLEAVED; 450c72fcc34Sopenharmony_ci *frames_per_buffer = 451c72fcc34Sopenharmony_ci state->frames_per_period * state->periods_per_buffer; 452c72fcc34Sopenharmony_ci 453c72fcc34Sopenharmony_ci // Use cache for double number of frames per period. 454c72fcc34Sopenharmony_ci err = frame_cache_init(&state->cache, *access, 455c72fcc34Sopenharmony_ci snd_pcm_format_physical_width(*format) / 8, 456c72fcc34Sopenharmony_ci *samples_per_frame, state->frames_per_period * 2); 457c72fcc34Sopenharmony_ci if (err < 0) 458c72fcc34Sopenharmony_ci return err; 459c72fcc34Sopenharmony_ci 460c72fcc34Sopenharmony_ci if (state->direction == FFADO_CAPTURE) 461c72fcc34Sopenharmony_ci state->process_frames = r_process_frames; 462c72fcc34Sopenharmony_ci else 463c72fcc34Sopenharmony_ci state->process_frames = w_process_frames; 464c72fcc34Sopenharmony_ci 465c72fcc34Sopenharmony_ci if (ffado_streaming_prepare(state->handle)) 466c72fcc34Sopenharmony_ci return -EIO; 467c72fcc34Sopenharmony_ci 468c72fcc34Sopenharmony_ci if (ffado_streaming_start(state->handle)) 469c72fcc34Sopenharmony_ci return -EIO; 470c72fcc34Sopenharmony_ci 471c72fcc34Sopenharmony_ci return 0; 472c72fcc34Sopenharmony_ci} 473c72fcc34Sopenharmony_ci 474c72fcc34Sopenharmony_cistatic int xfer_libffado_process_frames(struct xfer_context *xfer, 475c72fcc34Sopenharmony_ci unsigned int *frame_count, 476c72fcc34Sopenharmony_ci struct mapper_context *mapper, 477c72fcc34Sopenharmony_ci struct container_context *cntrs) 478c72fcc34Sopenharmony_ci{ 479c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 480c72fcc34Sopenharmony_ci ffado_wait_response res; 481c72fcc34Sopenharmony_ci int err; 482c72fcc34Sopenharmony_ci 483c72fcc34Sopenharmony_ci res = ffado_streaming_wait(state->handle); 484c72fcc34Sopenharmony_ci if (res == ffado_wait_shutdown || res == ffado_wait_error) { 485c72fcc34Sopenharmony_ci err = -EIO; 486c72fcc34Sopenharmony_ci } else if (res == ffado_wait_xrun) { 487c72fcc34Sopenharmony_ci // No way to recover in this backend. 488c72fcc34Sopenharmony_ci err = -EPIPE; 489c72fcc34Sopenharmony_ci } else if (res == ffado_wait_ok) { 490c72fcc34Sopenharmony_ci err = state->process_frames(xfer, frame_count, mapper, cntrs); 491c72fcc34Sopenharmony_ci } else { 492c72fcc34Sopenharmony_ci err = -ENXIO; 493c72fcc34Sopenharmony_ci } 494c72fcc34Sopenharmony_ci 495c72fcc34Sopenharmony_ci if (err < 0) 496c72fcc34Sopenharmony_ci *frame_count = 0; 497c72fcc34Sopenharmony_ci 498c72fcc34Sopenharmony_ci return err; 499c72fcc34Sopenharmony_ci} 500c72fcc34Sopenharmony_ci 501c72fcc34Sopenharmony_cistatic void xfer_libffado_pause(struct xfer_context *xfer, bool enable) 502c72fcc34Sopenharmony_ci{ 503c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 504c72fcc34Sopenharmony_ci 505c72fcc34Sopenharmony_ci // This is an emergency avoidance because this backend doesn't support 506c72fcc34Sopenharmony_ci // suspend/aresume operation. 507c72fcc34Sopenharmony_ci if (enable) { 508c72fcc34Sopenharmony_ci ffado_streaming_stop(state->handle); 509c72fcc34Sopenharmony_ci ffado_streaming_finish(state->handle); 510c72fcc34Sopenharmony_ci exit(EXIT_FAILURE); 511c72fcc34Sopenharmony_ci } 512c72fcc34Sopenharmony_ci} 513c72fcc34Sopenharmony_ci 514c72fcc34Sopenharmony_cistatic void xfer_libffado_post_process(struct xfer_context *xfer) 515c72fcc34Sopenharmony_ci{ 516c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 517c72fcc34Sopenharmony_ci 518c72fcc34Sopenharmony_ci if (state->handle != NULL) { 519c72fcc34Sopenharmony_ci ffado_streaming_stop(state->handle); 520c72fcc34Sopenharmony_ci ffado_streaming_finish(state->handle); 521c72fcc34Sopenharmony_ci } 522c72fcc34Sopenharmony_ci 523c72fcc34Sopenharmony_ci frame_cache_destroy(&state->cache); 524c72fcc34Sopenharmony_ci free(state->data_ch_map); 525c72fcc34Sopenharmony_ci state->data_ch_map = NULL; 526c72fcc34Sopenharmony_ci} 527c72fcc34Sopenharmony_ci 528c72fcc34Sopenharmony_cistatic void xfer_libffado_destroy(struct xfer_context *xfer) 529c72fcc34Sopenharmony_ci{ 530c72fcc34Sopenharmony_ci struct libffado_state *state = xfer->private_data; 531c72fcc34Sopenharmony_ci 532c72fcc34Sopenharmony_ci free(state->port_literal); 533c72fcc34Sopenharmony_ci free(state->node_literal); 534c72fcc34Sopenharmony_ci free(state->guid_literal); 535c72fcc34Sopenharmony_ci state->port_literal = NULL; 536c72fcc34Sopenharmony_ci state->node_literal = NULL; 537c72fcc34Sopenharmony_ci state->guid_literal = NULL; 538c72fcc34Sopenharmony_ci} 539c72fcc34Sopenharmony_ci 540c72fcc34Sopenharmony_cistatic void xfer_libffado_help(struct xfer_context *xfer) 541c72fcc34Sopenharmony_ci{ 542c72fcc34Sopenharmony_ci printf( 543c72fcc34Sopenharmony_ci" -p, --port decimal ID of port to decide 1394 OHCI controller for communication on IEEE 1394 bus\n" 544c72fcc34Sopenharmony_ci" -n, --node decimal ID of node to decide unit on IEEE 1394 bus for transmission of audio data frame\n" 545c72fcc34Sopenharmony_ci" -g, --guid hexadecimal ID for node on IEEE 1394 bus for transmission of audio data frame\n" 546c72fcc34Sopenharmony_ci" --frames-per-period the number of audio data frame to handle one operation (frame unit)\n" 547c72fcc34Sopenharmony_ci" --periods-per-bufer the number of periods in intermediate buffer between libffado (frame unit)\n" 548c72fcc34Sopenharmony_ci" --slave receive frames from the other Linux system on IEEE 1394 bus running with libffado.\n" 549c72fcc34Sopenharmony_ci" --snoop receive frames on packets of all isochronous channels.\n" 550c72fcc34Sopenharmony_ci" --sched-priority set SCHED_FIFO with given priority. see RLIMIT_RTPRIO in getrlimit(2).\n" 551c72fcc34Sopenharmony_ci ); 552c72fcc34Sopenharmony_ci} 553c72fcc34Sopenharmony_ci 554c72fcc34Sopenharmony_ciconst struct xfer_data xfer_libffado = { 555c72fcc34Sopenharmony_ci .s_opts = S_OPTS, 556c72fcc34Sopenharmony_ci .l_opts = l_opts, 557c72fcc34Sopenharmony_ci .l_opts_count = ARRAY_SIZE(l_opts), 558c72fcc34Sopenharmony_ci .ops = { 559c72fcc34Sopenharmony_ci .init = xfer_libffado_init, 560c72fcc34Sopenharmony_ci .parse_opt = xfer_libffado_parse_opt, 561c72fcc34Sopenharmony_ci .validate_opts = xfer_libffado_validate_opts, 562c72fcc34Sopenharmony_ci .pre_process = xfer_libffado_pre_process, 563c72fcc34Sopenharmony_ci .process_frames = xfer_libffado_process_frames, 564c72fcc34Sopenharmony_ci .pause = xfer_libffado_pause, 565c72fcc34Sopenharmony_ci .post_process = xfer_libffado_post_process, 566c72fcc34Sopenharmony_ci .destroy = xfer_libffado_destroy, 567c72fcc34Sopenharmony_ci .help = xfer_libffado_help, 568c72fcc34Sopenharmony_ci }, 569c72fcc34Sopenharmony_ci .private_size = sizeof(struct libffado_state), 570c72fcc34Sopenharmony_ci}; 571