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