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