summaryrefslogtreecommitdiff
path: root/cras/src/server/cras_alsa_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'cras/src/server/cras_alsa_io.c')
-rw-r--r--cras/src/server/cras_alsa_io.c2473
1 files changed, 0 insertions, 2473 deletions
diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c
deleted file mode 100644
index 275a6810..00000000
--- a/cras/src/server/cras_alsa_io.c
+++ /dev/null
@@ -1,2473 +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 <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <sys/param.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <syslog.h>
-#include <time.h>
-
-#include "audio_thread.h"
-#include "cras_alsa_helpers.h"
-#include "cras_alsa_io.h"
-#include "cras_alsa_jack.h"
-#include "cras_alsa_mixer.h"
-#include "cras_alsa_ucm.h"
-#include "cras_audio_area.h"
-#include "cras_config.h"
-#include "cras_utf8.h"
-#include "cras_hotword_handler.h"
-#include "cras_iodev.h"
-#include "cras_iodev_list.h"
-#include "cras_messages.h"
-#include "cras_ramp.h"
-#include "cras_rclient.h"
-#include "cras_shm.h"
-#include "cras_system_state.h"
-#include "cras_types.h"
-#include "cras_util.h"
-#include "cras_volume_curve.h"
-#include "sfh.h"
-#include "softvol_curve.h"
-#include "utlist.h"
-
-#define HOTWORD_DEV "Wake on Voice"
-#define DEFAULT "(default)"
-#define HDMI "HDMI"
-#define INTERNAL_MICROPHONE "Internal Mic"
-#define INTERNAL_SPEAKER "Speaker"
-#define KEYBOARD_MIC "Keyboard Mic"
-#define HEADPHONE "Headphone"
-#define MIC "Mic"
-#define USB "USB"
-#define LOOPBACK_CAPTURE "Loopback Capture"
-#define LOOPBACK_PLAYBACK "Loopback Playback"
-
-/*
- * For USB, pad the output buffer. This avoids a situation where there isn't a
- * complete URB's worth of audio ready to be transmitted when it is requested.
- * The URB interval does track directly to the audio clock, making it hard to
- * predict the exact interval.
- */
-#define USB_EXTRA_BUFFER_FRAMES 768
-
-/*
- * When snd_pcm_avail returns a value that is greater than buffer size,
- * we know there is an underrun. If the number of underrun samples
- * (avail - buffer_size) is greater than SEVERE_UNDERRUN_MS * rate,
- * it is a severe underrun. Main thread should disable and then enable
- * device to recover it from underrun.
- */
-#define SEVERE_UNDERRUN_MS 5000
-
-/*
- * When entering no stream state, audio thread needs to fill extra zeros in
- * order to play remaining valid frames. The value indicates how many
- * time will be filled.
- */
-static const struct timespec no_stream_fill_zeros_duration = {
- 0, 50 * 1000 * 1000 /* 50 msec. */
-};
-
-/*
- * This extends cras_ionode to include alsa-specific information.
- * Members:
- * mixer_output - From cras_alsa_mixer.
- * pcm_name - PCM name for snd_pcm_open.
- * volume_curve - Volume curve for this node.
- * jack - The jack associated with the node.
- */
-struct alsa_output_node {
- struct cras_ionode base;
- struct mixer_control *mixer_output;
- const char *pcm_name;
- struct cras_volume_curve *volume_curve;
- const struct cras_alsa_jack *jack;
-};
-
-struct alsa_input_node {
- struct cras_ionode base;
- struct mixer_control *mixer_input;
- const char *pcm_name;
- const struct cras_alsa_jack *jack;
- int8_t *channel_layout;
-};
-
-/*
- * Child of cras_iodev, alsa_io handles ALSA interaction for sound devices.
- * base - The cras_iodev structure "base class".
- * pcm_name - The PCM name passed to snd_pcm_open() (e.g. "hw:0,0").
- * dev_name - value from snd_pcm_info_get_name
- * dev_id - value from snd_pcm_info_get_id
- * device_index - ALSA index of device, Y in "hw:X:Y".
- * next_ionode_index - The index we will give to the next ionode. Each ionode
- * have a unique index within the iodev.
- * card_type - the type of the card this iodev belongs.
- * is_first - true if this is the first iodev on the card.
- * fully_specified - true if this device and it's nodes were fully specified.
- * That is, don't automatically create nodes for it.
- * handle - Handle to the opened ALSA device.
- * num_severe_underruns - Number of times we have run out of data badly.
- Unlike num_underruns which records for the duration
- where device is opened, num_severe_underruns records
- since device is created. When severe underrun occurs
- a possible action is to close/open device.
- * alsa_stream - Playback or capture type.
- * mixer - Alsa mixer used to control volume and mute of the device.
- * config - Card config for this alsa device.
- * jack_list - List of alsa jack controls for this device.
- * ucm - CRAS use case manager, if configuration is found.
- * mmap_offset - offset returned from mmap_begin.
- * poll_fd - Descriptor used to block until data is ready.
- * dma_period_set_microsecs - If non-zero, the value to apply to the dma_period.
- * free_running - true if device is playing zeros in the buffer without
- * user filling meaningful data. The device buffer is filled
- * with zeros. In this state, appl_ptr remains the same
- * while hw_ptr keeps running ahead.
- * filled_zeros_for_draining - The number of zeros filled for draining.
- * severe_underrun_frames - The threshold for severe underrun.
- * default_volume_curve - Default volume curve that converts from an index
- * to dBFS.
- * has_dependent_dev - true if this iodev has dependent device.
- */
-struct alsa_io {
- struct cras_iodev base;
- char *pcm_name;
- char *dev_name;
- char *dev_id;
- uint32_t device_index;
- uint32_t next_ionode_index;
- enum CRAS_ALSA_CARD_TYPE card_type;
- int is_first;
- int fully_specified;
- snd_pcm_t *handle;
- unsigned int num_severe_underruns;
- snd_pcm_stream_t alsa_stream;
- struct cras_alsa_mixer *mixer;
- const struct cras_card_config *config;
- struct cras_alsa_jack_list *jack_list;
- struct cras_use_case_mgr *ucm;
- snd_pcm_uframes_t mmap_offset;
- int poll_fd;
- unsigned int dma_period_set_microsecs;
- int free_running;
- unsigned int filled_zeros_for_draining;
- snd_pcm_uframes_t severe_underrun_frames;
- struct cras_volume_curve *default_volume_curve;
- int hwparams_set;
- int has_dependent_dev;
-};
-
-static void init_device_settings(struct alsa_io *aio);
-
-static int alsa_iodev_set_active_node(struct cras_iodev *iodev,
- struct cras_ionode *ionode,
- unsigned dev_enabled);
-
-static int get_fixed_rate(struct alsa_io *aio);
-
-static int update_supported_formats(struct cras_iodev *iodev);
-
-/*
- * Defines the default values of nodes.
- */
-static const struct {
- const char *name;
- enum CRAS_NODE_TYPE type;
- enum CRAS_NODE_POSITION position;
-} node_defaults[] = {
- {
- .name = DEFAULT,
- .type = CRAS_NODE_TYPE_UNKNOWN,
- .position = NODE_POSITION_INTERNAL,
- },
- {
- .name = INTERNAL_SPEAKER,
- .type = CRAS_NODE_TYPE_INTERNAL_SPEAKER,
- .position = NODE_POSITION_INTERNAL,
- },
- {
- .name = INTERNAL_MICROPHONE,
- .type = CRAS_NODE_TYPE_MIC,
- .position = NODE_POSITION_INTERNAL,
- },
- {
- .name = KEYBOARD_MIC,
- .type = CRAS_NODE_TYPE_MIC,
- .position = NODE_POSITION_KEYBOARD,
- },
- {
- .name = HDMI,
- .type = CRAS_NODE_TYPE_HDMI,
- .position = NODE_POSITION_EXTERNAL,
- },
- {
- .name = "IEC958",
- .type = CRAS_NODE_TYPE_HDMI,
- .position = NODE_POSITION_EXTERNAL,
- },
- {
- .name = "Headphone",
- .type = CRAS_NODE_TYPE_HEADPHONE,
- .position = NODE_POSITION_EXTERNAL,
- },
- {
- .name = "Front Headphone",
- .type = CRAS_NODE_TYPE_HEADPHONE,
- .position = NODE_POSITION_EXTERNAL,
- },
- {
- .name = "Front Mic",
- .type = CRAS_NODE_TYPE_MIC,
- .position = NODE_POSITION_FRONT,
- },
- {
- .name = "Rear Mic",
- .type = CRAS_NODE_TYPE_MIC,
- .position = NODE_POSITION_REAR,
- },
- {
- .name = "Mic",
- .type = CRAS_NODE_TYPE_MIC,
- .position = NODE_POSITION_EXTERNAL,
- },
- {
- .name = HOTWORD_DEV,
- .type = CRAS_NODE_TYPE_HOTWORD,
- .position = NODE_POSITION_INTERNAL,
- },
- {
- .name = "Haptic",
- .type = CRAS_NODE_TYPE_HAPTIC,
- .position = NODE_POSITION_INTERNAL,
- },
- {
- .name = "Rumbler",
- .type = CRAS_NODE_TYPE_HAPTIC,
- .position = NODE_POSITION_INTERNAL,
- },
- {
- .name = "Line Out",
- .type = CRAS_NODE_TYPE_LINEOUT,
- .position = NODE_POSITION_EXTERNAL,
- },
- {
- .name = "SCO Line In",
- .type = CRAS_NODE_TYPE_BLUETOOTH,
- .position = NODE_POSITION_EXTERNAL,
- },
- {
- .name = "SCO Line Out",
- .type = CRAS_NODE_TYPE_BLUETOOTH,
- .position = NODE_POSITION_EXTERNAL,
- },
- {
- .name = "Echo Reference",
- .type = CRAS_NODE_TYPE_ECHO_REFERENCE,
- .position = NODE_POSITION_INTERNAL,
- },
- {
- .name = LOOPBACK_CAPTURE,
- .type = CRAS_NODE_TYPE_ALSA_LOOPBACK,
- .position = NODE_POSITION_INTERNAL,
- },
- {
- .name = LOOPBACK_PLAYBACK,
- .type = CRAS_NODE_TYPE_ALSA_LOOPBACK,
- .position = NODE_POSITION_INTERNAL,
- },
-};
-
-static int set_hwparams(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- int period_wakeup;
- int rc;
-
- /* Only need to set hardware params once. */
- if (aio->hwparams_set)
- return 0;
-
- /* If it's a wake on voice device, period_wakeups are required. */
- period_wakeup = (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD);
-
- /* Sets frame rate and channel count to alsa device before
- * we test channel mapping. */
- rc = cras_alsa_set_hwparams(aio->handle, iodev->format,
- &iodev->buffer_size, period_wakeup,
- aio->dma_period_set_microsecs);
- if (rc < 0)
- return rc;
-
- aio->hwparams_set = 1;
- return 0;
-}
-
-/*
- * iodev callbacks.
- */
-
-static int frames_queued(const struct cras_iodev *iodev,
- struct timespec *tstamp)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- int rc;
- snd_pcm_uframes_t frames;
-
- rc = cras_alsa_get_avail_frames(aio->handle, aio->base.buffer_size,
- aio->severe_underrun_frames,
- iodev->info.name, &frames, tstamp);
- if (rc < 0) {
- if (rc == -EPIPE)
- aio->num_severe_underruns++;
- return rc;
- }
- clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
- if (iodev->direction == CRAS_STREAM_INPUT)
- return (int)frames;
-
- /* For output, return number of frames that are used. */
- return iodev->buffer_size - frames;
-}
-
-static int delay_frames(const struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- snd_pcm_sframes_t delay;
- int rc;
-
- rc = cras_alsa_get_delay_frames(aio->handle, iodev->buffer_size,
- &delay);
- if (rc < 0)
- return rc;
-
- return (int)delay;
-}
-
-static int close_dev(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
-
- /* Removes audio thread callback from main thread. */
- if (aio->poll_fd >= 0)
- audio_thread_rm_callback_sync(
- cras_iodev_list_get_audio_thread(), aio->poll_fd);
- if (!aio->handle)
- return 0;
- cras_alsa_pcm_close(aio->handle);
- aio->handle = NULL;
- aio->free_running = 0;
- aio->filled_zeros_for_draining = 0;
- aio->hwparams_set = 0;
- cras_iodev_free_format(&aio->base);
- cras_iodev_free_audio_area(&aio->base);
- return 0;
-}
-
-static int empty_hotword_cb(void *arg, int revents)
-{
- /* Only need this once. */
- struct alsa_io *aio = (struct alsa_io *)arg;
- audio_thread_rm_callback(aio->poll_fd);
- aio->poll_fd = -1;
- aio->base.input_streaming = 1;
-
- /* Send hotword triggered signal. */
- cras_hotword_send_triggered_msg();
-
- return 0;
-}
-
-static int open_dev(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- snd_pcm_t *handle;
- int rc;
- const char *pcm_name = NULL;
- int enable_noise_cancellation;
-
- if (aio->base.direction == CRAS_STREAM_OUTPUT) {
- struct alsa_output_node *aout =
- (struct alsa_output_node *)aio->base.active_node;
- pcm_name = aout->pcm_name;
- } else {
- struct alsa_input_node *ain =
- (struct alsa_input_node *)aio->base.active_node;
- pcm_name = ain->pcm_name;
- }
-
- /* For legacy UCM path which doesn't have PlaybackPCM or CapturePCM. */
- if (pcm_name == NULL)
- pcm_name = aio->pcm_name;
-
- rc = cras_alsa_pcm_open(&handle, pcm_name, aio->alsa_stream);
- if (rc < 0)
- return rc;
-
- aio->handle = handle;
-
- /* Enable or disable noise cancellation if it supports. */
- if (aio->ucm && iodev->direction == CRAS_STREAM_INPUT &&
- ucm_node_noise_cancellation_exists(aio->ucm,
- iodev->active_node->name)) {
- enable_noise_cancellation =
- cras_system_get_noise_cancellation_enabled();
- rc = ucm_enable_node_noise_cancellation(
- aio->ucm, iodev->active_node->name,
- enable_noise_cancellation);
- if (rc < 0)
- return rc;
- }
-
- return 0;
-}
-
-static int configure_dev(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- int rc;
-
- /* This is called after the first stream added so configure for it.
- * format must be set before opening the device.
- */
- if (iodev->format == NULL)
- return -EINVAL;
- aio->free_running = 0;
- aio->filled_zeros_for_draining = 0;
- aio->severe_underrun_frames =
- SEVERE_UNDERRUN_MS * iodev->format->frame_rate / 1000;
-
- cras_iodev_init_audio_area(iodev, iodev->format->num_channels);
-
- syslog(LOG_DEBUG, "Configure alsa device %s rate %zuHz, %zu channels",
- aio->pcm_name, iodev->format->frame_rate,
- iodev->format->num_channels);
-
- rc = set_hwparams(iodev);
- if (rc < 0)
- return rc;
-
- /* Set channel map to device */
- rc = cras_alsa_set_channel_map(aio->handle, iodev->format);
- if (rc < 0)
- return rc;
-
- /* Configure software params. */
- rc = cras_alsa_set_swparams(aio->handle);
- if (rc < 0)
- return rc;
-
- /* Initialize device settings. */
- init_device_settings(aio);
-
- aio->poll_fd = -1;
- if (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD) {
- struct pollfd *ufds;
- int count, i;
-
- count = snd_pcm_poll_descriptors_count(aio->handle);
- if (count <= 0) {
- syslog(LOG_ERR, "Invalid poll descriptors count\n");
- return count;
- }
-
- ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * count);
- if (ufds == NULL)
- return -ENOMEM;
-
- rc = snd_pcm_poll_descriptors(aio->handle, ufds, count);
- if (rc < 0) {
- syslog(LOG_ERR,
- "Getting hotword poll descriptors: %s\n",
- snd_strerror(rc));
- free(ufds);
- return rc;
- }
-
- for (i = 0; i < count; i++) {
- if (ufds[i].events & POLLIN) {
- aio->poll_fd = ufds[i].fd;
- break;
- }
- }
- free(ufds);
-
- if (aio->poll_fd >= 0)
- audio_thread_add_events_callback(
- aio->poll_fd, empty_hotword_cb, aio, POLLIN);
- }
-
- /* Capture starts right away, playback will wait for samples. */
- if (aio->alsa_stream == SND_PCM_STREAM_CAPTURE)
- cras_alsa_pcm_start(aio->handle);
-
- return 0;
-}
-
-/*
- * Check if ALSA device is opened by checking if handle is valid.
- * Note that to fully open a cras_iodev, ALSA device is opened first, then there
- * are some device init settings to be done in init_device_settings.
- * Therefore, when setting volume/mute/gain in init_device_settings,
- * cras_iodev is not in CRAS_IODEV_STATE_OPEN yet. We need to check if handle
- * is valid when setting those properties, instead of checking
- * cras_iodev_is_open.
- */
-static int has_handle(const struct alsa_io *aio)
-{
- return !!aio->handle;
-}
-
-static int start(const struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- snd_pcm_t *handle = aio->handle;
- int rc;
-
- if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING)
- return 0;
-
- if (snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
- rc = cras_alsa_attempt_resume(handle);
- if (rc < 0) {
- syslog(LOG_ERR, "Resume error: %s", snd_strerror(rc));
- return rc;
- }
- cras_iodev_reset_rate_estimator(iodev);
- } else {
- rc = cras_alsa_pcm_start(handle);
- if (rc < 0) {
- syslog(LOG_ERR, "Start error: %s", snd_strerror(rc));
- return rc;
- }
- }
-
- return 0;
-}
-
-static int get_buffer(struct cras_iodev *iodev, struct cras_audio_area **area,
- unsigned *frames)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- snd_pcm_uframes_t nframes = *frames;
- uint8_t *dst = NULL;
- size_t format_bytes;
- int rc;
-
- aio->mmap_offset = 0;
- format_bytes = cras_get_format_bytes(iodev->format);
-
- rc = cras_alsa_mmap_begin(aio->handle, format_bytes, &dst,
- &aio->mmap_offset, &nframes);
-
- iodev->area->frames = nframes;
- cras_audio_area_config_buf_pointers(iodev->area, iodev->format, dst);
-
- *area = iodev->area;
- *frames = nframes;
-
- return rc;
-}
-
-static int put_buffer(struct cras_iodev *iodev, unsigned nwritten)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
-
- return cras_alsa_mmap_commit(aio->handle, aio->mmap_offset, nwritten);
-}
-
-static int flush_buffer(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- snd_pcm_uframes_t nframes;
-
- if (iodev->direction == CRAS_STREAM_INPUT) {
- nframes = snd_pcm_avail(aio->handle);
- nframes = snd_pcm_forwardable(aio->handle);
- return snd_pcm_forward(aio->handle, nframes);
- }
- return 0;
-}
-
-/*
- * Gets the first plugged node in list. This is used as the
- * default node to set as active.
- */
-static struct cras_ionode *first_plugged_node(struct cras_iodev *iodev)
-{
- struct cras_ionode *n;
-
- /* When this is called at iodev creation, none of the nodes
- * are selected. Just pick the first plugged one and let Chrome
- * choose it later. */
- DL_FOREACH (iodev->nodes, n) {
- if (n->plugged)
- return n;
- }
- return iodev->nodes;
-}
-
-static void update_active_node(struct cras_iodev *iodev, unsigned node_idx,
- unsigned dev_enabled)
-{
- struct cras_ionode *n;
-
- /* If a node exists for node_idx, set it as active. */
- DL_FOREACH (iodev->nodes, n) {
- if (n->idx == node_idx) {
- alsa_iodev_set_active_node(iodev, n, dev_enabled);
- return;
- }
- }
-
- alsa_iodev_set_active_node(iodev, first_plugged_node(iodev),
- dev_enabled);
-}
-
-static int update_channel_layout(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- int err = 0;
-
- /* If the capture channel map is specified in UCM, prefer it over
- * what ALSA provides. */
- if (aio->ucm && (iodev->direction == CRAS_STREAM_INPUT)) {
- struct alsa_input_node *input =
- (struct alsa_input_node *)iodev->active_node;
-
- if (input->channel_layout) {
- memcpy(iodev->format->channel_layout,
- input->channel_layout,
- CRAS_CH_MAX * sizeof(*input->channel_layout));
- return 0;
- }
- }
-
- err = set_hwparams(iodev);
- if (err < 0)
- return err;
-
- return cras_alsa_get_channel_map(aio->handle, iodev->format);
-}
-
-static int set_hotword_model(struct cras_iodev *iodev, const char *model_name)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- if (!aio->ucm)
- return -EINVAL;
-
- return ucm_set_hotword_model(aio->ucm, model_name);
-}
-
-static char *get_hotword_models(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- if (!aio->ucm)
- return NULL;
-
- return ucm_get_hotword_models(aio->ucm);
-}
-
-/*
- * Alsa helper functions.
- */
-
-static struct alsa_output_node *get_active_output(const struct alsa_io *aio)
-{
- return (struct alsa_output_node *)aio->base.active_node;
-}
-
-static struct alsa_input_node *get_active_input(const struct alsa_io *aio)
-{
- return (struct alsa_input_node *)aio->base.active_node;
-}
-
-/*
- * Gets the curve for the active output node. If the node doesn't have volume
- * curve specified, return the default volume curve of the parent iodev.
- */
-static const struct cras_volume_curve *
-get_curve_for_output_node(const struct alsa_io *aio,
- const struct alsa_output_node *node)
-{
- if (node && node->volume_curve)
- return node->volume_curve;
- return aio->default_volume_curve;
-}
-
-/*
- * Gets the curve for the active output.
- */
-static const struct cras_volume_curve *
-get_curve_for_active_output(const struct alsa_io *aio)
-{
- struct alsa_output_node *node = get_active_output(aio);
- return get_curve_for_output_node(aio, node);
-}
-
-/*
- * Informs the system of the volume limits for this device.
- */
-static void set_alsa_volume_limits(struct alsa_io *aio)
-{
- const struct cras_volume_curve *curve;
-
- /* Only set the limits if the dev is active. */
- if (!has_handle(aio))
- return;
-
- curve = get_curve_for_active_output(aio);
- cras_system_set_volume_limits(curve->get_dBFS(curve, 1), /* min */
- curve->get_dBFS(curve,
- CRAS_MAX_SYSTEM_VOLUME));
-}
-
-/*
- * Sets the volume of the playback device to the specified level. Receives a
- * volume index from the system settings, ranging from 0 to 100, converts it to
- * dB using the volume curve, and sends the dB value to alsa.
- */
-static void set_alsa_volume(struct cras_iodev *iodev)
-{
- const struct alsa_io *aio = (const struct alsa_io *)iodev;
- const struct cras_volume_curve *curve;
- size_t volume;
- struct alsa_output_node *aout;
-
- assert(aio);
- if (aio->mixer == NULL)
- return;
-
- /* Only set the volume if the dev is active. */
- if (!has_handle(aio))
- return;
-
- volume = cras_system_get_volume();
- curve = get_curve_for_active_output(aio);
- if (curve == NULL)
- return;
- aout = get_active_output(aio);
- if (aout)
- volume = cras_iodev_adjust_node_volume(&aout->base, volume);
-
- /* Samples get scaled for devices using software volume, set alsa
- * volume to 100. */
- if (cras_iodev_software_volume_needed(iodev))
- volume = 100;
-
- cras_alsa_mixer_set_dBFS(aio->mixer, curve->get_dBFS(curve, volume),
- aout ? aout->mixer_output : NULL);
-}
-
-/*
- * Sets the alsa mute control for this iodev.
- */
-static void set_alsa_mute(struct cras_iodev *iodev)
-{
- const struct alsa_io *aio = (const struct alsa_io *)iodev;
- struct alsa_output_node *aout;
-
- if (!has_handle(aio))
- return;
-
- aout = get_active_output(aio);
- cras_alsa_mixer_set_mute(aio->mixer, cras_system_get_mute(),
- aout ? aout->mixer_output : NULL);
-}
-
-/*
- * Sets the capture gain to the current system input gain level, given in dBFS.
- * Set mute based on the system mute state. This gain can be positive or
- * negative and might be adjusted often if an app is running an AGC.
- */
-static void set_alsa_capture_gain(struct cras_iodev *iodev)
-{
- const struct alsa_io *aio = (const struct alsa_io *)iodev;
- struct alsa_input_node *ain;
- long min_capture_gain, max_capture_gain, gain;
- assert(aio);
- if (aio->mixer == NULL)
- return;
-
- /* Only set the volume if the dev is active. */
- if (!has_handle(aio))
- return;
-
- ain = get_active_input(aio);
-
- cras_alsa_mixer_set_capture_mute(aio->mixer,
- cras_system_get_capture_mute(),
- ain ? ain->mixer_input : NULL);
-
- /* For USB device without UCM config, not change a gain control. */
- if (ain && ain->base.type == CRAS_NODE_TYPE_USB && !aio->ucm)
- return;
-
- /* Set hardware gain to 0dB if software gain is needed. */
- if (cras_iodev_software_volume_needed(iodev))
- gain = 0;
- else {
- min_capture_gain = cras_alsa_mixer_get_minimum_capture_gain(
- aio->mixer, ain ? ain->mixer_input : NULL);
- max_capture_gain = cras_alsa_mixer_get_maximum_capture_gain(
- aio->mixer, ain ? ain->mixer_input : NULL);
- gain = MAX(iodev->active_node->capture_gain, min_capture_gain);
- gain = MIN(gain, max_capture_gain);
- }
-
- cras_alsa_mixer_set_capture_dBFS(aio->mixer, gain,
- ain ? ain->mixer_input : NULL);
-}
-
-/*
- * Swaps the left and right channels of the given node.
- */
-static int set_alsa_node_swapped(struct cras_iodev *iodev,
- struct cras_ionode *node, int enable)
-{
- const struct alsa_io *aio = (const struct alsa_io *)iodev;
- assert(aio);
- return ucm_enable_swap_mode(aio->ucm, node->name, enable);
-}
-
-/*
- * Initializes the device settings according to system volume, mute, gain
- * settings.
- * Updates system capture gain limits based on current active device/node.
- */
-static void init_device_settings(struct alsa_io *aio)
-{
- /* Register for volume/mute callback and set initial volume/mute for
- * the device. */
- if (aio->base.direction == CRAS_STREAM_OUTPUT) {
- set_alsa_volume_limits(aio);
- set_alsa_volume(&aio->base);
- set_alsa_mute(&aio->base);
- } else {
- set_alsa_capture_gain(&aio->base);
- }
-}
-
-/*
- * Functions run in the main server context.
- */
-
-/*
- * Frees resources used by the alsa iodev.
- * Args:
- * iodev - the iodev to free the resources from.
- */
-static void free_alsa_iodev_resources(struct alsa_io *aio)
-{
- struct cras_ionode *node;
- struct alsa_output_node *aout;
- struct alsa_input_node *ain;
-
- free(aio->base.supported_rates);
- free(aio->base.supported_channel_counts);
- free(aio->base.supported_formats);
-
- DL_FOREACH (aio->base.nodes, node) {
- if (aio->base.direction == CRAS_STREAM_OUTPUT) {
- aout = (struct alsa_output_node *)node;
- cras_volume_curve_destroy(aout->volume_curve);
- free((void *)aout->pcm_name);
- } else {
- ain = (struct alsa_input_node *)node;
- free((void *)ain->pcm_name);
- }
- cras_iodev_rm_node(&aio->base, node);
- free(node->softvol_scalers);
- free((void *)node->dsp_name);
- free(node);
- }
-
- cras_iodev_free_resources(&aio->base);
- free(aio->pcm_name);
- if (aio->dev_id)
- free(aio->dev_id);
- if (aio->dev_name)
- free(aio->dev_name);
-}
-
-/*
- * Returns true if this is the first internal device.
- */
-static int first_internal_device(struct alsa_io *aio)
-{
- return aio->is_first && aio->card_type == ALSA_CARD_TYPE_INTERNAL;
-}
-
-/*
- * Returns true if there is already a node created with the given name.
- */
-static int has_node(struct alsa_io *aio, const char *name)
-{
- struct cras_ionode *node;
-
- DL_FOREACH (aio->base.nodes, node)
- if (!strcmp(node->name, name))
- return 1;
-
- return 0;
-}
-
-/*
- * Returns true if string s ends with the given suffix.
- */
-int endswith(const char *s, const char *suffix)
-{
- size_t n = strlen(s);
- size_t m = strlen(suffix);
- return n >= m && !strcmp(s + (n - m), suffix);
-}
-
-#ifdef CRAS_DBUS
-/*
- * Drop the node name and replace it with node type.
- */
-static void drop_node_name(struct cras_ionode *node)
-{
- if (node->type == CRAS_NODE_TYPE_USB)
- strcpy(node->name, USB);
- else if (node->type == CRAS_NODE_TYPE_HDMI)
- strcpy(node->name, HDMI);
- else {
- /* Only HDMI or USB node might have invalid name to drop */
- syslog(LOG_ERR,
- "Unexpectedly drop node name for "
- "node: %s, type: %d",
- node->name, node->type);
- strcpy(node->name, DEFAULT);
- }
-}
-#endif
-
-/*
- * Sets the initial plugged state and type of a node based on its
- * name. Chrome will assign priority to nodes base on node type.
- */
-static void set_node_initial_state(struct cras_ionode *node,
- enum CRAS_ALSA_CARD_TYPE card_type)
-{
- unsigned i;
-
- node->volume = 100;
- node->type = CRAS_NODE_TYPE_UNKNOWN;
- /* Go through the known names */
- for (i = 0; i < ARRAY_SIZE(node_defaults); i++)
- if (!strncmp(node->name, node_defaults[i].name,
- strlen(node_defaults[i].name))) {
- node->position = node_defaults[i].position;
- node->plugged =
- (node->position != NODE_POSITION_EXTERNAL);
- node->type = node_defaults[i].type;
- if (node->plugged)
- gettimeofday(&node->plugged_time, NULL);
- break;
- }
-
- /*
- * If we didn't find a matching name above, but the node is a jack node,
- * and there is no "HDMI" in the node name, then this must be a 3.5mm
- * headphone/mic.
- * Set its type and name to headphone/mic. The name is important because
- * it associates the UCM section to the node so the properties like
- * default node gain can be obtained.
- * This matches node names like "DAISY-I2S Mic Jack".
- * If HDMI is in the node name, set its type to HDMI. This matches node names
- * like "Rockchip HDMI Jack".
- */
- if (i == ARRAY_SIZE(node_defaults)) {
- if (endswith(node->name, "Jack") && !strstr(node->name, HDMI)) {
- if (node->dev->direction == CRAS_STREAM_OUTPUT) {
- node->type = CRAS_NODE_TYPE_HEADPHONE;
- strncpy(node->name, HEADPHONE,
- sizeof(node->name) - 1);
- } else {
- node->type = CRAS_NODE_TYPE_MIC;
- strncpy(node->name, MIC,
- sizeof(node->name) - 1);
- }
- }
- if (strstr(node->name, HDMI) &&
- node->dev->direction == CRAS_STREAM_OUTPUT)
- node->type = CRAS_NODE_TYPE_HDMI;
- }
-
- /* Regardless of the node name of a USB headset (it can be "Speaker"),
- * set it's type to usb.
- */
- if (card_type == ALSA_CARD_TYPE_USB) {
- node->type = CRAS_NODE_TYPE_USB;
- node->position = NODE_POSITION_EXTERNAL;
- }
-
-#ifdef CRAS_DBUS
- if (!is_utf8_string(node->name))
- drop_node_name(node);
-#endif
-}
-
-static int get_ucm_flag_integer(struct alsa_io *aio, const char *flag_name,
- int *result)
-{
- char *value;
- int i;
-
- if (!aio->ucm)
- return -1;
-
- value = ucm_get_flag(aio->ucm, flag_name);
- if (!value)
- return -1;
-
- i = atoi(value);
- free(value);
- *result = i;
- return 0;
-}
-
-static int auto_unplug_input_node(struct alsa_io *aio)
-{
- int result;
- if (get_ucm_flag_integer(aio, "AutoUnplugInputNode", &result))
- return 0;
- return result;
-}
-
-static int auto_unplug_output_node(struct alsa_io *aio)
-{
- int result;
- if (get_ucm_flag_integer(aio, "AutoUnplugOutputNode", &result))
- return 0;
- return result;
-}
-
-static int no_create_default_input_node(struct alsa_io *aio)
-{
- int result;
- if (get_ucm_flag_integer(aio, "NoCreateDefaultInputNode", &result))
- return 0;
- return result;
-}
-
-static int no_create_default_output_node(struct alsa_io *aio)
-{
- int result;
- if (get_ucm_flag_integer(aio, "NoCreateDefaultOutputNode", &result))
- return 0;
- return result;
-}
-
-static void
-set_output_node_software_volume_needed(struct alsa_output_node *output,
- struct alsa_io *aio)
-{
- struct cras_alsa_mixer *mixer = aio->mixer;
- long range = 0;
-
- if (aio->ucm && ucm_get_disable_software_volume(aio->ucm)) {
- output->base.software_volume_needed = 0;
- syslog(LOG_DEBUG, "Disable software volume for %s from ucm.",
- output->base.name);
- return;
- }
-
- /* Use software volume for HDMI output and nodes without volume mixer
- * control. */
- if ((output->base.type == CRAS_NODE_TYPE_HDMI) ||
- (!cras_alsa_mixer_has_main_volume(mixer) &&
- !cras_alsa_mixer_has_volume(output->mixer_output)))
- output->base.software_volume_needed = 1;
-
- /* Use software volume if the usb device's volume range is smaller
- * than 40dB */
- if (output->base.type == CRAS_NODE_TYPE_USB) {
- range += cras_alsa_mixer_get_dB_range(mixer);
- range += cras_alsa_mixer_get_output_dB_range(
- output->mixer_output);
- if (range < 4000)
- output->base.software_volume_needed = 1;
- }
- if (output->base.software_volume_needed)
- syslog(LOG_DEBUG, "Use software volume for node: %s",
- output->base.name);
-}
-
-static void set_input_default_node_gain(struct alsa_input_node *input,
- struct alsa_io *aio)
-{
- long gain;
-
- input->base.capture_gain = DEFAULT_CAPTURE_GAIN;
- input->base.ui_gain_scaler = 1.0f;
-
- if (!aio->ucm)
- return;
-
- if (ucm_get_default_node_gain(aio->ucm, input->base.name, &gain) == 0)
- input->base.capture_gain = gain;
-}
-
-static void set_input_node_intrinsic_sensitivity(struct alsa_input_node *input,
- struct alsa_io *aio)
-{
- long sensitivity;
- int rc;
-
- input->base.intrinsic_sensitivity = 0;
-
- if (aio->ucm) {
- rc = ucm_get_intrinsic_sensitivity(aio->ucm, input->base.name,
- &sensitivity);
- if (rc)
- return;
- } else if (input->base.type == CRAS_NODE_TYPE_USB) {
- /*
- * For USB devices without UCM config, trust the default capture gain.
- * Set sensitivity to the default dbfs so the capture gain is 0.
- */
- sensitivity = DEFAULT_CAPTURE_VOLUME_DBFS;
- } else {
- return;
- }
-
- input->base.intrinsic_sensitivity = sensitivity;
- input->base.capture_gain = DEFAULT_CAPTURE_VOLUME_DBFS - sensitivity;
- syslog(LOG_INFO,
- "Use software gain %ld for %s because IntrinsicSensitivity %ld is"
- " specified in UCM",
- input->base.capture_gain, input->base.name, sensitivity);
-}
-
-static void check_auto_unplug_output_node(struct alsa_io *aio,
- struct cras_ionode *node, int plugged)
-{
- struct cras_ionode *tmp;
-
- if (!auto_unplug_output_node(aio))
- return;
-
- /* Auto unplug internal speaker if any output node has been created */
- if (!strcmp(node->name, INTERNAL_SPEAKER) && plugged) {
- DL_FOREACH (aio->base.nodes, tmp)
- if (tmp->plugged && (tmp != node))
- cras_iodev_set_node_plugged(node, 0);
- } else {
- DL_FOREACH (aio->base.nodes, tmp) {
- if (!strcmp(tmp->name, INTERNAL_SPEAKER))
- cras_iodev_set_node_plugged(tmp, !plugged);
- }
- }
-}
-
-/*
- * Callback for listing mixer outputs. The mixer will call this once for each
- * output associated with this device. Most commonly this is used to tell the
- * device it has Headphones and Speakers.
- */
-static struct alsa_output_node *new_output(struct alsa_io *aio,
- struct mixer_control *cras_output,
- const char *name)
-{
- struct alsa_output_node *output;
- syslog(LOG_DEBUG, "New output node for '%s'", name);
- if (aio == NULL) {
- syslog(LOG_ERR, "Invalid aio when listing outputs.");
- return NULL;
- }
- output = (struct alsa_output_node *)calloc(1, sizeof(*output));
- if (output == NULL) {
- syslog(LOG_ERR, "Out of memory when listing outputs.");
- return NULL;
- }
- output->base.dev = &aio->base;
- output->base.idx = aio->next_ionode_index++;
- output->base.stable_id =
- SuperFastHash(name, strlen(name), aio->base.info.stable_id);
- if (aio->ucm)
- output->base.dsp_name =
- ucm_get_dsp_name_for_dev(aio->ucm, name);
-
- if (strcmp(name, "SCO Line Out") == 0)
- output->base.is_sco_pcm = 1;
- output->mixer_output = cras_output;
-
- /* Volume curve. */
- output->volume_curve = cras_card_config_get_volume_curve_for_control(
- aio->config,
- name ? name : cras_alsa_mixer_get_control_name(cras_output));
-
- strncpy(output->base.name, name, sizeof(output->base.name) - 1);
- set_node_initial_state(&output->base, aio->card_type);
- set_output_node_software_volume_needed(output, aio);
-
- cras_iodev_add_node(&aio->base, &output->base);
-
- check_auto_unplug_output_node(aio, &output->base, output->base.plugged);
- return output;
-}
-
-static void new_output_by_mixer_control(struct mixer_control *cras_output,
- void *callback_arg)
-{
- struct alsa_io *aio = (struct alsa_io *)callback_arg;
- char node_name[CRAS_IODEV_NAME_BUFFER_SIZE];
- const char *ctl_name;
-
- ctl_name = cras_alsa_mixer_get_control_name(cras_output);
- if (!ctl_name)
- return;
-
- if (aio->card_type == ALSA_CARD_TYPE_USB) {
- if (snprintf(node_name, sizeof(node_name), "%s: %s",
- aio->base.info.name, ctl_name) > 0) {
- new_output(aio, cras_output, node_name);
- }
- } else {
- new_output(aio, cras_output, ctl_name);
- }
-}
-
-static void check_auto_unplug_input_node(struct alsa_io *aio,
- struct cras_ionode *node, int plugged)
-{
- struct cras_ionode *tmp;
- if (!auto_unplug_input_node(aio))
- return;
-
- /* Auto unplug internal mic if any input node has already
- * been created */
- if (!strcmp(node->name, INTERNAL_MICROPHONE) && plugged) {
- DL_FOREACH (aio->base.nodes, tmp)
- if (tmp->plugged && (tmp != node))
- cras_iodev_set_node_plugged(node, 0);
- } else {
- DL_FOREACH (aio->base.nodes, tmp)
- if (!strcmp(tmp->name, INTERNAL_MICROPHONE))
- cras_iodev_set_node_plugged(tmp, !plugged);
- }
-}
-
-static struct alsa_input_node *new_input(struct alsa_io *aio,
- struct mixer_control *cras_input,
- const char *name)
-{
- struct cras_iodev *iodev = &aio->base;
- struct alsa_input_node *input;
- int err;
-
- input = (struct alsa_input_node *)calloc(1, sizeof(*input));
- if (input == NULL) {
- syslog(LOG_ERR, "Out of memory when listing inputs.");
- return NULL;
- }
- input->base.dev = &aio->base;
- input->base.idx = aio->next_ionode_index++;
- input->base.stable_id =
- SuperFastHash(name, strlen(name), aio->base.info.stable_id);
- if (strcmp(name, "SCO Line In") == 0)
- input->base.is_sco_pcm = 1;
- input->mixer_input = cras_input;
- strncpy(input->base.name, name, sizeof(input->base.name) - 1);
- set_node_initial_state(&input->base, aio->card_type);
- set_input_default_node_gain(input, aio);
- set_input_node_intrinsic_sensitivity(input, aio);
-
- if (aio->ucm) {
- /* Check if channel map is specified in UCM. */
- input->channel_layout = (int8_t *)malloc(
- CRAS_CH_MAX * sizeof(*input->channel_layout));
- err = ucm_get_capture_chmap_for_dev(aio->ucm, name,
- input->channel_layout);
- if (err) {
- free(input->channel_layout);
- input->channel_layout = 0;
- }
- if (ucm_get_preempt_hotword(aio->ucm, name)) {
- iodev->pre_open_iodev_hook =
- cras_iodev_list_suspend_hotword_streams;
- iodev->post_close_iodev_hook =
- cras_iodev_list_resume_hotword_stream;
- }
-
- input->base.dsp_name = ucm_get_dsp_name_for_dev(aio->ucm, name);
- }
-
- cras_iodev_add_node(&aio->base, &input->base);
- check_auto_unplug_input_node(aio, &input->base, input->base.plugged);
- return input;
-}
-
-static void new_input_by_mixer_control(struct mixer_control *cras_input,
- void *callback_arg)
-{
- struct alsa_io *aio = (struct alsa_io *)callback_arg;
- char node_name[CRAS_IODEV_NAME_BUFFER_SIZE];
- const char *ctl_name = cras_alsa_mixer_get_control_name(cras_input);
-
- if (aio->card_type == ALSA_CARD_TYPE_USB) {
- int ret = snprintf(node_name, sizeof(node_name), "%s: %s",
- aio->base.info.name, ctl_name);
- // Truncation is OK, but add a check to make the compiler happy.
- if (ret == sizeof(node_name))
- node_name[sizeof(node_name) - 1] = '\0';
- new_input(aio, cras_input, node_name);
- } else {
- new_input(aio, cras_input, ctl_name);
- }
-}
-
-/*
- * Finds the output node associated with the jack. Returns NULL if not found.
- */
-static struct alsa_output_node *
-get_output_node_from_jack(struct alsa_io *aio,
- const struct cras_alsa_jack *jack)
-{
- struct mixer_control *mixer_output;
- struct cras_ionode *node = NULL;
- struct alsa_output_node *aout = NULL;
-
- /* Search by jack first. */
- DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, aout, jack, jack);
- if (aout)
- return aout;
-
- /* Search by mixer control next. */
- mixer_output = cras_alsa_jack_get_mixer_output(jack);
- if (mixer_output == NULL)
- return NULL;
-
- DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, aout, mixer_output,
- mixer_output);
- return aout;
-}
-
-static struct alsa_input_node *
-get_input_node_from_jack(struct alsa_io *aio, const struct cras_alsa_jack *jack)
-{
- struct mixer_control *mixer_input;
- struct cras_ionode *node = NULL;
- struct alsa_input_node *ain = NULL;
-
- mixer_input = cras_alsa_jack_get_mixer_input(jack);
- if (mixer_input == NULL) {
- DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, ain, jack,
- jack);
- return ain;
- }
-
- DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, ain, mixer_input,
- mixer_input);
- return ain;
-}
-
-static const struct cras_alsa_jack *get_jack_from_node(struct cras_ionode *node)
-{
- const struct cras_alsa_jack *jack = NULL;
-
- if (node == NULL)
- return NULL;
-
- if (node->dev->direction == CRAS_STREAM_OUTPUT)
- jack = ((struct alsa_output_node *)node)->jack;
- else if (node->dev->direction == CRAS_STREAM_INPUT)
- jack = ((struct alsa_input_node *)node)->jack;
-
- return jack;
-}
-
-/*
- * Returns the dsp name specified in the ucm config. If there is a dsp name
- * specified for the active node, use that. Otherwise NULL should be returned.
- */
-static const char *get_active_dsp_name(struct alsa_io *aio)
-{
- struct cras_ionode *node = aio->base.active_node;
-
- if (node == NULL)
- return NULL;
-
- return node->dsp_name;
-}
-
-/*
- * Creates volume curve for the node associated with given jack.
- */
-static struct cras_volume_curve *
-create_volume_curve_for_jack(const struct cras_card_config *config,
- const struct cras_alsa_jack *jack)
-{
- struct cras_volume_curve *curve;
- const char *name;
-
- /* Use jack's UCM device name as key to get volume curve. */
- name = cras_alsa_jack_get_ucm_device(jack);
- curve = cras_card_config_get_volume_curve_for_control(config, name);
- if (curve)
- return curve;
-
- /* Use alsa jack's name as key to get volume curve. */
- name = cras_alsa_jack_get_name(jack);
- curve = cras_card_config_get_volume_curve_for_control(config, name);
- if (curve)
- return curve;
-
- return NULL;
-}
-
-/*
- * Updates max_supported_channels value into cras_iodev_info.
- * Note that supported_rates, supported_channel_counts, and supported_formats of
- * iodev will be updated to the latest values after calling.
- */
-static void update_max_supported_channels(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- unsigned int max_channels = 0;
- size_t i;
- bool active_node_predicted = false;
- int rc;
-
- /*
- * max_supported_channels might be wrong in dependent PCM cases. Always
- * return 2 for such cases.
- */
- if (aio->has_dependent_dev) {
- max_channels = 2;
- goto update_info;
- }
-
- if (aio->handle) {
- syslog(LOG_ERR,
- "update_max_supported_channels should not be called "
- "while device is opened.");
- return;
- }
-
- /*
- * In the case of updating max_supported_channels on changing jack
- * plugging status of devices, the active node may not be determined
- * yet. Use the first node as the active node for obtaining the value of
- * max_supported_channels.
- */
- if (!iodev->active_node) {
- if (!iodev->nodes)
- goto update_info;
- iodev->active_node = iodev->nodes;
- syslog(LOG_DEBUG,
- "Predict ionode %s as active node temporarily.",
- iodev->active_node->name);
- active_node_predicted = true;
- }
-
- rc = open_dev(iodev);
- if (active_node_predicted)
- iodev->active_node = NULL; // Reset the predicted active_node.
- if (rc)
- goto update_info;
-
- rc = update_supported_formats(iodev);
- if (rc)
- goto close_iodev;
-
- for (i = 0; iodev->supported_channel_counts[i] != 0; i++) {
- if (iodev->supported_channel_counts[i] > max_channels)
- max_channels = iodev->supported_channel_counts[i];
- }
-
-close_iodev:
- close_dev(iodev);
-
-update_info:
- iodev->info.max_supported_channels = max_channels;
-}
-
-/*
- * Callback that is called when an output jack is plugged or unplugged.
- */
-static void jack_output_plug_event(const struct cras_alsa_jack *jack,
- int plugged, void *arg)
-{
- struct alsa_io *aio;
- struct alsa_output_node *node;
- const char *jack_name;
-
- if (arg == NULL)
- return;
-
- aio = (struct alsa_io *)arg;
- node = get_output_node_from_jack(aio, jack);
- jack_name = cras_alsa_jack_get_name(jack);
- if (!strcmp(jack_name, "Speaker Phantom Jack"))
- jack_name = INTERNAL_SPEAKER;
-
- /* If there isn't a node for this jack, create one. */
- if (node == NULL) {
- if (aio->fully_specified) {
- /* When fully specified, can't have new nodes. */
- syslog(LOG_ERR, "No matching output node for jack %s!",
- jack_name);
- return;
- }
- node = new_output(aio, NULL, jack_name);
- if (node == NULL)
- return;
-
- cras_alsa_jack_update_node_type(jack, &(node->base.type));
- }
-
- if (!node->jack) {
- if (aio->fully_specified)
- syslog(LOG_ERR,
- "Jack '%s' was found to match output node '%s'."
- " Please fix your UCM configuration to match.",
- jack_name, node->base.name);
-
- /* If we already have the node, associate with the jack. */
- node->jack = jack;
- if (node->volume_curve == NULL)
- node->volume_curve =
- create_volume_curve_for_jack(aio->config, jack);
- }
-
- syslog(LOG_DEBUG, "%s plugged: %d, %s", jack_name, plugged,
- cras_alsa_mixer_get_control_name(node->mixer_output));
-
- cras_alsa_jack_update_monitor_name(jack, node->base.name,
- sizeof(node->base.name));
-
-#ifdef CRAS_DBUS
- /* The name got from jack might be an invalid UTF8 string. */
- if (!is_utf8_string(node->base.name))
- drop_node_name(&node->base);
-#endif
-
- cras_iodev_set_node_plugged(&node->base, plugged);
-
- check_auto_unplug_output_node(aio, &node->base, plugged);
-
- /*
- * For HDMI plug event cases, update max supported channels according
- * to the current active node.
- */
- if (node->base.type == CRAS_NODE_TYPE_HDMI && plugged)
- update_max_supported_channels(&aio->base);
-}
-
-/*
- * Callback that is called when an input jack is plugged or unplugged.
- */
-static void jack_input_plug_event(const struct cras_alsa_jack *jack,
- int plugged, void *arg)
-{
- struct alsa_io *aio;
- struct alsa_input_node *node;
- struct mixer_control *cras_input;
- const char *jack_name;
-
- if (arg == NULL)
- return;
- aio = (struct alsa_io *)arg;
- node = get_input_node_from_jack(aio, jack);
- jack_name = cras_alsa_jack_get_name(jack);
-
- /* If there isn't a node for this jack, create one. */
- if (node == NULL) {
- if (aio->fully_specified) {
- /* When fully specified, can't have new nodes. */
- syslog(LOG_ERR, "No matching input node for jack %s!",
- jack_name);
- return;
- }
- cras_input = cras_alsa_jack_get_mixer_input(jack);
- node = new_input(aio, cras_input, jack_name);
- if (node == NULL)
- return;
- }
-
- syslog(LOG_DEBUG, "%s plugged: %d, %s", jack_name, plugged,
- cras_alsa_mixer_get_control_name(node->mixer_input));
-
- /* If we already have the node, associate with the jack. */
- if (!node->jack) {
- if (aio->fully_specified)
- syslog(LOG_ERR,
- "Jack '%s' was found to match input node '%s'."
- " Please fix your UCM configuration to match.",
- jack_name, node->base.name);
- node->jack = jack;
- }
-
- cras_iodev_set_node_plugged(&node->base, plugged);
-
- check_auto_unplug_input_node(aio, &node->base, plugged);
-}
-
-/*
- * Sets the name of the given iodev, using the name and index of the card
- * combined with the device index and direction.
- */
-static void set_iodev_name(struct cras_iodev *dev, const char *card_name,
- const char *dev_name, size_t card_index,
- size_t device_index,
- enum CRAS_ALSA_CARD_TYPE card_type, size_t usb_vid,
- size_t usb_pid, char *usb_serial_number)
-{
- snprintf(dev->info.name, sizeof(dev->info.name), "%s: %s:%zu,%zu",
- card_name, dev_name, card_index, device_index);
- dev->info.name[ARRAY_SIZE(dev->info.name) - 1] = '\0';
- syslog(LOG_DEBUG, "Add device name=%s", dev->info.name);
-
- dev->info.stable_id =
- SuperFastHash(card_name, strlen(card_name), strlen(card_name));
- dev->info.stable_id =
- SuperFastHash(dev_name, strlen(dev_name), dev->info.stable_id);
-
- switch (card_type) {
- case ALSA_CARD_TYPE_INTERNAL:
- dev->info.stable_id = SuperFastHash((const char *)&device_index,
- sizeof(device_index),
- dev->info.stable_id);
- break;
- case ALSA_CARD_TYPE_USB:
- dev->info.stable_id =
- SuperFastHash((const char *)&usb_vid, sizeof(usb_vid),
- dev->info.stable_id);
- dev->info.stable_id =
- SuperFastHash((const char *)&usb_pid, sizeof(usb_pid),
- dev->info.stable_id);
- dev->info.stable_id = SuperFastHash(usb_serial_number,
- strlen(usb_serial_number),
- dev->info.stable_id);
- break;
- default:
- break;
- }
- syslog(LOG_DEBUG, "Stable ID=%08x", dev->info.stable_id);
-}
-
-static int get_fixed_rate(struct alsa_io *aio)
-{
- const char *name;
-
- if (aio->base.direction == CRAS_STREAM_OUTPUT) {
- struct alsa_output_node *active = get_active_output(aio);
- if (!active)
- return -ENOENT;
- name = active->base.name;
- } else {
- struct alsa_input_node *active = get_active_input(aio);
- if (!active)
- return -ENOENT;
- name = active->base.name;
- }
-
- return ucm_get_sample_rate_for_dev(aio->ucm, name, aio->base.direction);
-}
-
-static size_t get_fixed_channels(struct alsa_io *aio)
-{
- const char *name;
- int rc;
- size_t channels;
-
- if (aio->base.direction == CRAS_STREAM_OUTPUT) {
- struct alsa_output_node *active = get_active_output(aio);
- if (!active)
- return -ENOENT;
- name = active->base.name;
- } else {
- struct alsa_input_node *active = get_active_input(aio);
- if (!active)
- return -ENOENT;
- name = active->base.name;
- }
-
- rc = ucm_get_channels_for_dev(aio->ucm, name, aio->base.direction,
- &channels);
- return (rc) ? 0 : channels;
-}
-
-/*
- * Updates the supported sample rates and channel counts.
- */
-static int update_supported_formats(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- int err;
- int fixed_rate;
- size_t fixed_channels;
-
- free(iodev->supported_rates);
- iodev->supported_rates = NULL;
- free(iodev->supported_channel_counts);
- iodev->supported_channel_counts = NULL;
- free(iodev->supported_formats);
- iodev->supported_formats = NULL;
-
- err = cras_alsa_fill_properties(aio->handle, &iodev->supported_rates,
- &iodev->supported_channel_counts,
- &iodev->supported_formats);
- if (err)
- return err;
-
- if (aio->ucm) {
- /* Allow UCM to override supplied rates. */
- fixed_rate = get_fixed_rate(aio);
- if (fixed_rate > 0) {
- free(iodev->supported_rates);
- iodev->supported_rates = (size_t *)malloc(
- 2 * sizeof(iodev->supported_rates[0]));
- iodev->supported_rates[0] = fixed_rate;
- iodev->supported_rates[1] = 0;
- }
-
- /* Allow UCM to override supported channel counts. */
- fixed_channels = get_fixed_channels(aio);
- if (fixed_channels > 0) {
- free(iodev->supported_channel_counts);
- iodev->supported_channel_counts = (size_t *)malloc(
- 2 * sizeof(iodev->supported_channel_counts[0]));
- iodev->supported_channel_counts[0] = fixed_channels;
- iodev->supported_channel_counts[1] = 0;
- }
- }
- return 0;
-}
-
-/*
- * Builds software volume scalers for output nodes in the device.
- */
-static void build_softvol_scalers(struct alsa_io *aio)
-{
- struct cras_ionode *ionode;
-
- DL_FOREACH (aio->base.nodes, ionode) {
- struct alsa_output_node *aout;
- const struct cras_volume_curve *curve;
-
- aout = (struct alsa_output_node *)ionode;
- curve = get_curve_for_output_node(aio, aout);
-
- ionode->softvol_scalers = softvol_build_from_curve(curve);
- }
-}
-
-static void enable_active_ucm(struct alsa_io *aio, int plugged)
-{
- const struct cras_alsa_jack *jack;
- const char *name;
-
- if (aio->base.direction == CRAS_STREAM_OUTPUT) {
- struct alsa_output_node *active = get_active_output(aio);
- if (!active)
- return;
- name = active->base.name;
- jack = active->jack;
- } else {
- struct alsa_input_node *active = get_active_input(aio);
- if (!active)
- return;
- name = active->base.name;
- jack = active->jack;
- }
-
- if (jack)
- cras_alsa_jack_enable_ucm(jack, plugged);
- else if (aio->ucm)
- ucm_set_enabled(aio->ucm, name, plugged);
-}
-
-static int fill_whole_buffer_with_zeros(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- int rc;
- uint8_t *dst = NULL;
- size_t format_bytes;
-
- /* Fill whole buffer with zeros. */
- rc = cras_alsa_mmap_get_whole_buffer(aio->handle, &dst);
-
- if (rc < 0) {
- syslog(LOG_ERR, "Failed to get whole buffer: %s",
- snd_strerror(rc));
- return rc;
- }
-
- format_bytes = cras_get_format_bytes(iodev->format);
- memset(dst, 0, iodev->buffer_size * format_bytes);
-
- return 0;
-}
-
-/*
- * Move appl_ptr to min_buffer_level + min_cb_level frames ahead of hw_ptr
- * when resuming from free run.
- */
-static int adjust_appl_ptr_for_leaving_free_run(struct cras_iodev *odev)
-{
- struct alsa_io *aio = (struct alsa_io *)odev;
- snd_pcm_uframes_t ahead;
-
- ahead = odev->min_buffer_level + odev->min_cb_level;
- return cras_alsa_resume_appl_ptr(aio->handle, ahead);
-}
-
-/*
- * Move appl_ptr to min_buffer_level + min_cb_level * 1.5 frames ahead of
- * hw_ptr when adjusting appl_ptr from underrun.
- */
-static int adjust_appl_ptr_for_underrun(struct cras_iodev *odev)
-{
- struct alsa_io *aio = (struct alsa_io *)odev;
- snd_pcm_uframes_t ahead;
-
- ahead = odev->min_buffer_level + odev->min_cb_level +
- odev->min_cb_level / 2;
- return cras_alsa_resume_appl_ptr(aio->handle, ahead);
-}
-
-/* This function is for leaving no-stream state but still not in free run yet.
- * The device may have valid samples remaining. We need to adjust appl_ptr to
- * the correct position, which is MAX(min_cb_level + min_buffer_level,
- * valid_sample) */
-static int adjust_appl_ptr_samples_remaining(struct cras_iodev *odev)
-{
- struct alsa_io *aio = (struct alsa_io *)odev;
- int rc;
- unsigned int real_hw_level, valid_sample, offset;
- struct timespec hw_tstamp;
-
- /* Get the amount of valid samples which haven't been played yet.
- * The real_hw_level is the real hw_level in device buffer. It doesn't
- * subtract min_buffer_level. */
- valid_sample = 0;
- rc = odev->frames_queued(odev, &hw_tstamp);
- if (rc < 0)
- return rc;
- real_hw_level = rc;
-
- /*
- * If underrun happened, handle it. Because alsa_output_underrun function
- * has already called adjust_appl_ptr, we don't need to call it again.
- */
- if (real_hw_level <= odev->min_buffer_level)
- return cras_iodev_output_underrun(odev, real_hw_level, 0);
-
- if (real_hw_level > aio->filled_zeros_for_draining)
- valid_sample = real_hw_level - aio->filled_zeros_for_draining;
-
- offset = MAX(odev->min_buffer_level + odev->min_cb_level, valid_sample);
-
- /* Fill zeros to make sure there are enough zero samples in device buffer.*/
- if (offset > real_hw_level) {
- rc = cras_iodev_fill_odev_zeros(odev, offset - real_hw_level);
- if (rc)
- return rc;
- }
- return cras_alsa_resume_appl_ptr(aio->handle, offset);
-}
-
-static int alsa_output_underrun(struct cras_iodev *odev)
-{
- int rc;
-
- /* Fill whole buffer with zeros. This avoids samples left in buffer causing
- * noise when device plays them. */
- rc = fill_whole_buffer_with_zeros(odev);
- if (rc)
- return rc;
- /* Adjust appl_ptr to leave underrun. */
- return adjust_appl_ptr_for_underrun(odev);
-}
-
-static int possibly_enter_free_run(struct cras_iodev *odev)
-{
- struct alsa_io *aio = (struct alsa_io *)odev;
- int rc;
- unsigned int real_hw_level, fr_to_write;
- struct timespec hw_tstamp;
-
- if (aio->free_running)
- return 0;
-
- /* Check if all valid samples are played. If all valid samples are played,
- * fill whole buffer with zeros. The real_hw_level is the real hw_level in
- * device buffer. It doesn't subtract min_buffer_level.*/
- rc = odev->frames_queued(odev, &hw_tstamp);
- if (rc < 0)
- return rc;
- real_hw_level = rc;
-
- /* If underrun happened, handle it and enter free run state. */
- if (real_hw_level <= odev->min_buffer_level) {
- rc = cras_iodev_output_underrun(odev, real_hw_level, 0);
- if (rc < 0)
- return rc;
- aio->free_running = 1;
- return 0;
- }
-
- if (real_hw_level <= aio->filled_zeros_for_draining ||
- real_hw_level == 0) {
- rc = fill_whole_buffer_with_zeros(odev);
- if (rc < 0)
- return rc;
- aio->free_running = 1;
- return 0;
- }
-
- /* Fill zeros to drain valid samples. */
- fr_to_write = MIN(cras_time_to_frames(&no_stream_fill_zeros_duration,
- odev->format->frame_rate),
- odev->buffer_size - real_hw_level);
- rc = cras_iodev_fill_odev_zeros(odev, fr_to_write);
- if (rc)
- return rc;
- aio->filled_zeros_for_draining += fr_to_write;
-
- return 0;
-}
-
-static int leave_free_run(struct cras_iodev *odev)
-{
- struct alsa_io *aio = (struct alsa_io *)odev;
- int rc;
-
- /* Restart rate estimation because free run internval should not
- * be included. */
- cras_iodev_reset_rate_estimator(odev);
-
- if (aio->free_running)
- rc = adjust_appl_ptr_for_leaving_free_run(odev);
- else
- rc = adjust_appl_ptr_samples_remaining(odev);
- if (rc) {
- syslog(LOG_ERR, "device %s failed to leave free run, rc = %d",
- odev->info.name, rc);
- return rc;
- }
- aio->free_running = 0;
- aio->filled_zeros_for_draining = 0;
-
- return 0;
-}
-
-/*
- * Free run state is the optimization of no_stream playback on alsa_io.
- * The whole buffer will be filled with zeros. Device can play these zeros
- * indefinitely. When there is new meaningful sample, appl_ptr should be
- * resumed to some distance ahead of hw_ptr.
- */
-static int no_stream(struct cras_iodev *odev, int enable)
-{
- if (enable)
- return possibly_enter_free_run(odev);
- else
- return leave_free_run(odev);
-}
-
-static int is_free_running(const struct cras_iodev *odev)
-{
- struct alsa_io *aio = (struct alsa_io *)odev;
-
- return aio->free_running;
-}
-
-static unsigned int get_num_severe_underruns(const struct cras_iodev *iodev)
-{
- const struct alsa_io *aio = (const struct alsa_io *)iodev;
- return aio->num_severe_underruns;
-}
-
-static void set_default_hotword_model(struct cras_iodev *iodev)
-{
- const char *default_models[] = { "en_all", "en_us" };
- cras_node_id_t node_id;
- unsigned i;
-
- if (!iodev->active_node ||
- iodev->active_node->type != CRAS_NODE_TYPE_HOTWORD)
- return;
-
- node_id = cras_make_node_id(iodev->info.idx, iodev->active_node->idx);
- /* This is a no-op if the default_model is not supported */
- for (i = 0; i < ARRAY_SIZE(default_models); ++i)
- if (!cras_iodev_list_set_hotword_model(node_id,
- default_models[i]))
- return;
-}
-
-static int get_valid_frames(struct cras_iodev *odev, struct timespec *tstamp)
-{
- struct alsa_io *aio = (struct alsa_io *)odev;
- int rc;
- unsigned int real_hw_level;
-
- /*
- * Get the amount of valid frames which haven't been played yet.
- * The real_hw_level is the real hw_level in device buffer. It doesn't
- * subtract min_buffer_level.
- */
- if (aio->free_running) {
- clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
- return 0;
- }
-
- rc = odev->frames_queued(odev, tstamp);
- if (rc < 0)
- return rc;
- real_hw_level = rc;
-
- if (real_hw_level > aio->filled_zeros_for_draining)
- return real_hw_level - aio->filled_zeros_for_draining;
-
- return 0;
-}
-
-static int support_noise_cancellation(const struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
-
- if (!aio->ucm || !iodev->active_node)
- return 0;
-
- return ucm_node_noise_cancellation_exists(aio->ucm,
- iodev->active_node->name);
-}
-
-/*
- * Exported Interface.
- */
-
-struct cras_iodev *
-alsa_iodev_create(size_t card_index, const char *card_name, size_t device_index,
- const char *pcm_name, const char *dev_name,
- const char *dev_id, enum CRAS_ALSA_CARD_TYPE card_type,
- int is_first, struct cras_alsa_mixer *mixer,
- const struct cras_card_config *config,
- struct cras_use_case_mgr *ucm, snd_hctl_t *hctl,
- enum CRAS_STREAM_DIRECTION direction, size_t usb_vid,
- size_t usb_pid, char *usb_serial_number)
-{
- struct alsa_io *aio;
- struct cras_iodev *iodev;
-
- if (direction != CRAS_STREAM_INPUT && direction != CRAS_STREAM_OUTPUT)
- return NULL;
-
- aio = (struct alsa_io *)calloc(1, sizeof(*aio));
- if (!aio)
- return NULL;
- iodev = &aio->base;
- iodev->direction = direction;
-
- aio->device_index = device_index;
- aio->card_type = card_type;
- aio->is_first = is_first;
- aio->handle = NULL;
- aio->num_severe_underruns = 0;
- if (dev_name) {
- aio->dev_name = strdup(dev_name);
- if (!aio->dev_name)
- goto cleanup_iodev;
- }
- if (dev_id) {
- aio->dev_id = strdup(dev_id);
- if (!aio->dev_id)
- goto cleanup_iodev;
- }
- aio->free_running = 0;
- aio->filled_zeros_for_draining = 0;
- aio->has_dependent_dev = 0;
- aio->pcm_name = strdup(pcm_name);
- if (aio->pcm_name == NULL)
- goto cleanup_iodev;
-
- if (direction == CRAS_STREAM_INPUT) {
- aio->alsa_stream = SND_PCM_STREAM_CAPTURE;
- aio->base.set_capture_gain = set_alsa_capture_gain;
- aio->base.set_capture_mute = set_alsa_capture_gain;
- } else {
- aio->alsa_stream = SND_PCM_STREAM_PLAYBACK;
- aio->base.set_volume = set_alsa_volume;
- aio->base.set_mute = set_alsa_mute;
- aio->base.output_underrun = alsa_output_underrun;
- }
- iodev->open_dev = open_dev;
- iodev->configure_dev = configure_dev;
- iodev->close_dev = close_dev;
- iodev->update_supported_formats = update_supported_formats;
- iodev->frames_queued = frames_queued;
- iodev->delay_frames = delay_frames;
- iodev->get_buffer = get_buffer;
- iodev->put_buffer = put_buffer;
- iodev->flush_buffer = flush_buffer;
- iodev->start = start;
- iodev->update_active_node = update_active_node;
- iodev->update_channel_layout = update_channel_layout;
- iodev->set_hotword_model = set_hotword_model;
- iodev->get_hotword_models = get_hotword_models;
- iodev->no_stream = no_stream;
- iodev->is_free_running = is_free_running;
- iodev->get_num_severe_underruns = get_num_severe_underruns;
- iodev->get_valid_frames = get_valid_frames;
- iodev->set_swap_mode_for_node = cras_iodev_dsp_set_swap_mode_for_node;
- iodev->support_noise_cancellation = support_noise_cancellation;
-
- if (card_type == ALSA_CARD_TYPE_USB)
- iodev->min_buffer_level = USB_EXTRA_BUFFER_FRAMES;
-
- iodev->ramp = cras_ramp_create();
- if (iodev->ramp == NULL)
- goto cleanup_iodev;
- iodev->initial_ramp_request = CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK;
-
- aio->mixer = mixer;
- aio->config = config;
- if (direction == CRAS_STREAM_OUTPUT) {
- aio->default_volume_curve =
- cras_card_config_get_volume_curve_for_control(
- config, "Default");
- if (aio->default_volume_curve == NULL)
- aio->default_volume_curve =
- cras_volume_curve_create_default();
- }
- aio->ucm = ucm;
- if (ucm) {
- unsigned int level;
- int rc;
-
- /* Set callback for swap mode if it is supported
- * in ucm modifier. */
- if (ucm_swap_mode_exists(ucm))
- aio->base.set_swap_mode_for_node =
- set_alsa_node_swapped;
-
- rc = ucm_get_min_buffer_level(ucm, &level);
- if (!rc && direction == CRAS_STREAM_OUTPUT)
- iodev->min_buffer_level = level;
- }
-
- set_iodev_name(iodev, card_name, dev_name, card_index, device_index,
- card_type, usb_vid, usb_pid, usb_serial_number);
-
- aio->jack_list = cras_alsa_jack_list_create(
- card_index, card_name, device_index, is_first, mixer, ucm, hctl,
- direction,
- direction == CRAS_STREAM_OUTPUT ? jack_output_plug_event :
- jack_input_plug_event,
- aio);
- if (!aio->jack_list)
- goto cleanup_iodev;
-
- /* HDMI outputs don't have volume adjustment, do it in software. */
- if (direction == CRAS_STREAM_OUTPUT && strstr(dev_name, HDMI))
- iodev->software_volume_needed = 1;
-
- /* Add this now so that cleanup of the iodev (in case of error or card
- * card removal will function as expected. */
- if (direction == CRAS_STREAM_OUTPUT)
- cras_iodev_list_add_output(&aio->base);
- else
- cras_iodev_list_add_input(&aio->base);
- return &aio->base;
-
-cleanup_iodev:
- free_alsa_iodev_resources(aio);
- free(aio);
- return NULL;
-}
-
-int alsa_iodev_legacy_complete_init(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- const char *dev_name;
- const char *dev_id;
- enum CRAS_STREAM_DIRECTION direction;
- int err;
- int is_first;
- struct cras_alsa_mixer *mixer;
-
- if (!aio)
- return -EINVAL;
- direction = iodev->direction;
- dev_name = aio->dev_name;
- dev_id = aio->dev_id;
- is_first = aio->is_first;
- mixer = aio->mixer;
-
- /* Create output nodes for mixer controls, such as Headphone
- * and Speaker, only for the first device. */
- if (direction == CRAS_STREAM_OUTPUT && is_first)
- cras_alsa_mixer_list_outputs(mixer, new_output_by_mixer_control,
- aio);
- else if (direction == CRAS_STREAM_INPUT && is_first)
- cras_alsa_mixer_list_inputs(mixer, new_input_by_mixer_control,
- aio);
-
- err = cras_alsa_jack_list_find_jacks_by_name_matching(aio->jack_list);
- if (err)
- return err;
-
- /* Create nodes for jacks that aren't associated with an
- * already existing node. Get an initial read of the jacks for
- * this device. */
- cras_alsa_jack_list_report(aio->jack_list);
-
- /* Make a default node if there is still no node for this
- * device, or we still don't have the "Speaker"/"Internal Mic"
- * node for the first internal device. Note that the default
- * node creation can be supressed by UCM flags for platforms
- * which really don't have an internal device. */
- if ((direction == CRAS_STREAM_OUTPUT) &&
- !no_create_default_output_node(aio)) {
- if (first_internal_device(aio) &&
- !has_node(aio, INTERNAL_SPEAKER) && !has_node(aio, HDMI)) {
- if (strstr(aio->base.info.name, HDMI))
- new_output(aio, NULL, HDMI);
- else
- new_output(aio, NULL, INTERNAL_SPEAKER);
- } else if (!aio->base.nodes) {
- new_output(aio, NULL, DEFAULT);
- }
- } else if ((direction == CRAS_STREAM_INPUT) &&
- !no_create_default_input_node(aio)) {
- if (first_internal_device(aio) &&
- !has_node(aio, INTERNAL_MICROPHONE))
- new_input(aio, NULL, INTERNAL_MICROPHONE);
- else if (strstr(dev_name, KEYBOARD_MIC))
- new_input(aio, NULL, KEYBOARD_MIC);
- else if (dev_id && strstr(dev_id, HOTWORD_DEV))
- new_input(aio, NULL, HOTWORD_DEV);
- else if (!aio->base.nodes)
- new_input(aio, NULL, DEFAULT);
- }
-
- /* Build software volume scalers. */
- if (direction == CRAS_STREAM_OUTPUT)
- build_softvol_scalers(aio);
-
- /* Set the active node as the best node we have now. */
- alsa_iodev_set_active_node(&aio->base, first_plugged_node(&aio->base),
- 0);
-
- /* Set plugged for the first USB device per card when it appears if
- * there is no jack reporting plug status. */
- if (aio->card_type == ALSA_CARD_TYPE_USB && is_first &&
- !get_jack_from_node(iodev->active_node))
- cras_iodev_set_node_plugged(iodev->active_node, 1);
-
- set_default_hotword_model(iodev);
-
- /* Record max supported channels into cras_iodev_info. */
- update_max_supported_channels(iodev);
-
- return 0;
-}
-
-int alsa_iodev_ucm_add_nodes_and_jacks(struct cras_iodev *iodev,
- struct ucm_section *section)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- struct mixer_control *control;
- struct alsa_input_node *input_node = NULL;
- struct cras_alsa_jack *jack;
- struct alsa_output_node *output_node = NULL;
- int rc;
-
- if (!aio || !section)
- return -EINVAL;
-
- /* Allow this section to add as a new node only if the device id
- * or dependent device id matches this iodev. */
- if (((uint32_t)section->dev_idx != aio->device_index) &&
- ((uint32_t)section->dependent_dev_idx != aio->device_index))
- return -EINVAL;
-
- /* Set flag has_dependent_dev for the case of dependent device. */
- if (section->dependent_dev_idx != -1)
- aio->has_dependent_dev = 1;
-
- /* This iodev is fully specified. Avoid automatic node creation. */
- aio->fully_specified = 1;
-
- /* Check here in case the DmaPeriodMicrosecs flag has only been
- * specified on one of many device entries with the same PCM. */
- if (!aio->dma_period_set_microsecs)
- aio->dma_period_set_microsecs =
- ucm_get_dma_period_for_dev(aio->ucm, section->name);
-
- /* Create a node matching this section. If there is a matching
- * control use that, otherwise make a node without a control. */
- control = cras_alsa_mixer_get_control_for_section(aio->mixer, section);
- if (iodev->direction == CRAS_STREAM_OUTPUT) {
- output_node = new_output(aio, control, section->name);
- if (!output_node)
- return -ENOMEM;
- output_node->pcm_name = strdup(section->pcm_name);
- } else if (iodev->direction == CRAS_STREAM_INPUT) {
- input_node = new_input(aio, control, section->name);
- if (!input_node)
- return -ENOMEM;
- input_node->pcm_name = strdup(section->pcm_name);
- }
-
- /* Find any jack controls for this device. */
- rc = cras_alsa_jack_list_add_jack_for_section(aio->jack_list, section,
- &jack);
- if (rc)
- return rc;
-
- /* Associated the jack with the node. */
- if (jack) {
- if (output_node) {
- output_node->jack = jack;
- if (!output_node->volume_curve)
- output_node->volume_curve =
- create_volume_curve_for_jack(
- aio->config, jack);
- } else if (input_node) {
- input_node->jack = jack;
- }
- }
- return 0;
-}
-
-void alsa_iodev_ucm_complete_init(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- struct cras_ionode *node;
-
- if (!iodev)
- return;
-
- /* Get an initial read of the jacks for this device. */
- cras_alsa_jack_list_report(aio->jack_list);
-
- /* Build software volume scaler. */
- if (iodev->direction == CRAS_STREAM_OUTPUT)
- build_softvol_scalers(aio);
-
- /* Set the active node as the best node we have now. */
- alsa_iodev_set_active_node(&aio->base, first_plugged_node(&aio->base),
- 0);
-
- /*
- * Set plugged for the USB device per card when it appears if
- * there is no jack reporting plug status
- */
- if (aio->card_type == ALSA_CARD_TYPE_USB) {
- DL_FOREACH (iodev->nodes, node) {
- if (!get_jack_from_node(node))
- cras_iodev_set_node_plugged(node, 1);
- }
- }
-
- set_default_hotword_model(iodev);
-
- node = iodev->active_node;
-
- /* Record max supported channels into cras_iodev_info. */
- if (node && node->plugged)
- update_max_supported_channels(iodev);
-}
-
-void alsa_iodev_destroy(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- int rc;
-
- if (iodev->direction == CRAS_STREAM_INPUT)
- rc = cras_iodev_list_rm_input(iodev);
- else
- rc = cras_iodev_list_rm_output(iodev);
-
- if (rc == -EBUSY) {
- syslog(LOG_ERR, "Failed to remove iodev %s", iodev->info.name);
- return;
- }
-
- /* Free resources when device successfully removed. */
- cras_alsa_jack_list_destroy(aio->jack_list);
- free_alsa_iodev_resources(aio);
- cras_volume_curve_destroy(aio->default_volume_curve);
- free(iodev);
-}
-
-unsigned alsa_iodev_index(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- return aio->device_index;
-}
-
-int alsa_iodev_has_hctl_jacks(struct cras_iodev *iodev)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- return cras_alsa_jack_list_has_hctl_jacks(aio->jack_list);
-}
-
-static void alsa_iodev_unmute_node(struct alsa_io *aio,
- struct cras_ionode *ionode)
-{
- struct alsa_output_node *active = (struct alsa_output_node *)ionode;
- struct mixer_control *mixer = active->mixer_output;
- struct alsa_output_node *output;
- struct cras_ionode *node;
-
- /* If this node is associated with mixer output, unmute the
- * active mixer output and mute all others, otherwise just set
- * the node as active and set the volume curve. */
- if (mixer) {
- /* Unmute the active mixer output, mute all others. */
- DL_FOREACH (aio->base.nodes, node) {
- output = (struct alsa_output_node *)node;
- if (output->mixer_output)
- cras_alsa_mixer_set_output_active_state(
- output->mixer_output, node == ionode);
- }
- }
-}
-
-static int alsa_iodev_set_active_node(struct cras_iodev *iodev,
- struct cras_ionode *ionode,
- unsigned dev_enabled)
-{
- struct alsa_io *aio = (struct alsa_io *)iodev;
- int rc = 0;
-
- if (iodev->active_node == ionode)
- goto skip;
-
- /* Disable jack ucm before switching node. */
- enable_active_ucm(aio, 0);
- if (dev_enabled && (iodev->direction == CRAS_STREAM_OUTPUT))
- alsa_iodev_unmute_node(aio, ionode);
-
- cras_iodev_set_active_node(iodev, ionode);
- aio->base.dsp_name = get_active_dsp_name(aio);
- cras_iodev_update_dsp(iodev);
-skip:
- enable_active_ucm(aio, dev_enabled);
- if (ionode->type == CRAS_NODE_TYPE_HOTWORD) {
- if (dev_enabled) {
- rc = ucm_enable_hotword_model(aio->ucm);
- if (rc < 0)
- return rc;
- } else
- ucm_disable_all_hotword_models(aio->ucm);
- }
- /* Setting the volume will also unmute if the system isn't muted. */
- init_device_settings(aio);
- return 0;
-}