• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 struct 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 
43 enum 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:"
52 static 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 
xfer_libffado_init(struct xfer_context * xfer,snd_pcm_stream_t direction)63 static 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 
xfer_libffado_parse_opt(struct xfer_context * xfer,int key,const char * optarg)78 static 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 
validate_sched_priority(struct libffado_state * state)106 static 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 
xfer_libffado_validate_opts(struct xfer_context * xfer)125 static 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 
r_process_frames(struct xfer_context * xfer,unsigned int * frame_count,struct mapper_context * mapper,struct container_context * cntrs)174 static 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 
w_process_frames(struct xfer_context * xfer,unsigned int * frame_count,struct mapper_context * mapper,struct container_context * cntrs)237 static 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 
open_handle(struct xfer_context * xfer,unsigned int frames_per_second)299 static 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 
enable_mbla_data_ch(struct libffado_state * state,unsigned int * samples_per_frame)344 static 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 
xfer_libffado_pre_process(struct xfer_context * xfer,snd_pcm_format_t * format,unsigned int * samples_per_frame,unsigned int * frames_per_second,snd_pcm_access_t * access,snd_pcm_uframes_t * frames_per_buffer)387 static 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 
xfer_libffado_process_frames(struct xfer_context * xfer,unsigned int * frame_count,struct mapper_context * mapper,struct container_context * cntrs)474 static 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 
xfer_libffado_pause(struct xfer_context * xfer,bool enable)501 static 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 
xfer_libffado_post_process(struct xfer_context * xfer)514 static 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 
xfer_libffado_destroy(struct xfer_context * xfer)528 static 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 
xfer_libffado_help(struct xfer_context * xfer)540 static 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 
554 const 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