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