|
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 |
};
|