summaryrefslogtreecommitdiff
path: root/cras/src/server/cras_alsa_helpers.c
diff options
context:
space:
mode:
Diffstat (limited to 'cras/src/server/cras_alsa_helpers.c')
-rw-r--r--cras/src/server/cras_alsa_helpers.c799
1 files changed, 0 insertions, 799 deletions
diff --git a/cras/src/server/cras_alsa_helpers.c b/cras/src/server/cras_alsa_helpers.c
deleted file mode 100644
index 6cdc165a..00000000
--- a/cras/src/server/cras_alsa_helpers.c
+++ /dev/null
@@ -1,799 +0,0 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <alsa/asoundlib.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <syslog.h>
-
-#include "cras_alsa_helpers.h"
-#include "cras_audio_format.h"
-#include "cras_util.h"
-
-/* Macro to convert between snd_pcm_chmap_position(defined in
- * alsa-lib since 1.0.27) and CRAS_CHANNEL, values of which are
- * of the same order but shifted by 3.
- */
-#define CH_TO_ALSA(ch) ((ch) + (3))
-#define CH_TO_CRAS(ch) ((ch) - (3))
-
-/* Assert the channel is defined in CRAS_CHANNELS. */
-#define ALSA_CH_VALID(ch) ((ch >= SND_CHMAP_FL) && (ch <= SND_CHMAP_FRC))
-
-/* Time difference between two consecutive underrun logs. */
-#define UNDERRUN_LOG_TIME_SECS 30
-
-/* Limit the number of channels supported for devices: b/158509536 */
-#define TEMP_CHANNEL_LIMIT 20
-
-/* Chances to give mmap_begin to work. */
-static const size_t MAX_MMAP_BEGIN_ATTEMPTS = 3;
-/* Time to sleep between resume attempts. */
-static const size_t ALSA_SUSPENDED_SLEEP_TIME_US = 250000;
-
-/* What rates should we check for on this dev?
- * Listed in order of preference. 0 terminalted. */
-static const size_t test_sample_rates[] = {
- 44100, 48000, 32000, 96000, 22050, 16000, 8000, 4000, 192000, 0,
-};
-
-/* What channel counts shoud be checked on this dev?
- * Listed in order of preference. 0 terminalted. */
-static const size_t test_channel_counts[] = { 10, 6, 4, 2, 1, 8, 0 };
-
-static const snd_pcm_format_t test_formats[] = {
- SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S32_LE,
- SND_PCM_FORMAT_S24_3LE, (snd_pcm_format_t)0
-};
-
-/* Looks up the list of channel map for the one can exactly matches
- * the layout specified in fmt.
- */
-static snd_pcm_chmap_query_t *
-cras_chmap_caps_match(snd_pcm_chmap_query_t **chmaps,
- struct cras_audio_format *fmt)
-{
- size_t ch, i;
- int idx, matches;
- snd_pcm_chmap_query_t **chmap;
-
- /* Search for channel map that already matches the order */
- for (chmap = chmaps; *chmap; chmap++) {
- if ((*chmap)->map.channels != fmt->num_channels)
- continue;
-
- matches = 1;
- for (ch = 0; ch < CRAS_CH_MAX; ch++) {
- idx = fmt->channel_layout[ch];
- if (idx == -1)
- continue;
- if ((unsigned)idx >= (*chmap)->map.channels)
- continue;
- if ((*chmap)->map.pos[idx] != CH_TO_ALSA(ch)) {
- matches = 0;
- break;
- }
- }
- if (matches)
- return *chmap;
- }
-
- /* Search for channel map that can arbitrarily swap order */
- for (chmap = chmaps; *chmap; chmap++) {
- if ((*chmap)->type == SND_CHMAP_TYPE_FIXED ||
- (*chmap)->map.channels != fmt->num_channels)
- continue;
-
- matches = 1;
- for (ch = 0; ch < CRAS_CH_MAX; ch++) {
- idx = fmt->channel_layout[ch];
- if (idx == -1)
- continue;
- int found = 0;
- for (i = 0; i < fmt->num_channels; i++) {
- if ((*chmap)->map.pos[i] == CH_TO_ALSA(ch)) {
- found = 1;
- break;
- }
- }
- if (found == 0) {
- matches = 0;
- break;
- }
- }
- if (matches && (*chmap)->type == SND_CHMAP_TYPE_VAR)
- return *chmap;
-
- /* Check if channel map is a match by arbitrarily swap
- * pair order */
- matches = 1;
- for (i = 0; i < fmt->num_channels; i += 2) {
- ch = CH_TO_CRAS((*chmap)->map.pos[i]);
- if (fmt->channel_layout[ch] & 0x01) {
- matches = 0;
- break;
- }
-
- if (fmt->channel_layout[ch] + 1 !=
- fmt->channel_layout[CH_TO_CRAS(
- (*chmap)->map.pos[i + 1])]) {
- matches = 0;
- break;
- }
- }
- if (matches)
- return *chmap;
- }
-
- return NULL;
-}
-
-/* When the exact match does not exist, select the best valid
- * channel map which can be supported by means of channel conversion
- * matrix.
- */
-static snd_pcm_chmap_query_t *
-cras_chmap_caps_conv_matrix(snd_pcm_chmap_query_t **chmaps,
- struct cras_audio_format *fmt)
-{
- float **conv_mtx;
- size_t i;
- snd_pcm_chmap_query_t **chmap;
- struct cras_audio_format *conv_fmt;
-
- conv_fmt = cras_audio_format_create(fmt->format, fmt->frame_rate,
- fmt->num_channels);
-
- for (chmap = chmaps; *chmap; chmap++) {
- if ((*chmap)->map.channels != fmt->num_channels)
- continue;
- for (i = 0; i < CRAS_CH_MAX; i++)
- conv_fmt->channel_layout[i] = -1;
- for (i = 0; i < conv_fmt->num_channels; i++) {
- if (!ALSA_CH_VALID((*chmap)->map.pos[i]))
- continue;
- conv_fmt->channel_layout[CH_TO_CRAS(
- (*chmap)->map.pos[i])] = i;
- }
-
- /* Examine channel map by test creating a conversion matrix
- * for each candidate. Once a non-null matrix is created,
- * that channel map is considered supported and select it as
- * the best match one.
- */
- conv_mtx = cras_channel_conv_matrix_create(fmt, conv_fmt);
- if (conv_mtx) {
- cras_channel_conv_matrix_destroy(
- conv_mtx, conv_fmt->num_channels);
- cras_audio_format_destroy(conv_fmt);
- return *chmap;
- }
- }
-
- cras_audio_format_destroy(conv_fmt);
- return NULL;
-}
-
-/* Finds the best channel map for given format and list of channel
- * map capability.
- */
-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)
-{
- snd_pcm_chmap_query_t **chmap;
- snd_pcm_chmap_query_t *match;
-
- match = cras_chmap_caps_match(chmaps, fmt);
- if (match)
- return match;
-
- match = cras_chmap_caps_conv_matrix(chmaps, fmt);
- if (match)
- return match;
-
- /* For capture stream, choose the first chmap matching channel
- * count. Channel positions reported in this chmap will be used
- * to fill correspond channels into client stream.
- */
- if (snd_pcm_stream(handle) == SND_PCM_STREAM_CAPTURE)
- for (chmap = chmaps; *chmap; chmap++)
- if ((*chmap)->map.channels == fmt->num_channels)
- return *chmap;
- return NULL;
-}
-
-int cras_alsa_pcm_open(snd_pcm_t **handle, const char *dev,
- snd_pcm_stream_t stream)
-{
- int rc;
- int retries = 3;
- static const unsigned int OPEN_RETRY_DELAY_US = 100000;
-
-retry_open:
- rc = snd_pcm_open(handle, dev, stream,
- SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE |
- SND_PCM_NO_AUTO_CHANNELS |
- SND_PCM_NO_AUTO_FORMAT);
- if (rc == -EBUSY && --retries) {
- usleep(OPEN_RETRY_DELAY_US);
- goto retry_open;
- }
-
- return rc;
-}
-
-int cras_alsa_pcm_close(snd_pcm_t *handle)
-{
- return snd_pcm_close(handle);
-}
-
-int cras_alsa_pcm_start(snd_pcm_t *handle)
-{
- return snd_pcm_start(handle);
-}
-
-int cras_alsa_pcm_drain(snd_pcm_t *handle)
-{
- return snd_pcm_drain(handle);
-}
-
-int cras_alsa_resume_appl_ptr(snd_pcm_t *handle, snd_pcm_uframes_t ahead)
-{
- int rc;
- snd_pcm_uframes_t period_frames, buffer_frames;
- snd_pcm_sframes_t to_move, avail_frames;
- rc = snd_pcm_avail(handle);
- if (rc == -EPIPE || rc == -ESTRPIPE) {
- cras_alsa_attempt_resume(handle);
- avail_frames = 0;
- } else if (rc < 0) {
- syslog(LOG_ERR, "Fail to get avail frames: %s",
- snd_strerror(rc));
- return rc;
- } else {
- avail_frames = rc;
- }
-
- rc = snd_pcm_get_params(handle, &buffer_frames, &period_frames);
- if (rc < 0) {
- syslog(LOG_ERR, "Fail to get buffer size: %s",
- snd_strerror(rc));
- return rc;
- }
-
- to_move = avail_frames - buffer_frames + ahead;
- if (to_move > 0) {
- rc = snd_pcm_forward(handle, to_move);
- } else if (to_move < 0) {
- rc = snd_pcm_rewind(handle, -to_move);
- } else {
- return 0;
- }
-
- if (rc < 0) {
- syslog(LOG_ERR, "Fail to resume appl_ptr: %s",
- snd_strerror(rc));
- return rc;
- }
- return 0;
-}
-
-int cras_alsa_set_channel_map(snd_pcm_t *handle, struct cras_audio_format *fmt)
-{
- size_t i, ch;
- snd_pcm_chmap_query_t **chmaps;
- snd_pcm_chmap_query_t *match;
-
- if (fmt->num_channels <= 2)
- return 0;
-
- chmaps = snd_pcm_query_chmaps(handle);
- if (chmaps == NULL) {
- syslog(LOG_WARNING, "No chmap queried! Skip chmap set");
- goto done;
- }
-
- match = cras_chmap_caps_best(handle, chmaps, fmt);
- if (!match) {
- syslog(LOG_ERR, "Unable to find the best channel map");
- goto done;
- }
-
- /* A channel map could match the layout after channels
- * pair/arbitrary swapped. Modified the channel positions
- * before set to HW.
- */
- for (i = 0; i < fmt->num_channels; i++) {
- for (ch = 0; ch < CRAS_CH_MAX; ch++)
- if (fmt->channel_layout[ch] == (int)i)
- break;
- if (ch != CRAS_CH_MAX)
- match->map.pos[i] = CH_TO_ALSA(ch);
- }
- if (snd_pcm_set_chmap(handle, &match->map) != 0)
- syslog(LOG_ERR, "Unable to set channel map");
-
-done:
- snd_pcm_free_chmaps(chmaps);
- return 0;
-}
-
-int cras_alsa_get_channel_map(snd_pcm_t *handle, struct cras_audio_format *fmt)
-{
- snd_pcm_chmap_query_t **chmaps;
- snd_pcm_chmap_query_t *match;
- int rc = 0;
- size_t i;
-
- chmaps = snd_pcm_query_chmaps(handle);
- if (chmaps == NULL) {
- rc = -EINVAL;
- goto done;
- }
-
- match = cras_chmap_caps_best(handle, chmaps, fmt);
- if (!match) {
- syslog(LOG_ERR, "Unable to find the best channel map");
- rc = -1;
- goto done;
- }
-
- /* Fill back the selected channel map so channel converter can
- * handle it. */
- for (i = 0; i < CRAS_CH_MAX; i++)
- fmt->channel_layout[i] = -1;
- for (i = 0; i < fmt->num_channels; i++) {
- if (!ALSA_CH_VALID(match->map.pos[i]))
- continue;
- fmt->channel_layout[CH_TO_CRAS(match->map.pos[i])] = i;
- }
-
- /* Handle the special channel map {SND_CHMAP_MONO} */
- if (match->map.channels == 1 && match->map.pos[0] == SND_CHMAP_MONO)
- fmt->channel_layout[CRAS_CH_FC] = 0;
-
-done:
- snd_pcm_free_chmaps(chmaps);
- return rc;
-}
-
-int cras_alsa_fill_properties(snd_pcm_t *handle, size_t **rates,
- size_t **channel_counts,
- snd_pcm_format_t **formats)
-{
- int rc;
- size_t i, num_found;
- snd_pcm_hw_params_t *params;
-
- snd_pcm_hw_params_alloca(&params);
-
- rc = snd_pcm_hw_params_any(handle, params);
- if (rc < 0) {
- syslog(LOG_ERR, "snd_pcm_hw_params_any: %s", snd_strerror(rc));
- return rc;
- }
-
- *rates = (size_t *)malloc(sizeof(test_sample_rates));
- if (*rates == NULL)
- return -ENOMEM;
- *channel_counts = (size_t *)malloc(sizeof(test_channel_counts));
- if (*channel_counts == NULL) {
- free(*rates);
- return -ENOMEM;
- }
- *formats = (snd_pcm_format_t *)malloc(sizeof(test_formats));
- if (*formats == NULL) {
- free(*channel_counts);
- free(*rates);
- return -ENOMEM;
- }
-
- num_found = 0;
- for (i = 0; test_sample_rates[i] != 0; i++) {
- rc = snd_pcm_hw_params_test_rate(handle, params,
- test_sample_rates[i], 0);
- if (rc == 0)
- (*rates)[num_found++] = test_sample_rates[i];
- }
- (*rates)[num_found] = 0;
- if (num_found == 0) {
- syslog(LOG_WARNING, "No valid sample rates.");
- return -EINVAL;
- }
-
- num_found = 0;
- for (i = 0; test_channel_counts[i] != 0; i++) {
- rc = snd_pcm_hw_params_test_channels(handle, params,
- test_channel_counts[i]);
- if (rc == 0)
- (*channel_counts)[num_found++] = test_channel_counts[i];
- }
- (*channel_counts)[num_found] = 0;
- if (num_found == 0) {
- // Pull the max channel count and use that.
- unsigned int max_channels = 0;
- rc = snd_pcm_hw_params_get_channels_max(params, &max_channels);
- if (rc < 0) {
- syslog(LOG_WARNING, "No valid channel counts found.");
- return -EINVAL;
- } else if (max_channels > TEMP_CHANNEL_LIMIT) {
- syslog(LOG_WARNING, "Can't support so many channels.");
- return -EINVAL;
- } else {
- (*channel_counts)[0] = (size_t)max_channels;
- (*channel_counts)[1] = 0;
- }
- }
-
- num_found = 0;
- for (i = 0; test_formats[i] != 0; i++) {
- rc = snd_pcm_hw_params_test_format(handle, params,
- test_formats[i]);
- if (rc == 0)
- (*formats)[num_found++] = test_formats[i];
- }
- (*formats)[num_found] = (snd_pcm_format_t)0;
- if (num_found == 0) {
- syslog(LOG_WARNING, "No valid sample formats.");
- return -EINVAL;
- }
-
- return 0;
-}
-
-int 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)
-{
- unsigned int rate, ret_rate;
- int err;
- snd_pcm_hw_params_t *hwparams;
-
- rate = format->frame_rate;
- snd_pcm_hw_params_alloca(&hwparams);
-
- err = snd_pcm_hw_params_any(handle, hwparams);
- if (err < 0) {
- syslog(LOG_ERR, "hw_params_any failed %s\n", snd_strerror(err));
- return err;
- }
- /* Disable hardware resampling. */
- err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, 0);
- if (err < 0) {
- syslog(LOG_ERR, "Disabling resampling %s\n", snd_strerror(err));
- return err;
- }
- /* Always interleaved. */
- err = snd_pcm_hw_params_set_access(handle, hwparams,
- SND_PCM_ACCESS_MMAP_INTERLEAVED);
- if (err < 0) {
- syslog(LOG_ERR, "Setting interleaved %s\n", snd_strerror(err));
- return err;
- }
- /* If period_wakeup flag is not set, try to disable ALSA wakeups,
- * we'll keep a timer. */
- if (!period_wakeup &&
- snd_pcm_hw_params_can_disable_period_wakeup(hwparams)) {
- err = snd_pcm_hw_params_set_period_wakeup(handle, hwparams, 0);
- if (err < 0)
- syslog(LOG_WARNING, "disabling wakeups %s\n",
- snd_strerror(err));
- }
- /* Setup the period time so that the hardware pulls the right amount
- * of data at the right time. */
- if (dma_period_time) {
- int dir = 0;
- unsigned int original = dma_period_time;
-
- err = snd_pcm_hw_params_set_period_time_near(
- handle, hwparams, &dma_period_time, &dir);
- if (err < 0) {
- syslog(LOG_ERR, "could not set period time: %s",
- snd_strerror(err));
- return err;
- } else if (original != dma_period_time) {
- syslog(LOG_DEBUG, "period time set to: %u",
- dma_period_time);
- }
- }
- /* Set the sample format. */
- err = snd_pcm_hw_params_set_format(handle, hwparams, format->format);
- if (err < 0) {
- syslog(LOG_ERR, "set format %s\n", snd_strerror(err));
- return err;
- }
- /* Set the stream rate. */
- ret_rate = rate;
- err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &ret_rate, 0);
- if (err < 0) {
- syslog(LOG_ERR, "set_rate_near %iHz %s\n", rate,
- snd_strerror(err));
- return err;
- }
- if (ret_rate != rate) {
- syslog(LOG_ERR, "tried for %iHz, settled for %iHz)\n", rate,
- ret_rate);
- return -EINVAL;
- }
- /* Set the count of channels. */
- err = snd_pcm_hw_params_set_channels(handle, hwparams,
- format->num_channels);
- if (err < 0) {
- syslog(LOG_ERR, "set_channels %s\n", snd_strerror(err));
- return err;
- }
-
- /* Make sure buffer frames is even, or snd_pcm_hw_params will
- * return invalid argument error. */
- err = snd_pcm_hw_params_get_buffer_size_max(hwparams, buffer_frames);
- if (err < 0)
- syslog(LOG_WARNING, "get buffer max %s\n", snd_strerror(err));
-
- *buffer_frames &= ~0x01;
- err = snd_pcm_hw_params_set_buffer_size_max(handle, hwparams,
- buffer_frames);
- if (err < 0) {
- syslog(LOG_ERR, "set_buffer_size_max %s", snd_strerror(err));
- return err;
- }
-
- syslog(LOG_DEBUG, "buffer size set to %u\n",
- (unsigned int)*buffer_frames);
-
- /* Finally, write the parameters to the device. */
- err = snd_pcm_hw_params(handle, hwparams);
- if (err < 0) {
- syslog(LOG_ERR,
- "hw_params: %s: rate: %u, ret_rate: %u, "
- "channel: %zu, format: %u\n",
- snd_strerror(err), rate, ret_rate, format->num_channels,
- format->format);
- return err;
- }
- return 0;
-}
-
-int cras_alsa_set_swparams(snd_pcm_t *handle)
-{
- int err;
- snd_pcm_sw_params_t *swparams;
- snd_pcm_uframes_t boundary;
-
- snd_pcm_sw_params_alloca(&swparams);
-
- err = snd_pcm_sw_params_current(handle, swparams);
- if (err < 0) {
- syslog(LOG_ERR, "sw_params_current: %s\n", snd_strerror(err));
- return err;
- }
- err = snd_pcm_sw_params_get_boundary(swparams, &boundary);
- if (err < 0) {
- syslog(LOG_ERR, "get_boundary: %s\n", snd_strerror(err));
- return err;
- }
- err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, boundary);
- if (err < 0) {
- syslog(LOG_ERR, "set_stop_threshold: %s\n", snd_strerror(err));
- return err;
- }
- /* Don't auto start. */
- err = snd_pcm_sw_params_set_start_threshold(handle, swparams, LONG_MAX);
- if (err < 0) {
- syslog(LOG_ERR, "set_stop_threshold: %s\n", snd_strerror(err));
- return err;
- }
-
- /* Disable period events. */
- err = snd_pcm_sw_params_set_period_event(handle, swparams, 0);
- if (err < 0) {
- syslog(LOG_ERR, "set_period_event: %s\n", snd_strerror(err));
- return err;
- }
-
- err = snd_pcm_sw_params(handle, swparams);
-
- if (err < 0) {
- syslog(LOG_ERR, "sw_params: %s\n", snd_strerror(err));
- return err;
- }
- return 0;
-}
-
-int 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)
-{
- snd_pcm_sframes_t frames;
- int rc = 0;
- static struct timespec tstamp_last_underrun_log = { .tv_sec = 0,
- .tv_nsec = 0 };
-
- /* Use snd_pcm_avail still to ensure that the hardware pointer is
- * up to date. Otherwise, we could use the deprecated snd_pcm_hwsync().
- * IMO this is a deficiency in the ALSA API.
- */
- frames = snd_pcm_avail(handle);
- if (frames >= 0)
- rc = snd_pcm_htimestamp(handle, avail, tstamp);
- else
- rc = frames;
- if (rc == -EPIPE || rc == -ESTRPIPE) {
- cras_alsa_attempt_resume(handle);
- rc = 0;
- goto error;
- } else if (rc < 0) {
- syslog(LOG_ERR, "pcm_avail error %s, %s\n", dev_name,
- snd_strerror(rc));
- goto error;
- } else if (frames > (snd_pcm_sframes_t)buf_size) {
- struct timespec tstamp_now;
- clock_gettime(CLOCK_MONOTONIC_RAW, &tstamp_now);
- /* Limit the log rate. */
- if ((tstamp_now.tv_sec - tstamp_last_underrun_log.tv_sec) >
- UNDERRUN_LOG_TIME_SECS) {
- syslog(LOG_ERR,
- "pcm_avail returned frames larger than buf_size: "
- "%s: %ld > %lu\n",
- dev_name, frames, buf_size);
- tstamp_last_underrun_log.tv_sec = tstamp_now.tv_sec;
- tstamp_last_underrun_log.tv_nsec = tstamp_now.tv_nsec;
- }
- if ((frames - (snd_pcm_sframes_t)buf_size) >
- (snd_pcm_sframes_t)severe_underrun_frames) {
- rc = -EPIPE;
- goto error;
- } else {
- frames = buf_size;
- }
- }
- *avail = frames;
- return 0;
-
-error:
- *avail = 0;
- tstamp->tv_sec = 0;
- tstamp->tv_nsec = 0;
- return rc;
-}
-
-int cras_alsa_get_delay_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
- snd_pcm_sframes_t *delay)
-{
- int rc;
-
- rc = snd_pcm_delay(handle, delay);
- if (rc < 0)
- return rc;
- if (*delay > (snd_pcm_sframes_t)buf_size)
- *delay = buf_size;
- if (*delay < 0)
- *delay = 0;
- return 0;
-}
-
-/*
- * Attempts to resume a PCM.
- * Note that this path does not get executed for default playback/capture
- * stream. Default playback/capture stream are removed from the device
- * upon suspend, and re-attached to the device after resume.
- * The only stream that lives across suspend resume is hotword stream.
- */
-int cras_alsa_attempt_resume(snd_pcm_t *handle)
-{
- int rc;
-
- syslog(LOG_INFO, "System suspended.");
- while ((rc = snd_pcm_resume(handle)) == -EAGAIN)
- usleep(ALSA_SUSPENDED_SLEEP_TIME_US);
- if (rc < 0) {
- /*
- * Some devices do not support snd_pcm_resume, that is
- * acceptable.
- */
- syslog(LOG_INFO, "System suspended, failed to resume %s.",
- snd_strerror(rc));
- rc = snd_pcm_prepare(handle);
- if (rc < 0) {
- syslog(LOG_ERR, "Suspended, failed to prepare: %s.",
- snd_strerror(rc));
- }
- /*
- * CRAS does not use auto-start (start_threshold = 0), so start
- * PCM after it is prepared. This is only for hotword stream.
- */
- rc = snd_pcm_start(handle);
- if (rc < 0) {
- syslog(LOG_ERR, "Suspended, failed to start: %s.",
- snd_strerror(rc));
- }
- }
- return rc;
-}
-
-int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst)
-{
- snd_pcm_uframes_t offset;
- /* The purpose of calling cras_alsa_mmap_begin is to get the base
- * address of the buffer. The requested and retrieved frames are not
- * meaningful here.
- * However, we need to set a non-zero requested frames to get a
- * non-zero retrieved frames. This is to avoid the error checking in
- * snd_pcm_mmap_begin, where it judges retrieved frames being 0 as a
- * failure.
- */
- snd_pcm_uframes_t frames = 1;
-
- return cras_alsa_mmap_begin(handle, 0, dst, &offset, &frames);
-}
-
-int 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)
-{
- int rc;
- unsigned int attempts = 0;
- const snd_pcm_channel_area_t *my_areas;
-
- while (attempts++ < MAX_MMAP_BEGIN_ATTEMPTS) {
- rc = snd_pcm_mmap_begin(handle, &my_areas, offset, frames);
- if (rc == -ESTRPIPE) {
- /* First handle suspend/resume. */
- rc = cras_alsa_attempt_resume(handle);
- if (rc < 0)
- return rc;
- continue; /* Recovered from suspend, try again. */
- } else if (rc < 0) {
- /* If we can recover, continue and try again. */
- if (snd_pcm_recover(handle, rc, 0) == 0)
- continue;
- syslog(LOG_INFO, "recover failed begin: %s\n",
- snd_strerror(rc));
- return rc;
- }
- /* Available frames could be zero right after input pcm handle
- * resumed. As for output pcm handle, some error has occurred
- * when mmap_begin return zero frames, return -EIO for that
- * case.
- */
- if (snd_pcm_stream(handle) == SND_PCM_STREAM_PLAYBACK &&
- *frames == 0) {
- syslog(LOG_INFO, "mmap_begin set frames to 0.");
- return -EIO;
- }
- *dst = (uint8_t *)my_areas[0].addr + (*offset) * format_bytes;
- return 0;
- }
- return -EIO;
-}
-
-int cras_alsa_mmap_commit(snd_pcm_t *handle, snd_pcm_uframes_t offset,
- snd_pcm_uframes_t frames)
-{
- int rc;
- snd_pcm_sframes_t res;
-
- res = snd_pcm_mmap_commit(handle, offset, frames);
- if (res != (snd_pcm_sframes_t)frames) {
- res = res >= 0 ? (int)-EPIPE : res;
- if (res == -ESTRPIPE) {
- /* First handle suspend/resume. */
- rc = cras_alsa_attempt_resume(handle);
- if (rc < 0)
- return rc;
- } else {
- /* If we can recover, continue and try again. */
- rc = snd_pcm_recover(handle, res, 0);
- if (rc < 0) {
- syslog(LOG_ERR,
- "mmap_commit: pcm_recover failed: %s\n",
- snd_strerror(rc));
- return rc;
- }
- }
- }
- return 0;
-}