1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #include <alsa/asoundlib.h>
7 #include <limits.h>
8 #include <stdlib.h>
9 #include <syslog.h>
10
11 #include "cras_alsa_helpers.h"
12 #include "cras_audio_format.h"
13 #include "cras_util.h"
14
15 /* Macro to convert between snd_pcm_chmap_position(defined in
16 * alsa-lib since 1.0.27) and CRAS_CHANNEL, values of which are
17 * of the same order but shifted by 3.
18 */
19 #define CH_TO_ALSA(ch) ((ch) + 3)
20 #define CH_TO_CRAS(ch) ((ch) - 3)
21
22 /* Assert the channel is defined in CRAS_CHANNELS. */
23 #define ALSA_CH_VALID(ch) ((ch >= SND_CHMAP_FL) && (ch <= SND_CHMAP_FRC))
24
25 /* Time difference between two consecutive underrun logs. */
26 #define UNDERRUN_LOG_TIME_SECS 30
27
28 /* Chances to give mmap_begin to work. */
29 static const size_t MAX_MMAP_BEGIN_ATTEMPTS = 3;
30 /* Time to sleep between resume attempts. */
31 static const size_t ALSA_SUSPENDED_SLEEP_TIME_US = 250000;
32
33 /* What rates should we check for on this dev?
34 * Listed in order of preference. 0 terminalted. */
35 static const size_t test_sample_rates[] = {
36 44100,
37 48000,
38 32000,
39 96000,
40 22050,
41 16000,
42 8000,
43 4000,
44 192000,
45 0
46 };
47
48 /* What channel counts shoud be checked on this dev?
49 * Listed in order of preference. 0 terminalted. */
50 static const size_t test_channel_counts[] = {
51 10,
52 6,
53 4,
54 2,
55 1,
56 8,
57 0
58 };
59
60 static const snd_pcm_format_t test_formats[] = {
61 SND_PCM_FORMAT_S16_LE,
62 SND_PCM_FORMAT_S24_LE,
63 SND_PCM_FORMAT_S32_LE,
64 SND_PCM_FORMAT_S24_3LE,
65 (snd_pcm_format_t)0
66 };
67
68 /* Looks up the list of channel map for the one can exactly matches
69 * the layout specified in fmt.
70 */
cras_chmap_caps_match(snd_pcm_chmap_query_t ** chmaps,struct cras_audio_format * fmt)71 static snd_pcm_chmap_query_t *cras_chmap_caps_match(
72 snd_pcm_chmap_query_t **chmaps,
73 struct cras_audio_format *fmt)
74 {
75 size_t ch, i;
76 int idx, matches;
77 snd_pcm_chmap_query_t **chmap;
78
79 /* Search for channel map that already matches the order */
80 for (chmap = chmaps; *chmap; chmap++) {
81 if ((*chmap)->map.channels != fmt->num_channels)
82 continue;
83
84 matches = 1;
85 for (ch = 0; ch < CRAS_CH_MAX; ch++) {
86 idx = fmt->channel_layout[ch];
87 if (idx == -1)
88 continue;
89 if ((unsigned)idx >= (*chmap)->map.channels)
90 continue;
91 if ((*chmap)->map.pos[idx] != CH_TO_ALSA(ch)) {
92 matches = 0;
93 break;
94 }
95 }
96 if (matches)
97 return *chmap;
98 }
99
100 /* Search for channel map that can arbitrarily swap order */
101 for (chmap = chmaps; *chmap; chmap++) {
102 if ((*chmap)->type == SND_CHMAP_TYPE_FIXED ||
103 (*chmap)->map.channels != fmt->num_channels)
104 continue;
105
106 matches = 1;
107 for (ch = 0; ch < CRAS_CH_MAX; ch++) {
108 idx = fmt->channel_layout[ch];
109 if (idx == -1)
110 continue;
111 int found = 0;
112 for (i = 0; i < fmt->num_channels; i++) {
113 if ((*chmap)->map.pos[i] == CH_TO_ALSA(ch)) {
114 found = 1;
115 break;
116 }
117 }
118 if (found == 0) {
119 matches = 0;
120 break;
121 }
122 }
123 if (matches && (*chmap)->type == SND_CHMAP_TYPE_VAR)
124 return *chmap;
125
126 /* Check if channel map is a match by arbitrarily swap
127 * pair order */
128 matches = 1;
129 for (i = 0; i < fmt->num_channels; i += 2) {
130 ch = CH_TO_CRAS((*chmap)->map.pos[i]);
131 if (fmt->channel_layout[ch] & 0x01) {
132 matches = 0;
133 break;
134 }
135
136 if (fmt->channel_layout[ch] + 1 !=
137 fmt->channel_layout[CH_TO_CRAS(
138 (*chmap)->map.pos[i + 1])]) {
139 matches = 0;
140 break;
141 }
142 }
143 if (matches)
144 return *chmap;
145 }
146
147 return NULL;
148 }
149
150 /* When the exact match does not exist, select the best valid
151 * channel map which can be supported by means of channel conversion
152 * matrix.
153 */
cras_chmap_caps_conv_matrix(snd_pcm_chmap_query_t ** chmaps,struct cras_audio_format * fmt)154 static snd_pcm_chmap_query_t *cras_chmap_caps_conv_matrix(
155 snd_pcm_chmap_query_t **chmaps,
156 struct cras_audio_format *fmt)
157 {
158 float **conv_mtx;
159 size_t i;
160 snd_pcm_chmap_query_t **chmap;
161 struct cras_audio_format *conv_fmt;
162
163 conv_fmt = cras_audio_format_create(fmt->format,
164 fmt->frame_rate, fmt->num_channels);
165
166 for (chmap = chmaps; *chmap; chmap++) {
167 if ((*chmap)->map.channels != fmt->num_channels)
168 continue;
169 for (i = 0; i < CRAS_CH_MAX; i++)
170 conv_fmt->channel_layout[i] = -1;
171 for (i = 0; i < conv_fmt->num_channels; i++) {
172 if (!ALSA_CH_VALID((*chmap)->map.pos[i]))
173 continue;
174 conv_fmt->channel_layout[CH_TO_CRAS(
175 (*chmap)->map.pos[i])] = i;
176 }
177
178 /* Examine channel map by test creating a conversion matrix
179 * for each candidate. Once a non-null matrix is created,
180 * that channel map is considered supported and select it as
181 * the best match one.
182 */
183 conv_mtx = cras_channel_conv_matrix_create(fmt, conv_fmt);
184 if (conv_mtx) {
185 cras_channel_conv_matrix_destroy(conv_mtx,
186 conv_fmt->num_channels);
187 cras_audio_format_destroy(conv_fmt);
188 return *chmap;
189 }
190 }
191
192 cras_audio_format_destroy(conv_fmt);
193 return NULL;
194 }
195
196 /* Finds the best channel map for given format and list of channel
197 * map capability.
198 */
cras_chmap_caps_best(snd_pcm_t * handle,snd_pcm_chmap_query_t ** chmaps,struct cras_audio_format * fmt)199 static snd_pcm_chmap_query_t *cras_chmap_caps_best(
200 snd_pcm_t *handle,
201 snd_pcm_chmap_query_t **chmaps,
202 struct cras_audio_format *fmt)
203 {
204 snd_pcm_chmap_query_t **chmap;
205 snd_pcm_chmap_query_t *match;
206
207 match = cras_chmap_caps_match(chmaps, fmt);
208 if (match)
209 return match;
210
211 match = cras_chmap_caps_conv_matrix(chmaps, fmt);
212 if (match)
213 return match;
214
215 /* For capture stream, choose the first chmap matching channel
216 * count. Channel positions reported in this chmap will be used
217 * to fill correspond channels into client stream.
218 */
219 if (snd_pcm_stream(handle) == SND_PCM_STREAM_CAPTURE)
220 for (chmap = chmaps; *chmap; chmap++)
221 if ((*chmap)->map.channels == fmt->num_channels)
222 return *chmap;
223 return NULL;
224 }
225
cras_alsa_pcm_open(snd_pcm_t ** handle,const char * dev,snd_pcm_stream_t stream)226 int cras_alsa_pcm_open(snd_pcm_t **handle, const char *dev,
227 snd_pcm_stream_t stream)
228 {
229 int rc;
230 int retries = 3;
231 static const unsigned int OPEN_RETRY_DELAY_US = 100000;
232
233 retry_open:
234 rc = snd_pcm_open(handle,
235 dev,
236 stream,
237 SND_PCM_NONBLOCK |
238 SND_PCM_NO_AUTO_RESAMPLE |
239 SND_PCM_NO_AUTO_CHANNELS |
240 SND_PCM_NO_AUTO_FORMAT);
241 if (rc == -EBUSY && --retries) {
242 usleep(OPEN_RETRY_DELAY_US);
243 goto retry_open;
244 }
245
246 return rc;
247 }
248
cras_alsa_pcm_close(snd_pcm_t * handle)249 int cras_alsa_pcm_close(snd_pcm_t *handle)
250 {
251 return snd_pcm_close(handle);
252 }
253
cras_alsa_pcm_start(snd_pcm_t * handle)254 int cras_alsa_pcm_start(snd_pcm_t *handle)
255 {
256 return snd_pcm_start(handle);
257 }
258
cras_alsa_pcm_drain(snd_pcm_t * handle)259 int cras_alsa_pcm_drain(snd_pcm_t *handle)
260 {
261 return snd_pcm_drain(handle);
262 }
263
cras_alsa_resume_appl_ptr(snd_pcm_t * handle,snd_pcm_uframes_t ahead)264 int cras_alsa_resume_appl_ptr(snd_pcm_t *handle, snd_pcm_uframes_t ahead)
265 {
266 int rc;
267 snd_pcm_uframes_t period_frames, buffer_frames;
268 snd_pcm_sframes_t to_move, avail_frames;
269 rc = snd_pcm_avail(handle);
270 if (rc == -EPIPE || rc == -ESTRPIPE) {
271 cras_alsa_attempt_resume(handle);
272 avail_frames = 0;
273 } else if (rc < 0) {
274 syslog(LOG_ERR, "Fail to get avail frames: %s",
275 snd_strerror(rc));
276 return rc;
277 } else {
278 avail_frames = rc;
279 }
280
281 rc = snd_pcm_get_params(handle, &buffer_frames, &period_frames);
282 if (rc < 0) {
283 syslog(LOG_ERR, "Fail to get buffer size: %s",
284 snd_strerror(rc));
285 return rc;
286 }
287
288 to_move = avail_frames - buffer_frames + ahead;
289 if (to_move > 0) {
290 rc = snd_pcm_forward(handle, to_move);
291 } else if (to_move < 0) {
292 rc = snd_pcm_rewind(handle, -to_move);
293 } else {
294 return 0;
295 }
296
297 if (rc < 0) {
298 syslog(LOG_ERR, "Fail to resume appl_ptr: %s",
299 snd_strerror(rc));
300 return rc;
301 }
302 return 0;
303 }
304
cras_alsa_set_channel_map(snd_pcm_t * handle,struct cras_audio_format * fmt)305 int cras_alsa_set_channel_map(snd_pcm_t *handle,
306 struct cras_audio_format *fmt)
307 {
308 size_t i, ch;
309 snd_pcm_chmap_query_t **chmaps;
310 snd_pcm_chmap_query_t *match;
311
312 if (fmt->num_channels <= 2)
313 return 0;
314
315 chmaps = snd_pcm_query_chmaps(handle);
316 if (chmaps == NULL) {
317 syslog(LOG_WARNING, "No chmap queried! Skip chmap set");
318 goto done;
319 }
320
321 match = cras_chmap_caps_best(handle, chmaps, fmt);
322 if (!match) {
323 syslog(LOG_ERR, "Unable to find the best channel map");
324 goto done;
325 }
326
327 /* A channel map could match the layout after channels
328 * pair/arbitrary swapped. Modified the channel positions
329 * before set to HW.
330 */
331 for (i = 0; i < fmt->num_channels; i++) {
332 for (ch = 0; ch < CRAS_CH_MAX; ch++)
333 if (fmt->channel_layout[ch] == (int)i)
334 break;
335 if (ch != CRAS_CH_MAX)
336 match->map.pos[i] = CH_TO_ALSA(ch);
337 }
338 if (snd_pcm_set_chmap(handle, &match->map) != 0)
339 syslog(LOG_ERR, "Unable to set channel map");
340
341 done:
342 snd_pcm_free_chmaps(chmaps);
343 return 0;
344 }
345
cras_alsa_get_channel_map(snd_pcm_t * handle,struct cras_audio_format * fmt)346 int cras_alsa_get_channel_map(snd_pcm_t *handle,
347 struct cras_audio_format *fmt)
348 {
349 snd_pcm_chmap_query_t **chmaps;
350 snd_pcm_chmap_query_t *match;
351 int rc = 0;
352 size_t i;
353
354 chmaps = snd_pcm_query_chmaps(handle);
355 if (chmaps == NULL) {
356 rc = -EINVAL;
357 goto done;
358 }
359
360 match = cras_chmap_caps_best(handle, chmaps, fmt);
361 if (!match) {
362 syslog(LOG_ERR, "Unable to find the best channel map");
363 rc = -1;
364 goto done;
365 }
366
367 /* Fill back the selected channel map so channel converter can
368 * handle it. */
369 for (i = 0; i < CRAS_CH_MAX; i++)
370 fmt->channel_layout[i] = -1;
371 for (i = 0; i < fmt->num_channels; i++) {
372 if (!ALSA_CH_VALID(match->map.pos[i]))
373 continue;
374 fmt->channel_layout[CH_TO_CRAS(match->map.pos[i])] = i;
375 }
376
377 /* Handle the special channel map {SND_CHMAP_MONO} */
378 if (match->map.channels == 1 && match->map.pos[0] == SND_CHMAP_MONO)
379 fmt->channel_layout[CRAS_CH_FC] = 0;
380
381 done:
382 snd_pcm_free_chmaps(chmaps);
383 return rc;
384 }
385
cras_alsa_fill_properties(snd_pcm_t * handle,size_t ** rates,size_t ** channel_counts,snd_pcm_format_t ** formats)386 int cras_alsa_fill_properties(snd_pcm_t *handle,
387 size_t **rates, size_t **channel_counts,
388 snd_pcm_format_t **formats)
389 {
390 int rc;
391 size_t i, num_found;
392 snd_pcm_hw_params_t *params;
393
394 snd_pcm_hw_params_alloca(¶ms);
395
396 rc = snd_pcm_hw_params_any(handle, params);
397 if (rc < 0) {
398 syslog(LOG_ERR, "snd_pcm_hw_params_any: %s", snd_strerror(rc));
399 return rc;
400 }
401
402 *rates = (size_t *)malloc(sizeof(test_sample_rates));
403 if (*rates == NULL)
404 return -ENOMEM;
405 *channel_counts = (size_t *)malloc(sizeof(test_channel_counts));
406 if (*channel_counts == NULL) {
407 free(*rates);
408 return -ENOMEM;
409 }
410 *formats = (snd_pcm_format_t *)malloc(sizeof(test_formats));
411 if (*formats == NULL) {
412 free(*channel_counts);
413 free(*rates);
414 return -ENOMEM;
415 }
416
417 num_found = 0;
418 for (i = 0; test_sample_rates[i] != 0; i++) {
419 rc = snd_pcm_hw_params_test_rate(handle, params,
420 test_sample_rates[i], 0);
421 if (rc == 0)
422 (*rates)[num_found++] = test_sample_rates[i];
423 }
424 (*rates)[num_found] = 0;
425 if (num_found == 0) {
426 syslog(LOG_WARNING, "No valid sample rates.");
427 return -EINVAL;
428 }
429
430 num_found = 0;
431 for (i = 0; test_channel_counts[i] != 0; i++) {
432 rc = snd_pcm_hw_params_test_channels(handle, params,
433 test_channel_counts[i]);
434 if (rc == 0)
435 (*channel_counts)[num_found++] = test_channel_counts[i];
436 }
437 (*channel_counts)[num_found] = 0;
438 if (num_found == 0) {
439 syslog(LOG_WARNING, "No valid channel counts found.");
440 return -EINVAL;
441 }
442
443 num_found = 0;
444 for (i = 0; test_formats[i] != 0; i++) {
445 rc = snd_pcm_hw_params_test_format(handle, params,
446 test_formats[i]);
447 if (rc == 0)
448 (*formats)[num_found++] = test_formats[i];
449 }
450 (*formats)[num_found] = (snd_pcm_format_t)0;
451 if (num_found == 0) {
452 syslog(LOG_WARNING, "No valid sample formats.");
453 return -EINVAL;
454 }
455
456 return 0;
457 }
458
cras_alsa_set_hwparams(snd_pcm_t * handle,struct cras_audio_format * format,snd_pcm_uframes_t * buffer_frames,int period_wakeup,unsigned int dma_period_time)459 int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format,
460 snd_pcm_uframes_t *buffer_frames, int period_wakeup,
461 unsigned int dma_period_time)
462 {
463 unsigned int rate, ret_rate;
464 int err;
465 snd_pcm_hw_params_t *hwparams;
466
467 rate = format->frame_rate;
468 snd_pcm_hw_params_alloca(&hwparams);
469
470 err = snd_pcm_hw_params_any(handle, hwparams);
471 if (err < 0) {
472 syslog(LOG_ERR, "hw_params_any failed %s\n", snd_strerror(err));
473 return err;
474 }
475 /* Disable hardware resampling. */
476 err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, 0);
477 if (err < 0) {
478 syslog(LOG_ERR, "Disabling resampling %s\n", snd_strerror(err));
479 return err;
480 }
481 /* Always interleaved. */
482 err = snd_pcm_hw_params_set_access(handle, hwparams,
483 SND_PCM_ACCESS_MMAP_INTERLEAVED);
484 if (err < 0) {
485 syslog(LOG_ERR, "Setting interleaved %s\n", snd_strerror(err));
486 return err;
487 }
488 /* If period_wakeup flag is not set, try to disable ALSA wakeups,
489 * we'll keep a timer. */
490 if (!period_wakeup &&
491 snd_pcm_hw_params_can_disable_period_wakeup(hwparams)) {
492 err = snd_pcm_hw_params_set_period_wakeup(handle, hwparams, 0);
493 if (err < 0)
494 syslog(LOG_WARNING, "disabling wakeups %s\n",
495 snd_strerror(err));
496 }
497 /* Setup the period time so that the hardware pulls the right amount
498 * of data at the right time. */
499 if (dma_period_time) {
500 int dir = 0;
501 unsigned int original = dma_period_time;
502
503 err = snd_pcm_hw_params_set_period_time_near(
504 handle, hwparams, &dma_period_time, &dir);
505 if (err < 0) {
506 syslog(LOG_ERR, "could not set period time: %s",
507 snd_strerror(err));
508 return err;
509 } else if (original != dma_period_time) {
510 syslog(LOG_DEBUG, "period time set to: %u",
511 dma_period_time);
512 }
513 }
514 /* Set the sample format. */
515 err = snd_pcm_hw_params_set_format(handle, hwparams,
516 format->format);
517 if (err < 0) {
518 syslog(LOG_ERR, "set format %s\n", snd_strerror(err));
519 return err;
520 }
521 /* Set the stream rate. */
522 ret_rate = rate;
523 err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &ret_rate, 0);
524 if (err < 0) {
525 syslog(LOG_ERR, "set_rate_near %iHz %s\n", rate,
526 snd_strerror(err));
527 return err;
528 }
529 if (ret_rate != rate) {
530 syslog(LOG_ERR, "tried for %iHz, settled for %iHz)\n", rate,
531 ret_rate);
532 return -EINVAL;
533 }
534 /* Set the count of channels. */
535 err = snd_pcm_hw_params_set_channels(handle, hwparams,
536 format->num_channels);
537 if (err < 0) {
538 syslog(LOG_ERR, "set_channels %s\n", snd_strerror(err));
539 return err;
540 }
541
542 /* Make sure buffer frames is even, or snd_pcm_hw_params will
543 * return invalid argument error. */
544 err = snd_pcm_hw_params_get_buffer_size_max(hwparams,
545 buffer_frames);
546 if (err < 0)
547 syslog(LOG_WARNING, "get buffer max %s\n", snd_strerror(err));
548
549 *buffer_frames &= ~0x01;
550 err = snd_pcm_hw_params_set_buffer_size_max(handle, hwparams,
551 buffer_frames);
552 if (err < 0) {
553 syslog(LOG_ERR, "set_buffer_size_max %s", snd_strerror(err));
554 return err;
555 }
556
557 syslog(LOG_DEBUG, "buffer size set to %u\n",
558 (unsigned int)*buffer_frames);
559
560 /* Finally, write the parameters to the device. */
561 err = snd_pcm_hw_params(handle, hwparams);
562 if (err < 0) {
563 syslog(LOG_ERR, "hw_params: %s: rate: %u, ret_rate: %u, "
564 "channel: %zu, format: %u\n", snd_strerror(err), rate,
565 ret_rate, format->num_channels, format->format);
566 return err;
567 }
568 return 0;
569 }
570
cras_alsa_set_swparams(snd_pcm_t * handle,int * enable_htimestamp)571 int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp)
572 {
573 int err;
574 snd_pcm_sw_params_t *swparams;
575 snd_pcm_uframes_t boundary;
576
577 snd_pcm_sw_params_alloca(&swparams);
578
579 err = snd_pcm_sw_params_current(handle, swparams);
580 if (err < 0) {
581 syslog(LOG_ERR, "sw_params_current: %s\n", snd_strerror(err));
582 return err;
583 }
584 err = snd_pcm_sw_params_get_boundary(swparams, &boundary);
585 if (err < 0) {
586 syslog(LOG_ERR, "get_boundary: %s\n", snd_strerror(err));
587 return err;
588 }
589 err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, boundary);
590 if (err < 0) {
591 syslog(LOG_ERR, "set_stop_threshold: %s\n", snd_strerror(err));
592 return err;
593 }
594 /* Don't auto start. */
595 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, LONG_MAX);
596 if (err < 0) {
597 syslog(LOG_ERR, "set_stop_threshold: %s\n", snd_strerror(err));
598 return err;
599 }
600
601 /* Disable period events. */
602 err = snd_pcm_sw_params_set_period_event(handle, swparams, 0);
603 if (err < 0) {
604 syslog(LOG_ERR, "set_period_event: %s\n", snd_strerror(err));
605 return err;
606 }
607
608 if (*enable_htimestamp) {
609 /* Use MONOTONIC_RAW time-stamps. */
610 err = snd_pcm_sw_params_set_tstamp_type(
611 handle, swparams,
612 SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW);
613 if (err < 0) {
614 syslog(LOG_ERR, "set_tstamp_type: %s\n",
615 snd_strerror(err));
616 return err;
617 }
618 err = snd_pcm_sw_params_set_tstamp_mode(
619 handle, swparams, SND_PCM_TSTAMP_ENABLE);
620 if (err < 0) {
621 syslog(LOG_ERR, "set_tstamp_mode: %s\n",
622 snd_strerror(err));
623 return err;
624 }
625 }
626
627 /* This hack is required because ALSA-LIB does not provide any way to
628 * detect whether MONOTONIC_RAW timestamps are supported by the kernel.
629 * In ALSA-LIB, the code checks the hardware protocol version. */
630 err = snd_pcm_sw_params(handle, swparams);
631 if (err == -EINVAL && *enable_htimestamp) {
632 *enable_htimestamp = 0;
633 syslog(LOG_WARNING,
634 "MONOTONIC_RAW timestamps are not supported.");
635
636 err = snd_pcm_sw_params_set_tstamp_type(
637 handle, swparams,
638 SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY);
639 if (err < 0) {
640 syslog(LOG_ERR, "set_tstamp_type: %s\n",
641 snd_strerror(err));
642 return err;
643 }
644 err = snd_pcm_sw_params_set_tstamp_mode(
645 handle, swparams, SND_PCM_TSTAMP_NONE);
646 if (err < 0) {
647 syslog(LOG_ERR, "set_tstamp_mode: %s\n",
648 snd_strerror(err));
649 return err;
650 }
651
652 err = snd_pcm_sw_params(handle, swparams);
653 }
654
655 if (err < 0) {
656 syslog(LOG_ERR, "sw_params: %s\n", snd_strerror(err));
657 return err;
658 }
659 return 0;
660 }
661
cras_alsa_get_avail_frames(snd_pcm_t * handle,snd_pcm_uframes_t buf_size,snd_pcm_uframes_t severe_underrun_frames,const char * dev_name,snd_pcm_uframes_t * avail,struct timespec * tstamp)662 int cras_alsa_get_avail_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
663 snd_pcm_uframes_t severe_underrun_frames,
664 const char *dev_name,
665 snd_pcm_uframes_t *avail,
666 struct timespec *tstamp)
667 {
668 snd_pcm_sframes_t frames;
669 int rc = 0;
670 static struct timespec tstamp_last_underrun_log =
671 {.tv_sec = 0, .tv_nsec = 0};
672
673 /* Use snd_pcm_avail still to ensure that the hardware pointer is
674 * up to date. Otherwise, we could use the deprecated snd_pcm_hwsync().
675 * IMO this is a deficiency in the ALSA API.
676 */
677 frames = snd_pcm_avail(handle);
678 if (frames >= 0)
679 rc = snd_pcm_htimestamp(handle, avail, tstamp);
680 else
681 rc = frames;
682 if (rc == -EPIPE || rc == -ESTRPIPE) {
683 cras_alsa_attempt_resume(handle);
684 rc = 0;
685 goto error;
686 } else if (rc < 0) {
687 syslog(LOG_ERR, "pcm_avail error %s, %s\n",
688 dev_name, snd_strerror(rc));
689 goto error;
690 } else if (frames > (snd_pcm_sframes_t)buf_size) {
691 struct timespec tstamp_now;
692 clock_gettime(CLOCK_MONOTONIC_RAW, &tstamp_now);
693 /* Limit the log rate. */
694 if ((tstamp_now.tv_sec - tstamp_last_underrun_log.tv_sec) >
695 UNDERRUN_LOG_TIME_SECS) {
696 syslog(LOG_ERR,
697 "pcm_avail returned frames larger than buf_size: "
698 "%s: %ld > %lu\n",
699 dev_name, frames, buf_size);
700 tstamp_last_underrun_log.tv_sec = tstamp_now.tv_sec;
701 tstamp_last_underrun_log.tv_nsec = tstamp_now.tv_nsec;
702 }
703 if ((frames - (snd_pcm_sframes_t)buf_size) >
704 (snd_pcm_sframes_t)severe_underrun_frames) {
705 rc = -EPIPE;
706 goto error;
707 } else {
708 frames = buf_size;
709 }
710 }
711 *avail = frames;
712 return 0;
713
714 error:
715 *avail = 0;
716 tstamp->tv_sec = 0;
717 tstamp->tv_nsec = 0;
718 return rc;
719 }
720
cras_alsa_get_delay_frames(snd_pcm_t * handle,snd_pcm_uframes_t buf_size,snd_pcm_sframes_t * delay)721 int cras_alsa_get_delay_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
722 snd_pcm_sframes_t *delay)
723 {
724 int rc;
725
726 rc = snd_pcm_delay(handle, delay);
727 if (rc < 0)
728 return rc;
729 if (*delay > (snd_pcm_sframes_t)buf_size)
730 *delay = buf_size;
731 if (*delay < 0)
732 *delay = 0;
733 return 0;
734 }
735
736 /*
737 * Attempts to resume a PCM.
738 * Note that this path does not get executed for default playback/capture
739 * stream. Default playback/capture stream are removed from the device
740 * upon suspend, and re-attached to the device after resume.
741 * The only stream that lives across suspend resume is hotword stream.
742 */
cras_alsa_attempt_resume(snd_pcm_t * handle)743 int cras_alsa_attempt_resume(snd_pcm_t *handle)
744 {
745 int rc;
746
747 syslog(LOG_INFO, "System suspended.");
748 while ((rc = snd_pcm_resume(handle)) == -EAGAIN)
749 usleep(ALSA_SUSPENDED_SLEEP_TIME_US);
750 if (rc < 0) {
751 /*
752 * Some devices do not support snd_pcm_resume, that is
753 * acceptable.
754 */
755 syslog(LOG_INFO, "System suspended, failed to resume %s.",
756 snd_strerror(rc));
757 rc = snd_pcm_prepare(handle);
758 if (rc < 0) {
759 syslog(LOG_ERR, "Suspended, failed to prepare: %s.",
760 snd_strerror(rc));
761 }
762 /*
763 * CRAS does not use auto-start (start_threshold = 0), so start
764 * PCM after it is prepared. This is only for hotword stream.
765 */
766 rc = snd_pcm_start(handle);
767 if (rc < 0) {
768 syslog(LOG_ERR, "Suspended, failed to start: %s.",
769 snd_strerror(rc));
770 }
771 }
772 return rc;
773 }
774
cras_alsa_mmap_get_whole_buffer(snd_pcm_t * handle,uint8_t ** dst)775 int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst)
776 {
777 snd_pcm_uframes_t offset;
778 /* The purpose of calling cras_alsa_mmap_begin is to get the base
779 * address of the buffer. The requested and retrieved frames are not
780 * meaningful here.
781 * However, we need to set a non-zero requested frames to get a
782 * non-zero retrieved frames. This is to avoid the error checking in
783 * snd_pcm_mmap_begin, where it judges retrieved frames being 0 as a
784 * failure.
785 */
786 snd_pcm_uframes_t frames = 1;
787
788 return cras_alsa_mmap_begin(handle, 0, dst, &offset, &frames);
789 }
790
cras_alsa_mmap_begin(snd_pcm_t * handle,unsigned int format_bytes,uint8_t ** dst,snd_pcm_uframes_t * offset,snd_pcm_uframes_t * frames)791 int cras_alsa_mmap_begin(snd_pcm_t *handle, unsigned int format_bytes,
792 uint8_t **dst, snd_pcm_uframes_t *offset,
793 snd_pcm_uframes_t *frames)
794 {
795 int rc;
796 unsigned int attempts = 0;
797 const snd_pcm_channel_area_t *my_areas;
798
799 while (attempts++ < MAX_MMAP_BEGIN_ATTEMPTS) {
800 rc = snd_pcm_mmap_begin(handle, &my_areas, offset, frames);
801 if (rc == -ESTRPIPE) {
802 /* First handle suspend/resume. */
803 rc = cras_alsa_attempt_resume(handle);
804 if (rc < 0)
805 return rc;
806 continue; /* Recovered from suspend, try again. */
807 } else if (rc < 0) {
808 /* If we can recover, continue and try again. */
809 if (snd_pcm_recover(handle, rc, 0) == 0)
810 continue;
811 syslog(LOG_INFO, "recover failed begin: %s\n",
812 snd_strerror(rc));
813 return rc;
814 }
815 /* Available frames could be zero right after input pcm handle
816 * resumed. As for output pcm handle, some error has occurred
817 * when mmap_begin return zero frames, return -EIO for that
818 * case.
819 */
820 if (snd_pcm_stream(handle) == SND_PCM_STREAM_PLAYBACK &&
821 *frames == 0) {
822 syslog(LOG_INFO, "mmap_begin set frames to 0.");
823 return -EIO;
824 }
825 *dst = (uint8_t *)my_areas[0].addr + (*offset) * format_bytes;
826 return 0;
827 }
828 return -EIO;
829 }
830
cras_alsa_mmap_commit(snd_pcm_t * handle,snd_pcm_uframes_t offset,snd_pcm_uframes_t frames)831 int cras_alsa_mmap_commit(snd_pcm_t *handle, snd_pcm_uframes_t offset,
832 snd_pcm_uframes_t frames)
833 {
834 int rc;
835 snd_pcm_sframes_t res;
836
837 res = snd_pcm_mmap_commit(handle, offset, frames);
838 if (res != (snd_pcm_sframes_t)frames) {
839 res = res >= 0 ? (int)-EPIPE : res;
840 if (res == -ESTRPIPE) {
841 /* First handle suspend/resume. */
842 rc = cras_alsa_attempt_resume(handle);
843 if (rc < 0)
844 return rc;
845 } else {
846 /* If we can recover, continue and try again. */
847 rc = snd_pcm_recover(handle, res, 0);
848 if (rc < 0) {
849 syslog(LOG_ERR,
850 "mmap_commit: pcm_recover failed: %s\n",
851 snd_strerror(rc));
852 return rc;
853 }
854 }
855 }
856 return 0;
857 }
858