1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // xfer-libasound.c - receive/transmit frames by alsa-lib.
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-libasound.h"
10 #include "misc.h"
11
12 static const char *const sched_model_labels [] = {
13 [SCHED_MODEL_IRQ] = "irq",
14 [SCHED_MODEL_TIMER] = "timer",
15 };
16
17 enum no_short_opts {
18 // 200 or later belong to non us-ascii character set.
19 OPT_PERIOD_SIZE = 200,
20 OPT_BUFFER_SIZE,
21 OPT_WAITER_TYPE,
22 OPT_SCHED_MODEL,
23 OPT_DISABLE_RESAMPLE,
24 OPT_DISABLE_CHANNELS,
25 OPT_DISABLE_FORMAT,
26 OPT_DISABLE_SOFTVOL,
27 OPT_FATAL_ERRORS,
28 OPT_TEST_NOWAIT,
29 // Obsoleted.
30 OPT_TEST_POSITION,
31 OPT_TEST_COEF,
32 };
33
34 #define S_OPTS "D:NMF:B:A:R:T:m:"
35 static const struct option l_opts[] = {
36 {"device", 1, 0, 'D'},
37 {"nonblock", 0, 0, 'N'},
38 {"mmap", 0, 0, 'M'},
39 {"period-time", 1, 0, 'F'},
40 {"buffer-time", 1, 0, 'B'},
41 {"period-size", 1, 0, OPT_PERIOD_SIZE},
42 {"buffer-size", 1, 0, OPT_BUFFER_SIZE},
43 {"avail-min", 1, 0, 'A'},
44 {"start-delay", 1, 0, 'R'},
45 {"stop-delay", 1, 0, 'T'},
46 {"waiter-type", 1, 0, OPT_WAITER_TYPE},
47 {"sched-model", 1, 0, OPT_SCHED_MODEL},
48 // For plugins in alsa-lib.
49 {"disable-resample", 0, 0, OPT_DISABLE_RESAMPLE},
50 {"disable-channels", 0, 0, OPT_DISABLE_CHANNELS},
51 {"disable-format", 0, 0, OPT_DISABLE_FORMAT},
52 {"disable-softvol", 0, 0, OPT_DISABLE_SOFTVOL},
53 // For debugging.
54 {"fatal-errors", 0, 0, OPT_FATAL_ERRORS},
55 {"test-nowait", 0, 0, OPT_TEST_NOWAIT},
56 // Obsoleted.
57 {"chmap", 1, 0, 'm'},
58 {"test-position", 0, 0, OPT_TEST_POSITION},
59 {"test-coef", 1, 0, OPT_TEST_COEF},
60 };
61
xfer_libasound_init(struct xfer_context * xfer,snd_pcm_stream_t direction)62 static int xfer_libasound_init(struct xfer_context *xfer,
63 snd_pcm_stream_t direction)
64 {
65 struct libasound_state *state = xfer->private_data;
66 int err;
67
68 err = snd_output_stdio_attach(&state->log, stderr, 0);
69 if (err < 0)
70 return err;
71
72 err = snd_pcm_hw_params_malloc(&state->hw_params);
73 if (err < 0)
74 return err;
75
76 return snd_pcm_sw_params_malloc(&state->sw_params);
77 }
78
xfer_libasound_parse_opt(struct xfer_context * xfer,int key,const char * optarg)79 static int xfer_libasound_parse_opt(struct xfer_context *xfer, int key,
80 const char *optarg)
81 {
82 struct libasound_state *state = xfer->private_data;
83 int err = 0;
84
85 if (key == 'D')
86 state->node_literal = arg_duplicate_string(optarg, &err);
87 else if (key == 'N')
88 state->nonblock = true;
89 else if (key == 'M')
90 state->mmap = true;
91 else if (key == 'F')
92 state->msec_per_period = arg_parse_decimal_num(optarg, &err);
93 else if (key == 'B')
94 state->msec_per_buffer = arg_parse_decimal_num(optarg, &err);
95 else if (key == OPT_PERIOD_SIZE)
96 state->frames_per_period = arg_parse_decimal_num(optarg, &err);
97 else if (key == OPT_BUFFER_SIZE)
98 state->frames_per_buffer = arg_parse_decimal_num(optarg, &err);
99 else if (key == 'A')
100 state->msec_for_avail_min = arg_parse_decimal_num(optarg, &err);
101 else if (key == 'R')
102 state->msec_for_start_threshold = arg_parse_decimal_num(optarg, &err);
103 else if (key == 'T')
104 state->msec_for_stop_threshold = arg_parse_decimal_num(optarg, &err);
105 else if (key == OPT_WAITER_TYPE)
106 state->waiter_type_literal = arg_duplicate_string(optarg, &err);
107 else if (key == OPT_SCHED_MODEL)
108 state->sched_model_literal = arg_duplicate_string(optarg, &err);
109 else if (key == OPT_DISABLE_RESAMPLE)
110 state->no_auto_resample = true;
111 else if (key == OPT_DISABLE_CHANNELS)
112 state->no_auto_channels = true;
113 else if (key == OPT_DISABLE_FORMAT)
114 state->no_auto_format = true;
115 else if (key == OPT_DISABLE_SOFTVOL)
116 state->no_softvol = true;
117 else if (key == 'm' ||
118 key == OPT_TEST_POSITION ||
119 key == OPT_TEST_COEF)
120 err = -EINVAL;
121 else if (key == OPT_FATAL_ERRORS)
122 state->finish_at_xrun = true;
123 else if (key == OPT_TEST_NOWAIT)
124 state->test_nowait = true;
125 else
126 err = -ENXIO;
127
128 return err;
129 }
130
xfer_libasound_validate_opts(struct xfer_context * xfer)131 int xfer_libasound_validate_opts(struct xfer_context *xfer)
132 {
133 struct libasound_state *state = xfer->private_data;
134 int err = 0;
135
136 state->verbose = xfer->verbose > 1;
137
138 if (state->node_literal == NULL) {
139 state->node_literal = strdup("default");
140 if (state->node_literal == NULL)
141 return -ENOMEM;
142 }
143
144 if (state->mmap && state->nonblock) {
145 fprintf(stderr,
146 "An option for mmap operation should not be used with "
147 "nonblocking option.\n");
148 return -EINVAL;
149 }
150
151 if (state->test_nowait) {
152 if (!state->nonblock && !state->mmap) {
153 fprintf(stderr,
154 "An option for nowait test should be used with "
155 "nonblock or mmap options.\n");
156 return -EINVAL;
157 }
158 }
159
160 if (state->msec_per_period > 0 && state->msec_per_buffer > 0) {
161 if (state->msec_per_period > state->msec_per_buffer) {
162 state->msec_per_period = state->msec_per_buffer;
163 state->msec_per_buffer = 0;
164 }
165 }
166
167 if (state->frames_per_period > 0 && state->frames_per_buffer > 0) {
168 if (state->frames_per_period > state->frames_per_buffer) {
169 state->frames_per_period = state->frames_per_buffer;
170 state->frames_per_buffer = 0;
171 }
172 }
173
174 state->sched_model = SCHED_MODEL_IRQ;
175 if (state->sched_model_literal != NULL) {
176 if (!strcmp(state->sched_model_literal, "timer")) {
177 state->sched_model = SCHED_MODEL_TIMER;
178 state->mmap = true;
179 state->nonblock = true;
180 }
181 }
182
183 if (state->waiter_type_literal != NULL) {
184 if (state->test_nowait) {
185 fprintf(stderr,
186 "An option for waiter type should not be "
187 "used with nowait test option.\n");
188 return -EINVAL;
189 }
190 if (!state->nonblock && !state->mmap) {
191 fprintf(stderr,
192 "An option for waiter type should be used "
193 "with nonblock or mmap or timer-based "
194 "scheduling options.\n");
195 return -EINVAL;
196 }
197 state->waiter_type =
198 waiter_type_from_label(state->waiter_type_literal);
199 } else {
200 state->waiter_type = WAITER_TYPE_DEFAULT;
201 }
202
203 return err;
204 }
205
set_access_hw_param(struct libasound_state * state)206 static int set_access_hw_param(struct libasound_state *state)
207 {
208 snd_pcm_access_mask_t *mask;
209 int err;
210
211 err = snd_pcm_access_mask_malloc(&mask);
212 if (err < 0)
213 return err;
214 snd_pcm_access_mask_none(mask);
215 if (state->mmap) {
216 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
217 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
218 } else {
219 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_RW_INTERLEAVED);
220 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
221 }
222 err = snd_pcm_hw_params_set_access_mask(state->handle, state->hw_params,
223 mask);
224 snd_pcm_access_mask_free(mask);
225
226 return err;
227 }
228
disable_period_wakeup(struct libasound_state * state)229 static int disable_period_wakeup(struct libasound_state *state)
230 {
231 int err;
232
233 if (snd_pcm_type(state->handle) != SND_PCM_TYPE_HW) {
234 logging(state,
235 "Timer-based scheduling is only available for 'hw' "
236 "PCM plugin.\n");
237 return -ENXIO;
238 }
239
240 if (!snd_pcm_hw_params_can_disable_period_wakeup(state->hw_params)) {
241 logging(state,
242 "This hardware doesn't support the mode of no-period-"
243 "wakeup. In this case, timer-based scheduling is not "
244 "available.\n");
245 return -EIO;
246 }
247
248 err = snd_pcm_hw_params_set_period_wakeup(state->handle,
249 state->hw_params, 0);
250 if (err < 0) {
251 logging(state,
252 "Fail to disable period wakeup so that the hardware "
253 "generates no IRQs during transmission of data "
254 "frames.\n");
255 }
256
257 return err;
258 }
259
open_handle(struct xfer_context * xfer)260 static int open_handle(struct xfer_context *xfer)
261 {
262 struct libasound_state *state = xfer->private_data;
263 int mode = 0;
264 int err;
265
266 if (state->nonblock)
267 mode |= SND_PCM_NONBLOCK;
268 if (state->no_auto_resample)
269 mode |= SND_PCM_NO_AUTO_RESAMPLE;
270 if (state->no_auto_channels)
271 mode |= SND_PCM_NO_AUTO_CHANNELS;
272 if (state->no_auto_format)
273 mode |= SND_PCM_NO_AUTO_FORMAT;
274 if (state->no_softvol)
275 mode |= SND_PCM_NO_SOFTVOL;
276
277 err = snd_pcm_open(&state->handle, state->node_literal, xfer->direction,
278 mode);
279 if (err < 0) {
280 logging(state, "Fail to open libasound PCM node for %s: %s\n",
281 snd_pcm_stream_name(xfer->direction),
282 state->node_literal);
283 return err;
284 }
285
286 if ((state->nonblock || state->mmap) && !state->test_nowait)
287 state->use_waiter = true;
288
289 err = snd_pcm_hw_params_any(state->handle, state->hw_params);
290 if (err < 0)
291 return err;
292
293 if (state->sched_model == SCHED_MODEL_TIMER) {
294 err = disable_period_wakeup(state);
295 if (err < 0)
296 return err;
297 }
298
299 if (xfer->dump_hw_params) {
300 logging(state, "Available HW Params of node: %s\n",
301 snd_pcm_name(state->handle));
302 snd_pcm_hw_params_dump(state->hw_params, state->log);
303 // TODO: there're more parameters which are not dumped by
304 // alsa-lib.
305 return 0;
306 }
307
308 return set_access_hw_param(state);
309 }
310
prepare_waiter(struct libasound_state * state)311 static int prepare_waiter(struct libasound_state *state)
312 {
313 unsigned int pfd_count;
314 int err;
315
316 // Nothing to do for dafault waiter (=snd_pcm_wait()).
317 if (state->waiter_type == WAITER_TYPE_DEFAULT)
318 return 0;
319
320 err = snd_pcm_poll_descriptors_count(state->handle);
321 if (err < 0)
322 return err;
323 if (err == 0)
324 return -ENXIO;
325 pfd_count = (unsigned int)err;
326
327 state->waiter = malloc(sizeof(*state->waiter));
328 if (state->waiter == NULL)
329 return -ENOMEM;
330
331 err = waiter_context_init(state->waiter, state->waiter_type, pfd_count);
332 if (err < 0)
333 return err;
334
335 err = snd_pcm_poll_descriptors(state->handle, state->waiter->pfds,
336 pfd_count);
337 if (err < 0)
338 return err;
339
340 return waiter_context_prepare(state->waiter);
341 }
342
xfer_libasound_wait_event(struct libasound_state * state,int timeout_msec,unsigned short * revents)343 int xfer_libasound_wait_event(struct libasound_state *state, int timeout_msec,
344 unsigned short *revents)
345 {
346 int count;
347
348 if (state->waiter_type != WAITER_TYPE_DEFAULT) {
349 struct waiter_context *waiter = state->waiter;
350 int err;
351
352 count = waiter_context_wait_event(waiter, timeout_msec);
353 if (count < 0)
354 return count;
355 if (count == 0 && timeout_msec > 0)
356 return -ETIMEDOUT;
357
358 err = snd_pcm_poll_descriptors_revents(state->handle,
359 waiter->pfds, waiter->pfd_count, revents);
360 if (err < 0)
361 return err;
362 } else {
363 count = snd_pcm_wait(state->handle, timeout_msec);
364 if (count < 0)
365 return count;
366 if (count == 0 && timeout_msec > 0)
367 return -ETIMEDOUT;
368
369 if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_PLAYBACK)
370 *revents = POLLOUT;
371 else
372 *revents = POLLIN;
373 }
374
375 return 0;
376 }
377
configure_hw_params(struct libasound_state * state,snd_pcm_format_t format,unsigned int samples_per_frame,unsigned int frames_per_second,unsigned int msec_per_period,unsigned int msec_per_buffer,snd_pcm_uframes_t frames_per_period,snd_pcm_uframes_t frames_per_buffer)378 static int configure_hw_params(struct libasound_state *state,
379 snd_pcm_format_t format,
380 unsigned int samples_per_frame,
381 unsigned int frames_per_second,
382 unsigned int msec_per_period,
383 unsigned int msec_per_buffer,
384 snd_pcm_uframes_t frames_per_period,
385 snd_pcm_uframes_t frames_per_buffer)
386 {
387 int err;
388
389 // Configure sample format.
390 if (format == SND_PCM_FORMAT_UNKNOWN) {
391 snd_pcm_format_mask_t *mask;
392
393 err = snd_pcm_format_mask_malloc(&mask);
394 if (err < 0)
395 return err;
396 snd_pcm_hw_params_get_format_mask(state->hw_params, mask);
397 for (format = 0; format <= SND_PCM_FORMAT_LAST; ++format) {
398 if (snd_pcm_format_mask_test(mask, format))
399 break;
400 }
401 snd_pcm_format_mask_free(mask);
402 if (format > SND_PCM_FORMAT_LAST) {
403 logging(state,
404 "Any sample format is not available.\n");
405 return -EINVAL;
406 }
407 }
408 err = snd_pcm_hw_params_set_format(state->handle, state->hw_params,
409 format);
410 if (err < 0) {
411 logging(state,
412 "Sample format '%s' is not available: %s\n",
413 snd_pcm_format_name(format), snd_strerror(err));
414 return err;
415 }
416
417 // Configure channels.
418 if (samples_per_frame == 0) {
419 err = snd_pcm_hw_params_get_channels_min(state->hw_params,
420 &samples_per_frame);
421 if (err < 0) {
422 logging(state,
423 "Any channel number is not available.\n");
424 return err;
425 }
426 }
427 err = snd_pcm_hw_params_set_channels(state->handle, state->hw_params,
428 samples_per_frame);
429 if (err < 0) {
430 logging(state,
431 "Channels count '%u' is not available: %s\n",
432 samples_per_frame, snd_strerror(err));
433 return err;
434 }
435
436 // Configure rate.
437 if (frames_per_second == 0) {
438 err = snd_pcm_hw_params_get_rate_min(state->hw_params,
439 &frames_per_second, NULL);
440 if (err < 0) {
441 logging(state,
442 "Any rate is not available.\n");
443 return err;
444 }
445
446 }
447 err = snd_pcm_hw_params_set_rate(state->handle, state->hw_params,
448 frames_per_second, 0);
449 if (err < 0) {
450 logging(state,
451 "Sampling rate '%u' is not available: %s\n",
452 frames_per_second, snd_strerror(err));
453 return err;
454 }
455
456 // Keep one of 'frames_per_buffer' and 'msec_per_buffer'.
457 if (frames_per_buffer == 0) {
458 if (msec_per_buffer == 0) {
459 err = snd_pcm_hw_params_get_buffer_time_max(
460 state->hw_params, &msec_per_buffer, NULL);
461 if (err < 0) {
462 logging(state,
463 "The maximum msec per buffer is not "
464 "available.\n");
465 return err;
466 }
467 if (msec_per_buffer > 500000)
468 msec_per_buffer = 500000;
469 }
470 } else if (msec_per_buffer > 0) {
471 uint64_t msec;
472
473 msec = 1000000 * frames_per_buffer / frames_per_second;
474 if (msec < msec_per_buffer)
475 msec_per_buffer = 0;
476 }
477
478 // Keep one of 'frames_per_period' and 'msec_per_period'.
479 if (frames_per_period == 0) {
480 if (msec_per_period == 0) {
481 if (msec_per_buffer > 0)
482 msec_per_period = msec_per_buffer / 4;
483 else
484 frames_per_period = frames_per_buffer / 4;
485 }
486 } else if (msec_per_period > 0) {
487 uint64_t msec;
488
489 msec = 1000000 * frames_per_period / frames_per_second;
490 if (msec < msec_per_period)
491 msec_per_period = 0;
492 }
493
494 if (msec_per_period) {
495 err = snd_pcm_hw_params_set_period_time_near(state->handle,
496 state->hw_params, &msec_per_period, NULL);
497 if (err < 0) {
498 logging(state,
499 "Fail to configure period time: %u msec\n",
500 msec_per_period);
501 return err;
502 }
503 } else {
504 err = snd_pcm_hw_params_set_period_size_near(state->handle,
505 state->hw_params, &frames_per_period, NULL);
506 if (err < 0) {
507 logging(state,
508 "Fail to configure period size: %lu frames\n",
509 frames_per_period);
510 return err;
511 }
512 }
513
514 if (msec_per_buffer) {
515 err = snd_pcm_hw_params_set_buffer_time_near(state->handle,
516 state->hw_params, &msec_per_buffer, NULL);
517 if (err < 0) {
518 logging(state,
519 "Fail to configure buffer time: %u msec\n",
520 msec_per_buffer);
521 return err;
522 }
523 } else {
524 err = snd_pcm_hw_params_set_buffer_size_near(state->handle,
525 state->hw_params, &frames_per_buffer);
526 if (err < 0) {
527 logging(state,
528 "Fail to configure buffer size: %lu frames\n",
529 frames_per_buffer);
530 return err;
531 }
532 }
533
534 return snd_pcm_hw_params(state->handle, state->hw_params);
535 }
536
retrieve_actual_hw_params(snd_pcm_hw_params_t * hw_params,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)537 static int retrieve_actual_hw_params(snd_pcm_hw_params_t *hw_params,
538 snd_pcm_format_t *format,
539 unsigned int *samples_per_frame,
540 unsigned int *frames_per_second,
541 snd_pcm_access_t *access,
542 snd_pcm_uframes_t *frames_per_buffer)
543 {
544 int err;
545
546 err = snd_pcm_hw_params_get_format(hw_params, format);
547 if (err < 0)
548 return err;
549
550 err = snd_pcm_hw_params_get_channels(hw_params,
551 samples_per_frame);
552 if (err < 0)
553 return err;
554
555 err = snd_pcm_hw_params_get_rate(hw_params, frames_per_second,
556 NULL);
557 if (err < 0)
558 return err;
559
560 err = snd_pcm_hw_params_get_access(hw_params, access);
561 if (err < 0)
562 return err;
563
564 return snd_pcm_hw_params_get_buffer_size(hw_params, frames_per_buffer);
565 }
566
configure_sw_params(struct libasound_state * state,unsigned int frames_per_second,unsigned int frames_per_buffer,unsigned int msec_for_avail_min,unsigned int msec_for_start_threshold,unsigned int msec_for_stop_threshold)567 static int configure_sw_params(struct libasound_state *state,
568 unsigned int frames_per_second,
569 unsigned int frames_per_buffer,
570 unsigned int msec_for_avail_min,
571 unsigned int msec_for_start_threshold,
572 unsigned int msec_for_stop_threshold)
573 {
574 snd_pcm_uframes_t frame_count;
575 int err;
576
577 if (msec_for_avail_min > 0) {
578 frame_count = msec_for_avail_min * frames_per_second / 1000000;
579 if (frame_count == 0 || frame_count > frames_per_buffer) {
580 logging(state,
581 "The msec for 'avail_min' is too %s: %u "
582 "msec (%lu frames at %u).\n",
583 frame_count == 0 ? "small" : "large",
584 msec_for_avail_min, frame_count,
585 frames_per_second);
586 return -EINVAL;
587 }
588 err = snd_pcm_sw_params_set_avail_min(state->handle,
589 state->sw_params, frame_count);
590 if (err < 0) {
591 logging(state,
592 "Fail to configure 'avail-min'.\n");
593 return -EINVAL;
594 }
595 }
596
597 if (msec_for_start_threshold > 0) {
598 frame_count = msec_for_start_threshold * frames_per_second /
599 1000000;
600 if (frame_count == 0 || frame_count > frames_per_buffer) {
601 logging(state,
602 "The msec for 'start-delay' is too %s: %u "
603 "msec (%lu frames at %u).\n",
604 frame_count == 0 ? "small" : "large",
605 msec_for_start_threshold, frame_count,
606 frames_per_second);
607 return -EINVAL;
608 }
609 err = snd_pcm_sw_params_set_start_threshold(state->handle,
610 state->sw_params, frame_count);
611 if (err < 0) {
612 logging(state,
613 "Fail to configure 'start-delay'.\n");
614 return -EINVAL;
615 }
616 }
617
618 if (msec_for_stop_threshold > 0) {
619 frame_count = msec_for_stop_threshold * frames_per_second /
620 1000000;
621 if (frame_count == 0 || frame_count > frames_per_buffer) {
622 logging(state,
623 "The msec for 'stop-delay' is too %s: %u "
624 "msec (%lu frames at %u).\n",
625 frame_count == 0 ? "small" : "large",
626 msec_for_stop_threshold, frame_count,
627 frames_per_second);
628 return -EINVAL;
629 }
630 err = snd_pcm_sw_params_set_stop_threshold(state->handle,
631 state->sw_params, frame_count);
632 if (err < 0) {
633 logging(state,
634 "Fail to configure 'stop-delay'.\n");
635 return -EINVAL;
636 }
637 }
638
639 return snd_pcm_sw_params(state->handle, state->sw_params);
640 }
641
xfer_libasound_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)642 static int xfer_libasound_pre_process(struct xfer_context *xfer,
643 snd_pcm_format_t *format,
644 unsigned int *samples_per_frame,
645 unsigned int *frames_per_second,
646 snd_pcm_access_t *access,
647 snd_pcm_uframes_t *frames_per_buffer)
648 {
649 struct libasound_state *state = xfer->private_data;
650 unsigned int flag;
651 int err;
652
653 err = open_handle(xfer);
654 if (err < 0)
655 return -ENXIO;
656
657 err = configure_hw_params(state, *format, *samples_per_frame,
658 *frames_per_second,
659 state->msec_per_period,
660 state->msec_per_buffer,
661 state->frames_per_period,
662 state->frames_per_buffer);
663 if (err < 0) {
664 logging(state, "Current hardware parameters:\n");
665 snd_pcm_hw_params_dump(state->hw_params, state->log);
666 return err;
667 }
668
669 // Retrieve actual parameters.
670 err = retrieve_actual_hw_params(state->hw_params, format,
671 samples_per_frame, frames_per_second,
672 access, frames_per_buffer);
673 if (err < 0)
674 return err;
675
676 // Query software parameters.
677 err = snd_pcm_sw_params_current(state->handle, state->sw_params);
678 if (err < 0)
679 return err;
680
681 // Assign I/O operation.
682 err = snd_pcm_hw_params_get_period_wakeup(state->handle,
683 state->hw_params, &flag);
684 if (err < 0)
685 return err;
686
687 if (flag) {
688 if (*access == SND_PCM_ACCESS_RW_INTERLEAVED ||
689 *access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
690 state->ops = &xfer_libasound_irq_rw_ops;
691 } else if (*access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
692 *access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) {
693 if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE)
694 state->ops = &xfer_libasound_irq_mmap_r_ops;
695 else
696 state->ops = &xfer_libasound_irq_mmap_w_ops;
697 } else {
698 return -ENXIO;
699 }
700 } else {
701 if (*access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
702 *access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) {
703 if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE)
704 state->ops = &xfer_libasound_timer_mmap_r_ops;
705 else
706 state->ops = &xfer_libasound_timer_mmap_w_ops;
707 } else {
708 return -ENXIO;
709 }
710 }
711
712 if (state->ops->private_size > 0) {
713 state->private_data = malloc(state->ops->private_size);
714 if (state->private_data == NULL)
715 return -ENOMEM;
716 memset(state->private_data, 0, state->ops->private_size);
717 }
718
719 err = state->ops->pre_process(state);
720 if (err < 0)
721 return err;
722
723 err = configure_sw_params(state, *frames_per_second,
724 *frames_per_buffer,
725 state->msec_for_avail_min,
726 state->msec_for_start_threshold,
727 state->msec_for_stop_threshold);
728 if (err < 0) {
729 logging(state, "Current software parameters:\n");
730 snd_pcm_sw_params_dump(state->sw_params, state->log);
731 return err;
732 }
733
734 if (xfer->verbose > 0) {
735 snd_pcm_dump(state->handle, state->log);
736 logging(state, "Scheduling model:\n");
737 logging(state, " %s\n", sched_model_labels[state->sched_model]);
738 }
739
740 if (state->use_waiter) {
741 // NOTE: This should be after configuring sw_params due to
742 // timer descriptor for time-based scheduling model.
743 err = prepare_waiter(state);
744 if (err < 0)
745 return err;
746
747 if (xfer->verbose > 0) {
748 logging(state, "Waiter type:\n");
749 logging(state,
750 " %s\n",
751 waiter_label_from_type(state->waiter_type));
752 }
753 }
754
755 return 0;
756 }
757
xfer_libasound_process_frames(struct xfer_context * xfer,unsigned int * frame_count,struct mapper_context * mapper,struct container_context * cntrs)758 static int xfer_libasound_process_frames(struct xfer_context *xfer,
759 unsigned int *frame_count,
760 struct mapper_context *mapper,
761 struct container_context *cntrs)
762 {
763 struct libasound_state *state = xfer->private_data;
764 int err;
765
766 if (state->handle == NULL)
767 return -ENXIO;
768
769 err = state->ops->process_frames(state, frame_count, mapper, cntrs);
770 if (err < 0) {
771 if (err == -EAGAIN)
772 return err;
773 if (err == -EPIPE && !state->finish_at_xrun) {
774 // Recover the stream and continue processing
775 // immediately. In this program -EPIPE comes from
776 // libasound implementation instead of file I/O.
777 err = snd_pcm_prepare(state->handle);
778 }
779
780 if (err < 0) {
781 // TODO: -EIO from libasound for hw PCM node means
782 // that IRQ disorder. This should be reported to help
783 // developers for drivers.
784 logging(state, "Fail to process frames: %s\n",
785 snd_strerror(err));
786 }
787 }
788
789 return err;
790 }
791
xfer_libasound_pause(struct xfer_context * xfer,bool enable)792 static void xfer_libasound_pause(struct xfer_context *xfer, bool enable)
793 {
794 struct libasound_state *state = xfer->private_data;
795 snd_pcm_state_t s = snd_pcm_state(state->handle);
796 int err;
797
798 if (state->handle == NULL)
799 return;
800
801 if (enable) {
802 if (s != SND_PCM_STATE_RUNNING)
803 return;
804 } else {
805 if (s != SND_PCM_STATE_PAUSED)
806 return;
807 }
808
809 // Not supported. Leave the substream to enter XRUN state.
810 if (!snd_pcm_hw_params_can_pause(state->hw_params))
811 return;
812
813 err = snd_pcm_pause(state->handle, enable);
814 if (err < 0 && state->verbose) {
815 logging(state, "snd_pcm_pause(): %s\n", snd_strerror(err));
816 }
817 }
818
xfer_libasound_post_process(struct xfer_context * xfer)819 static void xfer_libasound_post_process(struct xfer_context *xfer)
820 {
821 struct libasound_state *state = xfer->private_data;
822 snd_pcm_state_t pcm_state;
823 int err;
824
825 if (state->handle == NULL)
826 return;
827
828 pcm_state = snd_pcm_state(state->handle);
829 if (pcm_state != SND_PCM_STATE_OPEN &&
830 pcm_state != SND_PCM_STATE_DISCONNECTED) {
831 if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE ||
832 state->ops == &xfer_libasound_timer_mmap_w_ops) {
833 err = snd_pcm_drop(state->handle);
834 if (err < 0)
835 logging(state, "snd_pcm_drop(): %s\n",
836 snd_strerror(err));
837 } else {
838 // TODO: this is a bug in kernel land.
839 if (state->nonblock)
840 snd_pcm_nonblock(state->handle, 0);
841 err = snd_pcm_drain(state->handle);
842 if (state->nonblock)
843 snd_pcm_nonblock(state->handle, 1);
844 if (err < 0)
845 logging(state, "snd_pcm_drain(): %s\n",
846 snd_strerror(err));
847 }
848 }
849
850 err = snd_pcm_hw_free(state->handle);
851 if (err < 0)
852 logging(state, "snd_pcm_hw_free(): %s\n", snd_strerror(err));
853
854 snd_pcm_close(state->handle);
855 state->handle = NULL;
856
857 if (state->ops && state->ops->post_process)
858 state->ops->post_process(state);
859 free(state->private_data);
860 state->private_data = NULL;
861
862 // Free cache of content for configuration files so that memory leaks
863 // are not detected.
864 snd_config_update_free_global();
865 }
866
xfer_libasound_destroy(struct xfer_context * xfer)867 static void xfer_libasound_destroy(struct xfer_context *xfer)
868 {
869 struct libasound_state *state = xfer->private_data;
870
871 free(state->node_literal);
872 free(state->waiter_type_literal);
873 free(state->sched_model_literal);
874 state->node_literal = NULL;
875 state->waiter_type_literal = NULL;
876 state->sched_model_literal = NULL;
877
878 if (state->hw_params)
879 snd_pcm_hw_params_free(state->hw_params);
880 if (state->sw_params)
881 snd_pcm_sw_params_free(state->sw_params);
882 state->hw_params = NULL;
883 state->sw_params = NULL;
884
885 if (state->log)
886 snd_output_close(state->log);
887 state->log = NULL;
888 }
889
xfer_libasound_help(struct xfer_context * xfer)890 static void xfer_libasound_help(struct xfer_context *xfer)
891 {
892 printf(
893 " [BASICS]\n"
894 " -D, --device select node by name in coniguration space\n"
895 " -N, --nonblock nonblocking mode\n"
896 " -M, --mmap use mmap(2) for zero copying technique\n"
897 " -F, --period-time interval between interrupts (msec unit)\n"
898 " --period-size interval between interrupts (frame unit)\n"
899 " -B, --buffer-time size of buffer for frame(msec unit)\n"
900 " --buffer-size size of buffer for frame(frame unit)\n"
901 " --waiter-type type of waiter to handle available frames\n"
902 " --sched-model model of process scheduling\n"
903 " [SOFTWARE FEATURES]\n"
904 " -A, --avail-min threshold of frames to wake up process\n"
905 " -R, --start-delay threshold of frames to start PCM substream\n"
906 " -T, --stop-delay threshold of frames to stop PCM substream\n"
907 " [LIBASOUND PLUGIN OPTIONS]\n"
908 " --disable-resample disable rate conversion for plug plugin\n"
909 " --disable-channels disable channel conversion for plug plugin\n"
910 " --disable-format disable format conversion for plug plugin\n"
911 " --disable-softvol disable software volume for sofvol plugin\n"
912 " [DEBUG ASSISTANT]\n"
913 " --fatal-errors finish at XRUN\n"
914 " --test-nowait busy poll without any waiter\n"
915 );
916 }
917
918 const struct xfer_data xfer_libasound = {
919 .s_opts = S_OPTS,
920 .l_opts = l_opts,
921 .l_opts_count = ARRAY_SIZE(l_opts),
922 .ops = {
923 .init = xfer_libasound_init,
924 .parse_opt = xfer_libasound_parse_opt,
925 .validate_opts = xfer_libasound_validate_opts,
926 .pre_process = xfer_libasound_pre_process,
927 .process_frames = xfer_libasound_process_frames,
928 .pause = xfer_libasound_pause,
929 .post_process = xfer_libasound_post_process,
930 .destroy = xfer_libasound_destroy,
931 .help = xfer_libasound_help,
932 },
933 .private_size = sizeof(struct libasound_state),
934 };
935