Blame axfer/xfer-libasound.c

Packit 229ac0
// SPDX-License-Identifier: GPL-2.0
Packit 229ac0
//
Packit 229ac0
// xfer-libasound.c - receive/transmit frames by alsa-lib.
Packit 229ac0
//
Packit 229ac0
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
Packit 229ac0
//
Packit 229ac0
// Licensed under the terms of the GNU General Public License, version 2.
Packit 229ac0
Packit 229ac0
#include "xfer-libasound.h"
Packit 229ac0
#include "misc.h"
Packit 229ac0
Packit 229ac0
static const char *const sched_model_labels [] = {
Packit 229ac0
	[SCHED_MODEL_IRQ] = "irq",
Packit 229ac0
	[SCHED_MODEL_TIMER] = "timer",
Packit 229ac0
};
Packit 229ac0
Packit 229ac0
enum no_short_opts {
Packit 229ac0
        // 200 or later belong to non us-ascii character set.
Packit 229ac0
	OPT_PERIOD_SIZE = 200,
Packit 229ac0
	OPT_BUFFER_SIZE,
Packit 229ac0
	OPT_WAITER_TYPE,
Packit 229ac0
	OPT_SCHED_MODEL,
Packit 229ac0
	OPT_DISABLE_RESAMPLE,
Packit 229ac0
	OPT_DISABLE_CHANNELS,
Packit 229ac0
	OPT_DISABLE_FORMAT,
Packit 229ac0
	OPT_DISABLE_SOFTVOL,
Packit 229ac0
	OPT_FATAL_ERRORS,
Packit 229ac0
	OPT_TEST_NOWAIT,
Packit 229ac0
	// Obsoleted.
Packit 229ac0
	OPT_TEST_POSITION,
Packit 229ac0
	OPT_TEST_COEF,
Packit 229ac0
};
Packit 229ac0
Packit 229ac0
#define S_OPTS	"D:NMF:B:A:R:T:m:"
Packit 229ac0
static const struct option l_opts[] = {
Packit 229ac0
	{"device",		1, 0, 'D'},
Packit 229ac0
	{"nonblock",		0, 0, 'N'},
Packit 229ac0
	{"mmap",		0, 0, 'M'},
Packit 229ac0
	{"period-time",		1, 0, 'F'},
Packit 229ac0
	{"buffer-time",		1, 0, 'B'},
Packit 229ac0
	{"period-size",		1, 0, OPT_PERIOD_SIZE},
Packit 229ac0
	{"buffer-size",		1, 0, OPT_BUFFER_SIZE},
Packit 229ac0
	{"avail-min",		1, 0, 'A'},
Packit 229ac0
	{"start-delay",		1, 0, 'R'},
Packit 229ac0
	{"stop-delay",		1, 0, 'T'},
Packit 229ac0
	{"waiter-type",		1, 0, OPT_WAITER_TYPE},
Packit 229ac0
	{"sched-model",		1, 0, OPT_SCHED_MODEL},
Packit 229ac0
	// For plugins in alsa-lib.
Packit 229ac0
	{"disable-resample",	0, 0, OPT_DISABLE_RESAMPLE},
Packit 229ac0
	{"disable-channels",	0, 0, OPT_DISABLE_CHANNELS},
Packit 229ac0
	{"disable-format",	0, 0, OPT_DISABLE_FORMAT},
Packit 229ac0
	{"disable-softvol",	0, 0, OPT_DISABLE_SOFTVOL},
Packit 229ac0
	// For debugging.
Packit 229ac0
	{"fatal-errors",	0, 0, OPT_FATAL_ERRORS},
Packit 229ac0
	{"test-nowait",		0, 0, OPT_TEST_NOWAIT},
Packit 229ac0
	// Obsoleted.
Packit 229ac0
	{"chmap",		1, 0, 'm'},
Packit 229ac0
	{"test-position",	0, 0, OPT_TEST_POSITION},
Packit 229ac0
	{"test-coef",		1, 0, OPT_TEST_COEF},
Packit 229ac0
};
Packit 229ac0
Packit 229ac0
static int xfer_libasound_init(struct xfer_context *xfer,
Packit 229ac0
			       snd_pcm_stream_t direction)
