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