Blame axfer/xfer-libffado.c

Packit Service a9274b
// SPDX-License-Identifier: GPL-2.0
Packit Service a9274b
//
Packit Service a9274b
// xfer-libffado.c - receive/transmit frames by libffado.
Packit Service a9274b
//
Packit Service a9274b
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
Packit Service a9274b
//
Packit Service a9274b
// Licensed under the terms of the GNU General Public License, version 2.
Packit Service a9274b
Packit Service a9274b
#include "xfer.h"
Packit Service a9274b
#include "misc.h"
Packit Service a9274b
Packit Service a9274b
#include "frame-cache.h"
Packit Service a9274b
Packit Service a9274b
#include <stdio.h>
Packit Service a9274b
#include <sched.h>
Packit Service a9274b
Packit Service a9274b
#include <libffado/ffado.h>
Packit Service a9274b
Packit Service a9274b
struct libffado_state {
Packit Service a9274b
	ffado_device_t *handle;
Packit Service a9274b
	enum ffado_direction direction;
Packit Service a9274b
Packit Service a9274b
	char *port_literal;
Packit Service a9274b
	char *node_literal;
Packit Service a9274b
	char *guid_literal;
Packit Service a9274b
	unsigned int frames_per_period;
Packit Service a9274b
	unsigned int periods_per_buffer;
Packit Service a9274b
	unsigned int sched_priority;
Packit Service a9274b
	bool slave_mode:1;
Packit Service a9274b
	bool snoop_mode:1;
Packit Service a9274b
Packit Service a9274b
	unsigned int data_ch_count;
Packit Service a9274b
	ffado_streaming_stream_type *data_ch_map;
Packit Service a9274b
Packit Service a9274b
	int (*process_frames)(struct xfer_context *xfer,
Packit Service a9274b
			      unsigned int *frame_count,
Packit Service a9274b
			      struct mapper_context *mapper,
Packit Service a9274b
			      struct container_context *cntrs);
Packit Service a9274b
Packit Service a9274b
	struct frame_cache cache;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
enum no_short_opts {
Packit Service a9274b
	OPT_FRAMES_PER_PERIOD = 200,
Packit Service a9274b
	OPT_PERIODS_PER_BUFFER,
Packit Service a9274b
	OPT_SLAVE_MODE,
Packit Service a9274b
	OPT_SNOOP_MODE,
Packit Service a9274b
	OPT_SCHED_PRIORITY,
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
#define S_OPTS	"p:n:g:"
Packit Service a9274b
static const struct option l_opts[] = {
Packit Service a9274b
	{"port",		1, 0, 'p'},
Packit Service a9274b
	{"node",		1, 0, 'n'},
Packit Service a9274b
	{"guid",		1, 0, 'g'},
Packit Service a9274b
	{"frames-per-period",	1, 0, OPT_FRAMES_PER_PERIOD},
Packit Service a9274b
	{"periods-per-buffer",	1, 0, OPT_PERIODS_PER_BUFFER},
Packit Service a9274b
	{"slave",		0, 0, OPT_SLAVE_MODE},
Packit Service a9274b
	{"snoop",		0, 0, OPT_SNOOP_MODE},
Packit Service a9274b
	{"sched-priority",	1, 0, OPT_SCHED_PRIORITY}, // to SCHED_FIFO
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static int xfer_libffado_init(struct xfer_context *xfer,
Packit Service a9274b
			       snd_pcm_stream_t direction)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
Packit Service a9274b
	if (direction == SND_PCM_STREAM_CAPTURE)
Packit Service a9274b
		state->direction = FFADO_CAPTURE;
Packit Service a9274b
	else if (direction == SND_PCM_STREAM_PLAYBACK)
Packit Service a9274b
		state->direction = FFADO_PLAYBACK;
Packit Service a9274b
	else
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int xfer_libffado_parse_opt(struct xfer_context *xfer, int key,
Packit Service a9274b
				    const char *optarg)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	if (key == 'p')
Packit Service a9274b
		state->port_literal = arg_duplicate_string(optarg, &err;;
Packit Service a9274b
	else if (key == 'n')
Packit Service a9274b
		state->node_literal = arg_duplicate_string(optarg, &err;;
Packit Service a9274b
	else if (key == 'g')
Packit Service a9274b
		state->guid_literal = arg_duplicate_string(optarg, &err;;
Packit Service a9274b
	else if (key == OPT_FRAMES_PER_PERIOD)
Packit Service a9274b
		state->frames_per_period = arg_parse_decimal_num(optarg, &err;;
Packit Service a9274b
	else if (key == OPT_PERIODS_PER_BUFFER)
Packit Service a9274b
		state->periods_per_buffer = arg_parse_decimal_num(optarg, &err;;
Packit Service a9274b
	else if (key == OPT_SLAVE_MODE)
Packit Service a9274b
		state->slave_mode = true;
Packit Service a9274b
	else if (key == OPT_SNOOP_MODE)
Packit Service a9274b
		state->snoop_mode = true;
Packit Service a9274b
	else if (key == OPT_SCHED_PRIORITY)
Packit Service a9274b
		state->sched_priority = arg_parse_decimal_num(optarg, &err;;
Packit Service a9274b
	else
Packit Service a9274b
		err = -ENXIO;
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int validate_sched_priority(struct libffado_state *state)
Packit Service a9274b
{
Packit Service a9274b
	int val;
Packit Service a9274b
Packit Service a9274b
	val = sched_get_priority_max(SCHED_FIFO);
Packit Service a9274b
	if (val < 0)
Packit Service a9274b
		return -errno;
Packit Service a9274b
	if (state->sched_priority > val)
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
Packit Service a9274b
	val = sched_get_priority_min(SCHED_FIFO);
Packit Service a9274b
	if (val < 0)
Packit Service a9274b
		return -errno;
Packit Service a9274b
	if (state->sched_priority < val)
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int xfer_libffado_validate_opts(struct xfer_context *xfer)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	if (state->node_literal != NULL) {
Packit Service a9274b
		if (state->port_literal == NULL) {
Packit Service a9274b
			fprintf(stderr,
Packit Service a9274b
				"'n' option should correspond 'p' option.\n");
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (state->port_literal != NULL && state->guid_literal != NULL) {
Packit Service a9274b
		fprintf(stderr,
Packit Service a9274b
			"Neither 'p' option nor 'g' option is available at the "
Packit Service a9274b
			"same time.\n");
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (state->guid_literal != NULL) {
Packit Service a9274b
		if (strstr(state->guid_literal, "0x") != state->guid_literal) {
Packit Service a9274b
			fprintf(stderr,
Packit Service a9274b
				"A value of 'g' option should have '0x' as its "
Packit Service a9274b
				"prefix for hexadecimal number.\n");
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (state->slave_mode && state->snoop_mode) {
Packit Service a9274b
		fprintf(stderr, "Neither slave mode nor snoop mode is available"
Packit Service a9274b
				"at the same time.\n");
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (state->sched_priority > 0) {
Packit Service a9274b
		err = validate_sched_priority(state);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (state->frames_per_period == 0)
Packit Service a9274b
		state->frames_per_period = 512;
Packit Service a9274b
	if (state->periods_per_buffer == 0)
Packit Service a9274b
		state->periods_per_buffer = 2;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int r_process_frames(struct xfer_context *xfer,
Packit Service a9274b
			    unsigned int *frame_count,
Packit Service a9274b
			    struct mapper_context *mapper,
Packit Service a9274b
			    struct container_context *cntrs)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
	unsigned int avail_count;
Packit Service a9274b
	unsigned int bytes_per_frame;
Packit Service a9274b
	unsigned int consumed_count;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	// Trim up to expected frame count.
Packit Service a9274b
	avail_count = state->frames_per_period;
Packit Service a9274b
	if (*frame_count < avail_count)
Packit Service a9274b
		avail_count = *frame_count;
Packit Service a9274b
Packit Service a9274b
	// Cache required amount of frames.
Packit Service a9274b
	if (avail_count > frame_cache_get_count(&state->cache)) {
Packit Service a9274b
		int ch;
Packit Service a9274b
		int pos;
Packit Service a9274b
Packit Service a9274b
		// Register buffers.
Packit Service a9274b
		pos = 0;
Packit Service a9274b
		bytes_per_frame = state->cache.bytes_per_sample *
Packit Service a9274b
				  state->cache.samples_per_frame;
Packit Service a9274b
		for (ch = 0; ch < state->data_ch_count; ++ch) {
Packit Service a9274b
			char *buf;
Packit Service a9274b
Packit Service a9274b
			if (state->data_ch_map[ch] != ffado_stream_type_audio)
Packit Service a9274b
				continue;
Packit Service a9274b
Packit Service a9274b
			buf = state->cache.buf_ptr;
Packit Service a9274b
			buf += ch * bytes_per_frame;
Packit Service a9274b
			if (ffado_streaming_set_capture_stream_buffer(state->handle,
Packit Service a9274b
								      ch, buf))
Packit Service a9274b
				return -EIO;
Packit Service a9274b
			++pos;
Packit Service a9274b
		}
Packit Service a9274b
Packit Service a9274b
		assert(pos == xfer->samples_per_frame);
Packit Service a9274b
Packit Service a9274b
		// Move data to the buffer from intermediate buffer.
Packit Service a9274b
		if (!ffado_streaming_transfer_buffers(state->handle))
Packit Service a9274b
			return -EIO;
Packit Service a9274b
Packit Service a9274b
		frame_cache_increase_count(&state->cache,
Packit Service a9274b
					   state->frames_per_period);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Write out to file descriptors.
Packit Service a9274b
	consumed_count = frame_cache_get_count(&state->cache);
Packit Service a9274b
	err = mapper_context_process_frames(mapper, state->cache.buf,
Packit Service a9274b
					    &consumed_count, cntrs);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	frame_cache_reduce(&state->cache, consumed_count);
Packit Service a9274b
Packit Service a9274b
	*frame_count = consumed_count;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int w_process_frames(struct xfer_context *xfer,
Packit Service a9274b
			    unsigned int *frame_count,
Packit Service a9274b
			    struct mapper_context *mapper,
Packit Service a9274b
			    struct container_context *cntrs)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
	unsigned int avail_count;
Packit Service a9274b
	int pos;
Packit Service a9274b
	int ch;
Packit Service a9274b
	unsigned int bytes_per_frame;
Packit Service a9274b
	unsigned int consumed_count;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	// Trim up to expected frame_count.
Packit Service a9274b
	avail_count = state->frames_per_period;
Packit Service a9274b
	if (*frame_count < avail_count)
Packit Service a9274b
		avail_count = *frame_count;
Packit Service a9274b
Packit Service a9274b
	// Cache required amount of frames.
Packit Service a9274b
	if (avail_count > frame_cache_get_count(&state->cache)) {
Packit Service a9274b
		avail_count -= frame_cache_get_count(&state->cache);
Packit Service a9274b
Packit Service a9274b
		err = mapper_context_process_frames(mapper, state->cache.buf_ptr,
Packit Service a9274b
						    &avail_count, cntrs);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
		frame_cache_increase_count(&state->cache, avail_count);
Packit Service a9274b
		avail_count = state->cache.remained_count;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Register buffers.
Packit Service a9274b
	pos = 0;
Packit Service a9274b
	bytes_per_frame = state->cache.bytes_per_sample *
Packit Service a9274b
			  state->cache.samples_per_frame;
Packit Service a9274b
	for (ch = 0; ch < state->data_ch_count; ++ch) {
Packit Service a9274b
		char *buf;
Packit Service a9274b
Packit Service a9274b
		if (state->data_ch_map[ch] != ffado_stream_type_audio)
Packit Service a9274b
			continue;
Packit Service a9274b
Packit Service a9274b
		buf = state->cache.buf;
Packit Service a9274b
		buf += bytes_per_frame;
Packit Service a9274b
		if (ffado_streaming_set_playback_stream_buffer(state->handle,
Packit Service a9274b
								ch, buf))
Packit Service a9274b
			return -EIO;
Packit Service a9274b
		++pos;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	assert(pos == xfer->samples_per_frame);
Packit Service a9274b
Packit Service a9274b
	// Move data on the buffer for transmission.
Packit Service a9274b
	if (!ffado_streaming_transfer_buffers(state->handle))
Packit Service a9274b
		return -EIO;
Packit Service a9274b
	consumed_count = state->frames_per_period;
Packit Service a9274b
Packit Service a9274b
	frame_cache_reduce(&state->cache, consumed_count);
Packit Service a9274b
Packit Service a9274b
	*frame_count = consumed_count;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int open_handle(struct xfer_context *xfer,
Packit Service a9274b
		       unsigned int frames_per_second)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
	ffado_options_t options = {0};
Packit Service a9274b
	ffado_device_info_t info = {0};
Packit Service a9274b
Packit Service a9274b
	char str[32] = {0};
Packit Service a9274b
	char *strings[1];
Packit Service a9274b
Packit Service a9274b
	// Set target unit if given.
Packit Service a9274b
	if (state->port_literal != NULL) {
Packit Service a9274b
		if (state->node_literal != NULL) {
Packit Service a9274b
			snprintf(str, sizeof(str), "hw:%s,%s",
Packit Service a9274b
				 state->port_literal, state->node_literal);
Packit Service a9274b
		} else {
Packit Service a9274b
			snprintf(str, sizeof(str), "hw:%s",
Packit Service a9274b
				 state->port_literal);
Packit Service a9274b
		}
Packit Service a9274b
	} else if (state->guid_literal != NULL) {
Packit Service a9274b
		snprintf(str, sizeof(str), "guid:%s", state->guid_literal);
Packit Service a9274b
	}
Packit Service a9274b
	if (str[0] != '\0') {
Packit Service a9274b
		info.nb_device_spec_strings = 1;
Packit Service a9274b
		strings[0] = str;
Packit Service a9274b
		info.device_spec_strings = strings;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Set common options.
Packit Service a9274b
	options.sample_rate = frames_per_second;
Packit Service a9274b
	options.period_size = state->frames_per_period;
Packit Service a9274b
	options.nb_buffers = state->periods_per_buffer;
Packit Service a9274b
	options.realtime = !!(state->sched_priority > 0);
Packit Service a9274b
	options.packetizer_priority = state->sched_priority;
Packit Service a9274b
	options.slave_mode = state->slave_mode;
Packit Service a9274b
	options.snoop_mode = state->snoop_mode;
Packit Service a9274b
	options.verbose = xfer->verbose;
Packit Service a9274b
Packit Service a9274b
	state->handle = ffado_streaming_init(info, options);
Packit Service a9274b
	if (state->handle == NULL)
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int enable_mbla_data_ch(struct libffado_state *state,
Packit Service a9274b
			       unsigned int *samples_per_frame)
Packit Service a9274b
{
Packit Service a9274b
	int (*func_type)(ffado_device_t *handle, int pos);
Packit Service a9274b
	int (*func_onoff)(ffado_device_t *handle, int pos, int on);
Packit Service a9274b
	int count;
Packit Service a9274b
	int ch;
Packit Service a9274b
Packit Service a9274b
	if (state->direction == FFADO_CAPTURE) {
Packit Service a9274b
		func_type = ffado_streaming_get_capture_stream_type;
Packit Service a9274b
		func_onoff = ffado_streaming_capture_stream_onoff;
Packit Service a9274b
		count = ffado_streaming_get_nb_capture_streams(state->handle);
Packit Service a9274b
	} else {
Packit Service a9274b
		func_type = ffado_streaming_get_playback_stream_type;
Packit Service a9274b
		func_onoff = ffado_streaming_playback_stream_onoff;
Packit Service a9274b
		count = ffado_streaming_get_nb_playback_streams(state->handle);
Packit Service a9274b
	}
Packit Service a9274b
	if (count <= 0)
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	state->data_ch_map = calloc(count, sizeof(*state->data_ch_map));
Packit Service a9274b
	if (state->data_ch_map == NULL)
Packit Service a9274b
		return -ENOMEM;
Packit Service a9274b
	state->data_ch_count = count;
Packit Service a9274b
Packit Service a9274b
	// When a data ch is off, data in the ch is truncated. This helps to
Packit Service a9274b
	// align PCM frames in interleaved order.
Packit Service a9274b
	*samples_per_frame = 0;
Packit Service a9274b
	for (ch = 0; ch < count; ++ch) {
Packit Service a9274b
		int on;
Packit Service a9274b
Packit Service a9274b
		state->data_ch_map[ch] = func_type(state->handle, ch);
Packit Service a9274b
Packit Service a9274b
		on = !!(state->data_ch_map[ch] == ffado_stream_type_audio);
Packit Service a9274b
		if (func_onoff(state->handle, ch, on))
Packit Service a9274b
			return -EIO;
Packit Service a9274b
		if (on)
Packit Service a9274b
			++(*samples_per_frame);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int xfer_libffado_pre_process(struct xfer_context *xfer,
Packit Service a9274b
				     snd_pcm_format_t *format,
Packit Service a9274b
				     unsigned int *samples_per_frame,
Packit Service a9274b
				     unsigned int *frames_per_second,
Packit Service a9274b
				     snd_pcm_access_t *access,
Packit Service a9274b
				     snd_pcm_uframes_t *frames_per_buffer)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
	unsigned int channels;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	// Supported format of sample is 24 bit multi bit linear audio in
Packit Service a9274b
	// AM824 format or the others.
Packit Service a9274b
	if (state->direction == FFADO_CAPTURE) {
Packit Service a9274b
		if (*format == SND_PCM_FORMAT_UNKNOWN)
Packit Service a9274b
			*format = SND_PCM_FORMAT_S24;
Packit Service a9274b
	}
Packit Service a9274b
	if (*format != SND_PCM_FORMAT_S24) {
Packit Service a9274b
		fprintf(stderr,
Packit Service a9274b
			"A libffado backend supports S24 only.\n");
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// The backend requires the number of frames per second for its
Packit Service a9274b
	// initialization.
Packit Service a9274b
	if (state->direction == FFADO_CAPTURE) {
Packit Service a9274b
		if (*frames_per_second == 0)
Packit Service a9274b
			*frames_per_second = 48000;
Packit Service a9274b
	}
Packit Service a9274b
	if (*frames_per_second < 32000 || *frames_per_second > 192000) {
Packit Service a9274b
		fprintf(stderr,
Packit Service a9274b
			"A libffado backend supports sampling rate between "
Packit Service a9274b
			"32000 and 192000, discretely.\n");
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	err = open_handle(xfer, *frames_per_second);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	if (ffado_streaming_set_audio_datatype(state->handle,
Packit Service a9274b
					       ffado_audio_datatype_int24))
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
Packit Service a9274b
	// Decide buffer layout.
Packit Service a9274b
	err = enable_mbla_data_ch(state, &channels);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	// This backend doesn't support resampling.
Packit Service a9274b
	if (state->direction == FFADO_CAPTURE) {
Packit Service a9274b
		if (*samples_per_frame == 0)
Packit Service a9274b
			*samples_per_frame = channels;
Packit Service a9274b
	}
Packit Service a9274b
	if (*samples_per_frame != channels) {
Packit Service a9274b
		fprintf(stderr,
Packit Service a9274b
			"The number of samples per frame should be %u.\n",
Packit Service a9274b
			channels);
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// A buffer has interleaved-aligned PCM frames.
Packit Service a9274b
	*access = SND_PCM_ACCESS_RW_INTERLEAVED;
Packit Service a9274b
	*frames_per_buffer =
Packit Service a9274b
			state->frames_per_period * state->periods_per_buffer;
Packit Service a9274b
Packit Service a9274b
	// Use cache for double number of frames per period.
Packit Service a9274b
	err = frame_cache_init(&state->cache, *access,
Packit Service a9274b
			       snd_pcm_format_physical_width(*format) / 8,
Packit Service a9274b
			       *samples_per_frame, state->frames_per_period * 2);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	if (state->direction == FFADO_CAPTURE)
Packit Service a9274b
		state->process_frames = r_process_frames;
Packit Service a9274b
	else
Packit Service a9274b
		state->process_frames = w_process_frames;
Packit Service a9274b
Packit Service a9274b
	if (ffado_streaming_prepare(state->handle))
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	if (ffado_streaming_start(state->handle))
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int xfer_libffado_process_frames(struct xfer_context *xfer,
Packit Service a9274b
					unsigned int *frame_count,
Packit Service a9274b
					struct mapper_context *mapper,
Packit Service a9274b
					struct container_context *cntrs)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
	ffado_wait_response res;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	res = ffado_streaming_wait(state->handle);
Packit Service a9274b
	if (res == ffado_wait_shutdown || res == ffado_wait_error) {
Packit Service a9274b
		err = -EIO;
Packit Service a9274b
	} else if (res == ffado_wait_xrun) {
Packit Service a9274b
		// No way to recover in this backend.
Packit Service a9274b
		err = -EPIPE;
Packit Service a9274b
	} else if (res == ffado_wait_ok) {
Packit Service a9274b
		err = state->process_frames(xfer, frame_count, mapper, cntrs);
Packit Service a9274b
	} else {
Packit Service a9274b
		err = -ENXIO;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		*frame_count = 0;
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void xfer_libffado_pause(struct xfer_context *xfer, bool enable)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
Packit Service a9274b
	// This is an emergency avoidance because this backend doesn't support
Packit Service a9274b
	// suspend/aresume operation.
Packit Service a9274b
	if (enable) {
Packit Service a9274b
		ffado_streaming_stop(state->handle);
Packit Service a9274b
		ffado_streaming_finish(state->handle);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void xfer_libffado_post_process(struct xfer_context *xfer)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
Packit Service a9274b
	if (state->handle != NULL) {
Packit Service a9274b
		ffado_streaming_stop(state->handle);
Packit Service a9274b
		ffado_streaming_finish(state->handle);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	frame_cache_destroy(&state->cache);
Packit Service a9274b
	free(state->data_ch_map);
Packit Service a9274b
	state->data_ch_map = NULL;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void xfer_libffado_destroy(struct xfer_context *xfer)
Packit Service a9274b
{
Packit Service a9274b
	struct libffado_state *state = xfer->private_data;
Packit Service a9274b
Packit Service a9274b
	free(state->port_literal);
Packit Service a9274b
	free(state->node_literal);
Packit Service a9274b
	free(state->guid_literal);
Packit Service a9274b
	state->port_literal = NULL;
Packit Service a9274b
	state->node_literal = NULL;
Packit Service a9274b
	state->guid_literal = NULL;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void xfer_libffado_help(struct xfer_context *xfer)
Packit Service a9274b
{
Packit Service a9274b
	printf(
Packit Service a9274b
"      -p, --port           decimal ID of port to decide 1394 OHCI controller for communication on IEEE 1394 bus\n"
Packit Service a9274b
"      -n, --node           decimal ID of node to decide unit on IEEE 1394 bus for transmission of audio data frame\n"
Packit Service a9274b
"      -g, --guid           hexadecimal ID for node on IEEE 1394 bus for transmission of audio data frame\n"
Packit Service a9274b
"      --frames-per-period  the number of audio data frame to handle one operation (frame unit)\n"
Packit Service a9274b
"      --periods-per-bufer  the number of periods in intermediate buffer between libffado (frame unit)\n"
Packit Service a9274b
"      --slave              receive frames from the other Linux system on IEEE 1394 bus running with libffado.\n"
Packit Service a9274b
"      --snoop              receive frames on packets of all isochronous channels.\n"
Packit Service a9274b
"      --sched-priority     set SCHED_FIFO with given priority. see RLIMIT_RTPRIO in getrlimit(2).\n"
Packit Service a9274b
	);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
const struct xfer_data xfer_libffado = {
Packit Service a9274b
	.s_opts = S_OPTS,
Packit Service a9274b
	.l_opts = l_opts,
Packit Service a9274b
	.l_opts_count = ARRAY_SIZE(l_opts),
Packit Service a9274b
	.ops = {
Packit Service a9274b
		.init		= xfer_libffado_init,
Packit Service a9274b
		.parse_opt	= xfer_libffado_parse_opt,
Packit Service a9274b
		.validate_opts	= xfer_libffado_validate_opts,
Packit Service a9274b
		.pre_process	= xfer_libffado_pre_process,
Packit Service a9274b
		.process_frames	= xfer_libffado_process_frames,
Packit Service a9274b
		.pause		= xfer_libffado_pause,
Packit Service a9274b
		.post_process	= xfer_libffado_post_process,
Packit Service a9274b
		.destroy	= xfer_libffado_destroy,
Packit Service a9274b
		.help		= xfer_libffado_help,
Packit Service a9274b
	},
Packit Service a9274b
	.private_size = sizeof(struct libffado_state),
Packit Service a9274b
};