Packit 229ac0
{
Packit 229ac0
	struct libasound_state *state = xfer->private_data;
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	err = snd_output_stdio_attach(&state->log, stderr, 0);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_hw_params_malloc(&state->hw_params);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	return snd_pcm_sw_params_malloc(&state->sw_params);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int xfer_libasound_parse_opt(struct xfer_context *xfer, int key,
Packit 229ac0
				    const char *optarg)
Packit 229ac0
{
Packit 229ac0
	struct libasound_state *state = xfer->private_data;
Packit 229ac0
	int err = 0;
Packit 229ac0
Packit 229ac0
	if (key == 'D')
Packit 229ac0
		state->node_literal = arg_duplicate_string(optarg, &err;;
Packit 229ac0
	else if (key == 'N')
Packit 229ac0
		state->nonblock = true;
Packit 229ac0
	else if (key == 'M')
Packit 229ac0
		state->mmap = true;
Packit 229ac0
	else if (key == 'F')
Packit 229ac0
		state->msec_per_period = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
	else if (key == 'B')
Packit 229ac0
		state->msec_per_buffer = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
	else if (key == OPT_PERIOD_SIZE)
Packit 229ac0
		state->frames_per_period = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
	else if (key == OPT_BUFFER_SIZE)
Packit 229ac0
		state->frames_per_buffer = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
	else if (key == 'A')
Packit 229ac0
		state->msec_for_avail_min = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
	else if (key == 'R')
Packit 229ac0
		state->msec_for_start_threshold = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
	else if (key == 'T')
Packit 229ac0
		state->msec_for_stop_threshold = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
	else if (key == OPT_WAITER_TYPE)
Packit 229ac0
		state->waiter_type_literal = arg_duplicate_string(optarg, &err;;
Packit 229ac0
	else if (key == OPT_SCHED_MODEL)
Packit 229ac0
		state->sched_model_literal = arg_duplicate_string(optarg, &err;;
Packit 229ac0
	else if (key == OPT_DISABLE_RESAMPLE)
Packit 229ac0
		state->no_auto_resample = true;
Packit 229ac0
	else if (key == OPT_DISABLE_CHANNELS)
Packit 229ac0
		state->no_auto_channels = true;
Packit 229ac0
	else if (key == OPT_DISABLE_FORMAT)
Packit 229ac0
		state->no_auto_format = true;
Packit 229ac0
	else if (key == OPT_DISABLE_SOFTVOL)
Packit 229ac0
		state->no_softvol = true;
Packit 229ac0
	else if (key == 'm' ||
Packit 229ac0
		 key == OPT_TEST_POSITION ||
Packit 229ac0
		 key == OPT_TEST_COEF)
Packit 229ac0
		err = -EINVAL;
Packit 229ac0
	else if (key == OPT_FATAL_ERRORS)
Packit 229ac0
		state->finish_at_xrun = true;
Packit 229ac0
	else if (key == OPT_TEST_NOWAIT)
Packit 229ac0
		state->test_nowait = true;
Packit 229ac0
	else
Packit 229ac0
		err = -ENXIO;
Packit 229ac0
Packit 229ac0
	return err;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
int xfer_libasound_validate_opts(struct xfer_context *xfer)
Packit 229ac0
{
Packit 229ac0
	struct libasound_state *state = xfer->private_data;
Packit 229ac0
	int err = 0;
Packit 229ac0
Packit 229ac0
	state->verbose = xfer->verbose > 1;
Packit 229ac0
Packit 229ac0
	if (state->node_literal == NULL) {
Packit 229ac0
		state->node_literal = strdup("default");
Packit 229ac0
		if (state->node_literal == NULL)
Packit 229ac0
			return -ENOMEM;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (state->mmap && state->nonblock) {
Packit 229ac0
		fprintf(stderr,
Packit 229ac0
			"An option for mmap operation should not be used with "
Packit 229ac0
			"nonblocking option.\n");
Packit 229ac0
		return -EINVAL;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (state->test_nowait) {
Packit 229ac0
		if (!state->nonblock && !state->mmap) {
Packit 229ac0
			fprintf(stderr,
Packit 229ac0
				"An option for nowait test should be used with "
Packit 229ac0
				"nonblock or mmap options.\n");
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (state->msec_per_period > 0 && state->msec_per_buffer > 0) {
Packit 229ac0
		if (state->msec_per_period > state->msec_per_buffer) {
Packit 229ac0
			state->msec_per_period = state->msec_per_buffer;
Packit 229ac0
			state->msec_per_buffer = 0;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (state->frames_per_period > 0 && state->frames_per_buffer > 0) {
Packit 229ac0
		if (state->frames_per_period > state->frames_per_buffer) {
Packit 229ac0
			state->frames_per_period = state->frames_per_buffer;
Packit 229ac0
			state->frames_per_buffer = 0;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	state->sched_model = SCHED_MODEL_IRQ;
Packit 229ac0
	if (state->sched_model_literal != NULL) {
Packit 229ac0
		if (!strcmp(state->sched_model_literal, "timer")) {
Packit 229ac0
			state->sched_model = SCHED_MODEL_TIMER;
Packit 229ac0
			state->mmap = true;
Packit 229ac0
			state->nonblock = true;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (state->waiter_type_literal != NULL) {
Packit 229ac0
		if (state->test_nowait) {
Packit 229ac0
			fprintf(stderr,
Packit 229ac0
				"An option for waiter type should not be "
Packit 229ac0
				"used with nowait test option.\n");
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
		if (!state->nonblock && !state->mmap) {
Packit 229ac0
			fprintf(stderr,
Packit 229ac0
				"An option for waiter type should be used "
Packit 229ac0
				"with nonblock or mmap or timer-based "
Packit 229ac0
				"scheduling options.\n");
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
		state->waiter_type =
Packit 229ac0
			waiter_type_from_label(state->waiter_type_literal);
Packit 229ac0
	} else {
Packit 229ac0
		state->waiter_type = WAITER_TYPE_DEFAULT;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return err;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int set_access_hw_param(struct libasound_state *state)
Packit 229ac0
{
Packit 229ac0
	snd_pcm_access_mask_t *mask;
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_access_mask_malloc(&mask);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
	snd_pcm_access_mask_none(mask);
Packit 229ac0
	if (state->mmap) {
Packit 229ac0
		snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
Packit 229ac0
		snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
Packit 229ac0
	} else {
Packit 229ac0
		snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_RW_INTERLEAVED);
Packit 229ac0
		snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
Packit 229ac0
	}
Packit 229ac0
	err = snd_pcm_hw_params_set_access_mask(state->handle, state->hw_params,
Packit 229ac0
						mask);
Packit 229ac0
	snd_pcm_access_mask_free(mask);
Packit 229ac0
Packit 229ac0
	return err;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int disable_period_wakeup(struct libasound_state *state)
Packit 229ac0
{
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	if (snd_pcm_type(state->handle) != SND_PCM_TYPE_HW) {
Packit 229ac0
		logging(state,
Packit 229ac0
			"Timer-based scheduling is only available for 'hw' "
Packit 229ac0
			"PCM plugin.\n");
Packit 229ac0
		return -ENXIO;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (!snd_pcm_hw_params_can_disable_period_wakeup(state->hw_params)) {
Packit 229ac0
		logging(state,
Packit 229ac0
			"This hardware doesn't support the mode of no-period-"
Packit 229ac0
			"wakeup. In this case, timer-based scheduling is not "
Packit 229ac0
			"available.\n");
Packit 229ac0
		return -EIO;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	err = snd_pcm_hw_params_set_period_wakeup(state->handle,
Packit 229ac0
						  state->hw_params, 0);
Packit 229ac0
	if (err < 0) {
Packit 229ac0
		logging(state,
Packit 229ac0
			"Fail to disable period wakeup so that the hardware "
Packit 229ac0
			"generates no IRQs during transmission of data "
Packit 229ac0
			"frames.\n");
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return err;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int open_handle(struct xfer_context *xfer)
Packit 229ac0
{
Packit 229ac0
	struct libasound_state *state = xfer->private_data;
Packit 229ac0
	int mode = 0;
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	if (state->nonblock)
Packit 229ac0
		mode |= SND_PCM_NONBLOCK;
Packit 229ac0
	if (state->no_auto_resample)
Packit 229ac0
		mode |= SND_PCM_NO_AUTO_RESAMPLE;
Packit 229ac0
	if (state->no_auto_channels)
Packit 229ac0
		mode |= SND_PCM_NO_AUTO_CHANNELS;
Packit 229ac0
	if (state->no_auto_format)
Packit 229ac0
		mode |= SND_PCM_NO_AUTO_FORMAT;
Packit 229ac0
	if (state->no_softvol)
Packit 229ac0
		mode |= SND_PCM_NO_SOFTVOL;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_open(&state->handle, state->node_literal, xfer->direction,
Packit 229ac0
			   mode);
Packit 229ac0
	if (err < 0) {
Packit 229ac0
		logging(state, "Fail to open libasound PCM node for %s: %s\n",
Packit 229ac0
			snd_pcm_stream_name(xfer->direction),
Packit 229ac0
			state->node_literal);
Packit 229ac0
		return err;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if ((state->nonblock || state->mmap) && !state->test_nowait)
Packit 229ac0
		state->use_waiter = true;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_hw_params_any(state->handle, state->hw_params);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	if (state->sched_model == SCHED_MODEL_TIMER) {
Packit 229ac0
		err = disable_period_wakeup(state);
Packit 229ac0
		if (err < 0)
Packit 229ac0
			return err;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (xfer->dump_hw_params) {
Packit 229ac0
		logging(state, "Available HW Params of node: %s\n",
Packit 229ac0
			snd_pcm_name(state->handle));
Packit 229ac0
		snd_pcm_hw_params_dump(state->hw_params, state->log);
Packit 229ac0
		// TODO: there're more parameters which are not dumped by
Packit 229ac0
		// alsa-lib.
Packit 229ac0
		return 0;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return set_access_hw_param(state);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int prepare_waiter(struct libasound_state *state)
Packit 229ac0
{
Packit 229ac0
	unsigned int pfd_count;
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	// Nothing to do for dafault waiter (=snd_pcm_wait()).
Packit 229ac0
	if (state->waiter_type == WAITER_TYPE_DEFAULT)
Packit 229ac0
		return 0;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_poll_descriptors_count(state->handle);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
	if (err == 0)
Packit 229ac0
		return -ENXIO;
Packit 229ac0
	pfd_count = (unsigned int)err;
Packit 229ac0
Packit 229ac0
	state->waiter = malloc(sizeof(*state->waiter));
Packit 229ac0
	if (state->waiter == NULL)
Packit 229ac0
		return -ENOMEM;
Packit 229ac0
Packit 229ac0
	err = waiter_context_init(state->waiter, state->waiter_type, pfd_count);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_poll_descriptors(state->handle, state->waiter->pfds,
Packit 229ac0
				       pfd_count);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	return waiter_context_prepare(state->waiter);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
int xfer_libasound_wait_event(struct libasound_state *state, int timeout_msec,
Packit 229ac0
			      unsigned short *revents)
Packit 229ac0
{
Packit 229ac0
	int count;
Packit 229ac0
Packit 229ac0
	if (state->waiter_type != WAITER_TYPE_DEFAULT) {
Packit 229ac0
		struct waiter_context *waiter = state->waiter;
Packit 229ac0
		int err;
Packit 229ac0
Packit 229ac0
		count = waiter_context_wait_event(waiter, timeout_msec);
Packit 229ac0
		if (count < 0)
Packit 229ac0
			return count;
Packit 229ac0
		if (count == 0 && timeout_msec > 0)
Packit 229ac0
			return -ETIMEDOUT;
Packit 229ac0
Packit 229ac0
		err = snd_pcm_poll_descriptors_revents(state->handle,
Packit 229ac0
				waiter->pfds, waiter->pfd_count, revents);
Packit 229ac0
		if (err < 0)
Packit 229ac0
			return err;
Packit 229ac0
	} else {
Packit 229ac0
		count = snd_pcm_wait(state->handle, timeout_msec);
Packit 229ac0
		if (count < 0)
Packit 229ac0
			return count;
Packit 229ac0
		if (count == 0 && timeout_msec > 0)
Packit 229ac0
			return -ETIMEDOUT;
Packit 229ac0
Packit 229ac0
		if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_PLAYBACK)
Packit 229ac0
			*revents = POLLOUT;
Packit 229ac0
		else
Packit 229ac0
			*revents = POLLIN;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return 0;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int configure_hw_params(struct libasound_state *state,
Packit 229ac0
			       snd_pcm_format_t format,
Packit 229ac0
			       unsigned int samples_per_frame,
Packit 229ac0
			       unsigned int frames_per_second,
Packit 229ac0
			       unsigned int msec_per_period,
Packit 229ac0
			       unsigned int msec_per_buffer,
Packit 229ac0
			       snd_pcm_uframes_t frames_per_period,
Packit 229ac0
			       snd_pcm_uframes_t frames_per_buffer)
Packit 229ac0
{
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	// Configure sample format.
Packit 229ac0
	if (format == SND_PCM_FORMAT_UNKNOWN) {
Packit 229ac0
		snd_pcm_format_mask_t *mask;
Packit 229ac0
Packit 229ac0
		err = snd_pcm_format_mask_malloc(&mask);
Packit 229ac0
		if (err < 0)
Packit 229ac0
			return err;
Packit 229ac0
		snd_pcm_hw_params_get_format_mask(state->hw_params, mask);
Packit 229ac0
		for (format = 0; format <= SND_PCM_FORMAT_LAST; ++format) {
Packit 229ac0
			if (snd_pcm_format_mask_test(mask, format))
Packit 229ac0
				break;
Packit 229ac0
		}
Packit 229ac0
		snd_pcm_format_mask_free(mask);
Packit 229ac0
		if (format > SND_PCM_FORMAT_LAST) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"Any sample format is not available.\n");
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
	err = snd_pcm_hw_params_set_format(state->handle, state->hw_params,
Packit 229ac0
					   format);
Packit 229ac0
	if (err < 0) {
Packit 229ac0
		logging(state,
Packit 229ac0
			"Sample format '%s' is not available: %s\n",
Packit 229ac0
			snd_pcm_format_name(format), snd_strerror(err));
Packit 229ac0
		return err;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	// Configure channels.
Packit 229ac0
	if (samples_per_frame == 0) {
Packit 229ac0
		err = snd_pcm_hw_params_get_channels_min(state->hw_params,
Packit 229ac0
							 &samples_per_frame);
Packit 229ac0
		if (err < 0) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"Any channel number is not available.\n");
Packit 229ac0
			return err;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
	err = snd_pcm_hw_params_set_channels(state->handle, state->hw_params,
Packit 229ac0
					     samples_per_frame);
Packit 229ac0
	if (err < 0) {
Packit 229ac0
		logging(state,
Packit 229ac0
			"Channels count '%u' is not available: %s\n",
Packit 229ac0
			samples_per_frame, snd_strerror(err));
Packit 229ac0
		return err;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	// Configure rate.
Packit 229ac0
	if (frames_per_second == 0) {
Packit 229ac0
		err = snd_pcm_hw_params_get_rate_min(state->hw_params,
Packit 229ac0
						     &frames_per_second, NULL);
Packit 229ac0
		if (err < 0) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"Any rate is not available.\n");
Packit 229ac0
			return err;
Packit 229ac0
		}
Packit 229ac0
Packit 229ac0
	}
Packit 229ac0
	err = snd_pcm_hw_params_set_rate(state->handle, state->hw_params,
Packit 229ac0
					 frames_per_second, 0);
Packit 229ac0
	if (err < 0) {
Packit 229ac0
		logging(state,
Packit 229ac0
			"Sampling rate '%u' is not available: %s\n",
Packit 229ac0
			frames_per_second, snd_strerror(err));
Packit 229ac0
		return err;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	// Keep one of 'frames_per_buffer' and 'msec_per_buffer'.
Packit 229ac0
	if (frames_per_buffer == 0) {
Packit 229ac0
		if (msec_per_buffer == 0) {
Packit 229ac0
			err = snd_pcm_hw_params_get_buffer_time_max(
Packit 229ac0
				state->hw_params, &msec_per_buffer, NULL);
Packit 229ac0
			if (err < 0) {
Packit 229ac0
				logging(state,
Packit 229ac0
					"The maximum msec per buffer is not "
Packit 229ac0
					"available.\n");
Packit 229ac0
				return err;
Packit 229ac0
			}
Packit 229ac0
			if (msec_per_buffer > 500000)
Packit 229ac0
				msec_per_buffer = 500000;
Packit 229ac0
		}
Packit 229ac0
	} else if (msec_per_buffer > 0) {
Packit 229ac0
		uint64_t msec;
Packit 229ac0
Packit 229ac0
		msec = 1000000 * frames_per_buffer / frames_per_second;
Packit 229ac0
		if (msec < msec_per_buffer)
Packit 229ac0
			msec_per_buffer = 0;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	// Keep one of 'frames_per_period' and 'msec_per_period'.
Packit 229ac0
	if (frames_per_period == 0) {
Packit 229ac0
		if (msec_per_period == 0) {
Packit 229ac0
			if (msec_per_buffer > 0)
Packit 229ac0
				msec_per_period = msec_per_buffer / 4;
Packit 229ac0
			else
Packit 229ac0
				frames_per_period = frames_per_buffer / 4;
Packit 229ac0
		}
Packit 229ac0
	} else if (msec_per_period > 0) {
Packit 229ac0
		uint64_t msec;
Packit 229ac0
Packit 229ac0
		msec = 1000000 * frames_per_period / frames_per_second;
Packit 229ac0
		if (msec < msec_per_period)
Packit 229ac0
			msec_per_period = 0;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (msec_per_period) {
Packit 229ac0
		err = snd_pcm_hw_params_set_period_time_near(state->handle,
Packit 229ac0
				state->hw_params, &msec_per_period, NULL);
Packit 229ac0
		if (err < 0) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"Fail to configure period time: %u msec\n",
Packit 229ac0
				msec_per_period);
Packit 229ac0
			return err;
Packit 229ac0
		}
Packit 229ac0
	} else {
Packit 229ac0
		err = snd_pcm_hw_params_set_period_size_near(state->handle,
Packit 229ac0
				state->hw_params, &frames_per_period, NULL);
Packit 229ac0
		if (err < 0) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"Fail to configure period size: %lu frames\n",
Packit 229ac0
				frames_per_period);
Packit 229ac0
			return err;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (msec_per_buffer) {
Packit 229ac0
		err = snd_pcm_hw_params_set_buffer_time_near(state->handle,
Packit 229ac0
				state->hw_params, &msec_per_buffer, NULL);
Packit 229ac0
		if (err < 0) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"Fail to configure buffer time: %u msec\n",
Packit 229ac0
				msec_per_buffer);
Packit 229ac0
			return err;
Packit 229ac0
		}
Packit 229ac0
	} else {
Packit 229ac0
		err = snd_pcm_hw_params_set_buffer_size_near(state->handle,
Packit 229ac0
					state->hw_params, &frames_per_buffer);
Packit 229ac0
		if (err < 0) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"Fail to configure buffer size: %lu frames\n",
Packit 229ac0
				frames_per_buffer);
Packit 229ac0
			return err;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return snd_pcm_hw_params(state->handle, state->hw_params);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int retrieve_actual_hw_params(snd_pcm_hw_params_t *hw_params,
Packit 229ac0
				     snd_pcm_format_t *format,
Packit 229ac0
				     unsigned int *samples_per_frame,
Packit 229ac0
				     unsigned int *frames_per_second,
Packit 229ac0
				     snd_pcm_access_t *access,
Packit 229ac0
				     snd_pcm_uframes_t *frames_per_buffer)
Packit 229ac0
{
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_hw_params_get_format(hw_params, format);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_hw_params_get_channels(hw_params,
Packit 229ac0
					     samples_per_frame);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_hw_params_get_rate(hw_params, frames_per_second,
Packit 229ac0
					 NULL);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_hw_params_get_access(hw_params, access);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	return snd_pcm_hw_params_get_buffer_size(hw_params, frames_per_buffer);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int configure_sw_params(struct libasound_state *state,
Packit 229ac0
			       unsigned int frames_per_second,
Packit 229ac0
			       unsigned int frames_per_buffer,
Packit 229ac0
			       unsigned int msec_for_avail_min,
Packit 229ac0
			       unsigned int msec_for_start_threshold,
Packit 229ac0
			       unsigned int msec_for_stop_threshold)
Packit 229ac0
{
Packit 229ac0
	snd_pcm_uframes_t frame_count;
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	if (msec_for_avail_min > 0) {
Packit 229ac0
		frame_count = msec_for_avail_min * frames_per_second / 1000000;
Packit 229ac0
		if (frame_count == 0 || frame_count > frames_per_buffer) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"The msec for 'avail_min' is too %s: %u "
Packit 229ac0
				"msec (%lu frames at %u).\n",
Packit 229ac0
				frame_count == 0 ? "small" : "large",
Packit 229ac0
				msec_for_avail_min, frame_count,
Packit 229ac0
				frames_per_second);
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
		err = snd_pcm_sw_params_set_avail_min(state->handle,
Packit 229ac0
						state->sw_params, frame_count);
Packit 229ac0
		if (err < 0) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"Fail to configure 'avail-min'.\n");
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (msec_for_start_threshold > 0) {
Packit 229ac0
		frame_count = msec_for_start_threshold * frames_per_second /
Packit 229ac0
			      1000000;
Packit 229ac0
		if (frame_count == 0 || frame_count > frames_per_buffer) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"The msec for 'start-delay' is too %s: %u "
Packit 229ac0
				"msec (%lu frames at %u).\n",
Packit 229ac0
				frame_count == 0 ? "small" : "large",
Packit 229ac0
				msec_for_start_threshold, frame_count,
Packit 229ac0
				frames_per_second);
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
		err = snd_pcm_sw_params_set_start_threshold(state->handle,
Packit 229ac0
						state->sw_params, frame_count);
Packit 229ac0
		if (err < 0) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"Fail to configure 'start-delay'.\n");
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (msec_for_stop_threshold > 0) {
Packit 229ac0
		frame_count = msec_for_stop_threshold * frames_per_second /
Packit 229ac0
			      1000000;
Packit 229ac0
		if (frame_count == 0 || frame_count > frames_per_buffer) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"The msec for 'stop-delay' is too %s: %u "
Packit 229ac0
				"msec (%lu frames at %u).\n",
Packit 229ac0
				frame_count == 0 ? "small" : "large",
Packit 229ac0
				msec_for_stop_threshold, frame_count,
Packit 229ac0
				frames_per_second);
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
		err = snd_pcm_sw_params_set_stop_threshold(state->handle,
Packit 229ac0
						state->sw_params, frame_count);
Packit 229ac0
		if (err < 0) {
Packit 229ac0
			logging(state,
Packit 229ac0
				"Fail to configure 'stop-delay'.\n");
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return snd_pcm_sw_params(state->handle, state->sw_params);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int xfer_libasound_pre_process(struct xfer_context *xfer,
Packit 229ac0
				      snd_pcm_format_t *format,
Packit 229ac0
				      unsigned int *samples_per_frame,
Packit 229ac0
				      unsigned int *frames_per_second,
Packit 229ac0
				      snd_pcm_access_t *access,
Packit 229ac0
				      snd_pcm_uframes_t *frames_per_buffer)
Packit 229ac0
{
Packit 229ac0
	struct libasound_state *state = xfer->private_data;
Packit 229ac0
	unsigned int flag;
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	err = open_handle(xfer);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return -ENXIO;
Packit 229ac0
Packit 229ac0
	err = configure_hw_params(state, *format, *samples_per_frame,
Packit 229ac0
				  *frames_per_second,
Packit 229ac0
				  state->msec_per_period,
Packit 229ac0
				  state->msec_per_buffer,
Packit 229ac0
				  state->frames_per_period,
Packit 229ac0
				  state->frames_per_buffer);
Packit 229ac0
	if (err < 0) {
Packit 229ac0
		logging(state, "Current hardware parameters:\n");
Packit 229ac0
		snd_pcm_hw_params_dump(state->hw_params, state->log);
Packit 229ac0
		return err;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	// Retrieve actual parameters.
Packit 229ac0
	err = retrieve_actual_hw_params(state->hw_params, format,
Packit 229ac0
					samples_per_frame, frames_per_second,
Packit 229ac0
					access, frames_per_buffer);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	// Query software parameters.
Packit 229ac0
	err = snd_pcm_sw_params_current(state->handle, state->sw_params);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	// Assign I/O operation.
Packit 229ac0
	err = snd_pcm_hw_params_get_period_wakeup(state->handle,
Packit 229ac0
						  state->hw_params, &flag;;
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	if (flag) {
Packit 229ac0
		if (*access == SND_PCM_ACCESS_RW_INTERLEAVED ||
Packit 229ac0
		    *access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
Packit 229ac0
			state->ops = &xfer_libasound_irq_rw_ops;
Packit 229ac0
		} else if (*access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
Packit 229ac0
			   *access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) {
Packit 229ac0
			if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE)
Packit 229ac0
				state->ops = &xfer_libasound_irq_mmap_r_ops;
Packit 229ac0
			else
Packit 229ac0
				state->ops = &xfer_libasound_irq_mmap_w_ops;
Packit 229ac0
		} else {
Packit 229ac0
			return -ENXIO;
Packit 229ac0
		}
Packit 229ac0
	} else {
Packit 229ac0
		if (*access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
Packit 229ac0
		    *access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) {
Packit 229ac0
			if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE)
Packit 229ac0
				state->ops = &xfer_libasound_timer_mmap_r_ops;
Packit 229ac0
			else
Packit 229ac0
				state->ops = &xfer_libasound_timer_mmap_w_ops;
Packit 229ac0
		} else {
Packit 229ac0
			return -ENXIO;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (state->ops->private_size > 0) {
Packit 229ac0
		state->private_data = malloc(state->ops->private_size);
Packit 229ac0
		if (state->private_data == NULL)
Packit 229ac0
			return -ENOMEM;
Packit 229ac0
		memset(state->private_data, 0, state->ops->private_size);
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	err = state->ops->pre_process(state);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	err = configure_sw_params(state, *frames_per_second,
Packit 229ac0
				  *frames_per_buffer,
Packit 229ac0
				  state->msec_for_avail_min,
Packit 229ac0
				  state->msec_for_start_threshold,
Packit 229ac0
				  state->msec_for_stop_threshold);
Packit 229ac0
	if (err < 0) {
Packit 229ac0
		logging(state, "Current software parameters:\n");
Packit 229ac0
		snd_pcm_sw_params_dump(state->sw_params, state->log);
Packit 229ac0
		return err;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (xfer->verbose > 0) {
Packit 229ac0
		snd_pcm_dump(state->handle, state->log);
Packit 229ac0
		logging(state, "Scheduling model:\n");
Packit 229ac0
		logging(state, "  %s\n", sched_model_labels[state->sched_model]);
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (state->use_waiter) {
Packit 229ac0
		// NOTE: This should be after configuring sw_params due to
Packit 229ac0
		// timer descriptor for time-based scheduling model.
Packit 229ac0
		err = prepare_waiter(state);
Packit 229ac0
		if (err < 0)
Packit 229ac0
			return err;
Packit 229ac0
Packit 229ac0
		if (xfer->verbose > 0) {
Packit 229ac0
			logging(state, "Waiter type:\n");
Packit 229ac0
			logging(state,
Packit 229ac0
				"  %s\n",
Packit 229ac0
				waiter_label_from_type(state->waiter_type));
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return 0;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int xfer_libasound_process_frames(struct xfer_context *xfer,
Packit 229ac0
					 unsigned int *frame_count,
Packit 229ac0
					 struct mapper_context *mapper,
Packit 229ac0
					 struct container_context *cntrs)
Packit 229ac0
{
Packit 229ac0
	struct libasound_state *state = xfer->private_data;
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	if (state->handle == NULL)
Packit 229ac0
		return -ENXIO;
Packit 229ac0
Packit 229ac0
	err = state->ops->process_frames(state, frame_count, mapper, cntrs);
Packit 229ac0
	if (err < 0) {
Packit 229ac0
		if (err == -EAGAIN)
Packit 229ac0
			return err;
Packit 229ac0
		if (err == -EPIPE && !state->finish_at_xrun) {
Packit 229ac0
			// Recover the stream and continue processing
Packit 229ac0
			// immediately. In this program -EPIPE comes from
Packit 229ac0
			// libasound implementation instead of file I/O.
Packit 229ac0
			err = snd_pcm_prepare(state->handle);
Packit 229ac0
		}
Packit 229ac0
Packit 229ac0
		if (err < 0) {
Packit 229ac0
			// TODO: -EIO from libasound for hw PCM node means
Packit 229ac0
			// that IRQ disorder. This should be reported to help
Packit 229ac0
			// developers for drivers.
Packit 229ac0
			logging(state, "Fail to process frames: %s\n",
Packit 229ac0
				snd_strerror(err));
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return err;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void xfer_libasound_pause(struct xfer_context *xfer, bool enable)
Packit 229ac0
{
Packit 229ac0
	struct libasound_state *state = xfer->private_data;
Packit 229ac0
	snd_pcm_state_t s = snd_pcm_state(state->handle);
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	if (state->handle == NULL)
Packit 229ac0
		return;
Packit 229ac0
Packit 229ac0
	if (enable) {
Packit 229ac0
		if (s != SND_PCM_STATE_RUNNING)
Packit 229ac0
			return;
Packit 229ac0
	} else {
Packit 229ac0
		if (s != SND_PCM_STATE_PAUSED)
Packit 229ac0
			return;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	// Not supported. Leave the substream to enter XRUN state.
Packit 229ac0
	if (!snd_pcm_hw_params_can_pause(state->hw_params))
Packit 229ac0
		return;
Packit 229ac0
Packit 229ac0
	err = snd_pcm_pause(state->handle, enable);
Packit 229ac0
	if (err < 0 && state->verbose) {
Packit 229ac0
		logging(state, "snd_pcm_pause(): %s\n", snd_strerror(err));
Packit 229ac0
	}
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void xfer_libasound_post_process(struct xfer_context *xfer)
Packit 229ac0
{
Packit 229ac0
	struct libasound_state *state = xfer->private_data;
Packit 229ac0
	snd_pcm_state_t pcm_state;
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	if (state->handle == NULL)
Packit 229ac0
		return;
Packit 229ac0
Packit 229ac0
	pcm_state = snd_pcm_state(state->handle);
Packit 229ac0
	if (pcm_state != SND_PCM_STATE_OPEN &&
Packit 229ac0
	    pcm_state != SND_PCM_STATE_DISCONNECTED) {
Packit 229ac0
		if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE ||
Packit 229ac0
		    state->ops == &xfer_libasound_timer_mmap_w_ops) {
Packit 229ac0
			err = snd_pcm_drop(state->handle);
Packit 229ac0
			if (err < 0)
Packit 229ac0
				logging(state, "snd_pcm_drop(): %s\n",
Packit 229ac0
				       snd_strerror(err));
Packit 229ac0
		} else {
Packit 229ac0
			// TODO: this is a bug in kernel land.
Packit 229ac0
			if (state->nonblock)
Packit 229ac0
				snd_pcm_nonblock(state->handle, 0);
Packit 229ac0
			err = snd_pcm_drain(state->handle);
Packit 229ac0
			if (state->nonblock)
Packit 229ac0
				snd_pcm_nonblock(state->handle, 1);
Packit 229ac0
			if (err < 0)
Packit 229ac0
				logging(state, "snd_pcm_drain(): %s\n",
Packit 229ac0
				       snd_strerror(err));
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	err = snd_pcm_hw_free(state->handle);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		logging(state, "snd_pcm_hw_free(): %s\n", snd_strerror(err));
Packit 229ac0
Packit 229ac0
	snd_pcm_close(state->handle);
Packit 229ac0
	state->handle = NULL;
Packit 229ac0
Packit 229ac0
	if (state->ops && state->ops->post_process)
Packit 229ac0
		state->ops->post_process(state);
Packit 229ac0
	free(state->private_data);
Packit 229ac0
	state->private_data = NULL;
Packit 229ac0
Packit 229ac0
	// Free cache of content for configuration files so that memory leaks
Packit 229ac0
	// are not detected.
Packit 229ac0
	snd_config_update_free_global();
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void xfer_libasound_destroy(struct xfer_context *xfer)
Packit 229ac0
{
Packit 229ac0
	struct libasound_state *state = xfer->private_data;
Packit 229ac0
Packit 229ac0
	free(state->node_literal);
Packit 229ac0
	free(state->waiter_type_literal);
Packit 229ac0
	free(state->sched_model_literal);
Packit 229ac0
	state->node_literal = NULL;
Packit 229ac0
	state->waiter_type_literal = NULL;
Packit 229ac0
	state->sched_model_literal = NULL;
Packit 229ac0
Packit 229ac0
	if (state->hw_params)
Packit 229ac0
		snd_pcm_hw_params_free(state->hw_params);
Packit 229ac0
	if (state->sw_params)
Packit 229ac0
		snd_pcm_sw_params_free(state->sw_params);
Packit 229ac0
	state->hw_params = NULL;
Packit 229ac0
	state->sw_params = NULL;
Packit 229ac0
Packit 229ac0
	if (state->log)
Packit 229ac0
		snd_output_close(state->log);
Packit 229ac0
	state->log = NULL;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void xfer_libasound_help(struct xfer_context *xfer)
Packit 229ac0
{
Packit 229ac0
	printf(
Packit 229ac0
"      [BASICS]\n"
Packit 229ac0
"        -D, --device          select node by name in coniguration space\n"
Packit 229ac0
"        -N, --nonblock        nonblocking mode\n"
Packit 229ac0
"        -M, --mmap            use mmap(2) for zero copying technique\n"
Packit 229ac0
"        -F, --period-time     interval between interrupts (msec unit)\n"
Packit 229ac0
"        --period-size         interval between interrupts (frame unit)\n"
Packit 229ac0
"        -B, --buffer-time     size of buffer for frame(msec unit)\n"
Packit 229ac0
"        --buffer-size         size of buffer for frame(frame unit)\n"
Packit 229ac0
"        --waiter-type         type of waiter to handle available frames\n"
Packit 229ac0
"        --sched-model         model of process scheduling\n"
Packit 229ac0
"      [SOFTWARE FEATURES]\n"
Packit 229ac0
"        -A, --avail-min       threshold of frames to wake up process\n"
Packit 229ac0
"        -R, --start-delay     threshold of frames to start PCM substream\n"
Packit 229ac0
"        -T, --stop-delay      threshold of frames to stop PCM substream\n"
Packit 229ac0
"      [LIBASOUND PLUGIN OPTIONS]\n"
Packit 229ac0
"        --disable-resample    disable rate conversion for plug plugin\n"
Packit 229ac0
"        --disable-channels    disable channel conversion for plug plugin\n"
Packit 229ac0
"        --disable-format      disable format conversion for plug plugin\n"
Packit 229ac0
"        --disable-softvol     disable software volume for sofvol plugin\n"
Packit 229ac0
"      [DEBUG ASSISTANT]\n"
Packit 229ac0
"        --fatal-errors        finish at XRUN\n"
Packit 229ac0
"        --test-nowait         busy poll without any waiter\n"
Packit 229ac0
	);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
const struct xfer_data xfer_libasound = {
Packit 229ac0
	.s_opts = S_OPTS,
Packit 229ac0
	.l_opts = l_opts,
Packit 229ac0
	.l_opts_count = ARRAY_SIZE(l_opts),
Packit 229ac0
	.ops = {
Packit 229ac0
		.init		= xfer_libasound_init,
Packit 229ac0
		.parse_opt	= xfer_libasound_parse_opt,
Packit 229ac0
		.validate_opts	= xfer_libasound_validate_opts,
Packit 229ac0
		.pre_process	= xfer_libasound_pre_process,
Packit 229ac0
		.process_frames	= xfer_libasound_process_frames,
Packit 229ac0
		.pause		= xfer_libasound_pause,
Packit 229ac0
		.post_process	= xfer_libasound_post_process,
Packit 229ac0
		.destroy	= xfer_libasound_destroy,
Packit 229ac0
		.help		= xfer_libasound_help,
Packit 229ac0
	},
Packit 229ac0
	.private_size = sizeof(struct libasound_state),
Packit 229ac0
};