summaryrefslogtreecommitdiff
path: root/cras/src/server/cras_iodev_list.c
diff options
context:
space:
mode:
Diffstat (limited to 'cras/src/server/cras_iodev_list.c')
-rw-r--r--cras/src/server/cras_iodev_list.c1920
1 files changed, 0 insertions, 1920 deletions
diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c
deleted file mode 100644
index b818c97b..00000000
--- a/cras/src/server/cras_iodev_list.c
+++ /dev/null
@@ -1,1920 +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 <syslog.h>
-
-#include "audio_thread.h"
-#include "cras_empty_iodev.h"
-#include "cras_iodev.h"
-#include "cras_iodev_info.h"
-#include "cras_iodev_list.h"
-#include "cras_loopback_iodev.h"
-#include "cras_main_thread_log.h"
-#include "cras_observer.h"
-#include "cras_rstream.h"
-#include "cras_server.h"
-#include "cras_tm.h"
-#include "cras_types.h"
-#include "cras_system_state.h"
-#include "server_stream.h"
-#include "softvol_curve.h"
-#include "stream_list.h"
-#include "test_iodev.h"
-#include "utlist.h"
-
-const struct timespec idle_timeout_interval = { .tv_sec = 10, .tv_nsec = 0 };
-
-/* Linked list of available devices. */
-struct iodev_list {
- struct cras_iodev *iodevs;
- size_t size;
-};
-
-/* List of enabled input/output devices.
- * dev - The device.
- * init_timer - Timer for a delayed call to init this iodev.
- */
-struct enabled_dev {
- struct cras_iodev *dev;
- struct enabled_dev *prev, *next;
-};
-
-struct dev_init_retry {
- int dev_idx;
- struct cras_timer *init_timer;
- struct dev_init_retry *next, *prev;
-};
-
-struct device_enabled_cb {
- device_enabled_callback_t enabled_cb;
- device_disabled_callback_t disabled_cb;
- void *cb_data;
- struct device_enabled_cb *next, *prev;
-};
-
-struct main_thread_event_log *main_log;
-
-/* Lists for devs[CRAS_STREAM_INPUT] and devs[CRAS_STREAM_OUTPUT]. */
-static struct iodev_list devs[CRAS_NUM_DIRECTIONS];
-/* The observer client iodev_list used to listen on various events. */
-static struct cras_observer_client *list_observer;
-/* Keep a list of enabled inputs and outputs. */
-static struct enabled_dev *enabled_devs[CRAS_NUM_DIRECTIONS];
-/* Keep an empty device per direction. */
-static struct cras_iodev *fallback_devs[CRAS_NUM_DIRECTIONS];
-/* Special empty device for hotword streams. */
-static struct cras_iodev *empty_hotword_dev;
-/* Loopback devices. */
-static struct cras_iodev *loopdev_post_mix;
-static struct cras_iodev *loopdev_post_dsp;
-/* List of pending device init retries. */
-static struct dev_init_retry *init_retries;
-
-/* Keep a constantly increasing index for iodevs. Index 0 is reserved
- * to mean "no device". */
-static uint32_t next_iodev_idx = MAX_SPECIAL_DEVICE_IDX;
-
-/* Call when a device is enabled or disabled. */
-struct device_enabled_cb *device_enable_cbs;
-
-/* Thread that handles audio input and output. */
-static struct audio_thread *audio_thread;
-/* List of all streams. */
-static struct stream_list *stream_list;
-/* Idle device timer. */
-static struct cras_timer *idle_timer;
-/* Flag to indicate that the stream list is disconnected from audio thread. */
-static int stream_list_suspended = 0;
-/* If init device failed, retry after 1 second. */
-static const unsigned int INIT_DEV_DELAY_MS = 1000;
-/* Flag to indicate that hotword streams are suspended. */
-static int hotword_suspended = 0;
-/* Flag to indicate that suspended hotword streams should be auto-resumed at
- * system resume. */
-static int hotword_auto_resume = 0;
-
-static void idle_dev_check(struct cras_timer *timer, void *data);
-
-static struct cras_iodev *find_dev(size_t dev_index)
-{
- struct cras_iodev *dev;
-
- DL_FOREACH (devs[CRAS_STREAM_OUTPUT].iodevs, dev)
- if (dev->info.idx == dev_index)
- return dev;
-
- DL_FOREACH (devs[CRAS_STREAM_INPUT].iodevs, dev)
- if (dev->info.idx == dev_index)
- return dev;
-
- return NULL;
-}
-
-static struct cras_ionode *find_node(struct cras_iodev *iodev,
- unsigned int node_idx)
-{
- struct cras_ionode *node;
- DL_SEARCH_SCALAR(iodev->nodes, node, idx, node_idx);
- return node;
-}
-
-/* Adds a device to the list. Used from add_input and add_output. */
-static int add_dev_to_list(struct cras_iodev *dev)
-{
- struct cras_iodev *tmp;
- uint32_t new_idx;
- struct iodev_list *list = &devs[dev->direction];
-
- DL_FOREACH (list->iodevs, tmp)
- if (tmp == dev)
- return -EEXIST;
-
- dev->format = NULL;
- dev->format = NULL;
- dev->prev = dev->next = NULL;
-
- /* Move to the next index and make sure it isn't taken. */
- new_idx = next_iodev_idx;
- while (1) {
- if (new_idx < MAX_SPECIAL_DEVICE_IDX)
- new_idx = MAX_SPECIAL_DEVICE_IDX;
- DL_SEARCH_SCALAR(list->iodevs, tmp, info.idx, new_idx);
- if (tmp == NULL)
- break;
- new_idx++;
- }
- dev->info.idx = new_idx;
- next_iodev_idx = new_idx + 1;
- list->size++;
-
- syslog(LOG_INFO, "Adding %s dev at index %u.",
- dev->direction == CRAS_STREAM_OUTPUT ? "output" : "input",
- dev->info.idx);
- DL_PREPEND(list->iodevs, dev);
-
- cras_iodev_list_update_device_list();
- return 0;
-}
-
-/* Removes a device to the list. Used from rm_input and rm_output. */
-static int rm_dev_from_list(struct cras_iodev *dev)
-{
- struct cras_iodev *tmp;
-
- DL_FOREACH (devs[dev->direction].iodevs, tmp)
- if (tmp == dev) {
- if (cras_iodev_is_open(dev))
- return -EBUSY;
- DL_DELETE(devs[dev->direction].iodevs, dev);
- devs[dev->direction].size--;
- return 0;
- }
-
- /* Device not found. */
- return -EINVAL;
-}
-
-/* Fills a dev_info array from the iodev_list. */
-static void fill_dev_list(struct iodev_list *list,
- struct cras_iodev_info *dev_info, size_t out_size)
-{
- int i = 0;
- struct cras_iodev *tmp;
- DL_FOREACH (list->iodevs, tmp) {
- memcpy(&dev_info[i], &tmp->info, sizeof(dev_info[0]));
- i++;
- if (i == out_size)
- return;
- }
-}
-
-static const char *node_type_to_str(struct cras_ionode *node)
-{
- switch (node->type) {
- case CRAS_NODE_TYPE_INTERNAL_SPEAKER:
- return "INTERNAL_SPEAKER";
- case CRAS_NODE_TYPE_HEADPHONE:
- return "HEADPHONE";
- case CRAS_NODE_TYPE_HDMI:
- return "HDMI";
- case CRAS_NODE_TYPE_HAPTIC:
- return "HAPTIC";
- case CRAS_NODE_TYPE_MIC:
- switch (node->position) {
- case NODE_POSITION_INTERNAL:
- return "INTERNAL_MIC";
- case NODE_POSITION_FRONT:
- return "FRONT_MIC";
- case NODE_POSITION_REAR:
- return "REAR_MIC";
- case NODE_POSITION_KEYBOARD:
- return "KEYBOARD_MIC";
- case NODE_POSITION_EXTERNAL:
- default:
- return "MIC";
- }
- case CRAS_NODE_TYPE_HOTWORD:
- return "HOTWORD";
- case CRAS_NODE_TYPE_LINEOUT:
- return "LINEOUT";
- case CRAS_NODE_TYPE_POST_MIX_PRE_DSP:
- return "POST_MIX_LOOPBACK";
- case CRAS_NODE_TYPE_POST_DSP:
- return "POST_DSP_LOOPBACK";
- case CRAS_NODE_TYPE_USB:
- return "USB";
- case CRAS_NODE_TYPE_BLUETOOTH:
- return "BLUETOOTH";
- case CRAS_NODE_TYPE_BLUETOOTH_NB_MIC:
- return "BLUETOOTH_NB_MIC";
- case CRAS_NODE_TYPE_FALLBACK_NORMAL:
- return "FALLBACK_NORMAL";
- case CRAS_NODE_TYPE_FALLBACK_ABNORMAL:
- return "FALLBACK_ABNORMAL";
- case CRAS_NODE_TYPE_ECHO_REFERENCE:
- return "ECHO_REFERENCE";
- case CRAS_NODE_TYPE_ALSA_LOOPBACK:
- return "ALSA_LOOPBACK";
- case CRAS_NODE_TYPE_UNKNOWN:
- default:
- return "UNKNOWN";
- }
-}
-
-/* Fills an ionode_info array from the iodev_list. */
-static int fill_node_list(struct iodev_list *list,
- struct cras_ionode_info *node_info, size_t out_size)
-{
- int i = 0;
- struct cras_iodev *dev;
- struct cras_ionode *node;
- DL_FOREACH (list->iodevs, dev) {
- DL_FOREACH (dev->nodes, node) {
- node_info->iodev_idx = dev->info.idx;
- node_info->ionode_idx = node->idx;
- node_info->plugged = node->plugged;
- node_info->plugged_time.tv_sec =
- node->plugged_time.tv_sec;
- node_info->plugged_time.tv_usec =
- node->plugged_time.tv_usec;
- node_info->active =
- dev->is_enabled && (dev->active_node == node);
- node_info->volume = node->volume;
- node_info->capture_gain = node->capture_gain;
- node_info->ui_gain_scaler = node->ui_gain_scaler;
- node_info->left_right_swapped =
- node->left_right_swapped;
- node_info->stable_id = node->stable_id;
- strcpy(node_info->name, node->name);
- strcpy(node_info->active_hotword_model,
- node->active_hotword_model);
- snprintf(node_info->type, sizeof(node_info->type), "%s",
- node_type_to_str(node));
- node_info->type_enum = node->type;
- node_info++;
- i++;
- if (i == out_size)
- return i;
- }
- }
- return i;
-}
-
-/* Copies the info for each device in the list to "list_out". */
-static int get_dev_list(struct iodev_list *list,
- struct cras_iodev_info **list_out)
-{
- struct cras_iodev_info *dev_info;
-
- if (!list_out)
- return list->size;
-
- *list_out = NULL;
- if (list->size == 0)
- return 0;
-
- dev_info = malloc(sizeof(*list_out[0]) * list->size);
- if (dev_info == NULL)
- return -ENOMEM;
-
- fill_dev_list(list, dev_info, list->size);
-
- *list_out = dev_info;
- return list->size;
-}
-
-/* Called when the system volume changes. Pass the current volume setting to
- * the default output if it is active. */
-static void sys_vol_change(void *context, int32_t volume)
-{
- struct cras_iodev *dev;
-
- DL_FOREACH (devs[CRAS_STREAM_OUTPUT].iodevs, dev) {
- if (dev->set_volume && cras_iodev_is_open(dev))
- dev->set_volume(dev);
- }
-}
-
-/* Called when the system mute state changes. Pass the current mute setting
- * to the default output if it is active. */
-static void sys_mute_change(void *context, int muted, int user_muted,
- int mute_locked)
-{
- struct cras_iodev *dev;
- int should_mute = muted || user_muted;
-
- DL_FOREACH (devs[CRAS_STREAM_OUTPUT].iodevs, dev) {
- if (!cras_iodev_is_open(dev)) {
- /* For closed devices, just set its mute state. */
- cras_iodev_set_mute(dev);
- } else {
- audio_thread_dev_start_ramp(
- audio_thread, dev->info.idx,
- (should_mute ?
- CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE :
- CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE));
- }
- }
-}
-
-static void remove_all_streams_from_dev(struct cras_iodev *dev)
-{
- struct cras_rstream *rstream;
-
- audio_thread_rm_open_dev(audio_thread, dev->direction, dev->info.idx);
-
- DL_FOREACH (stream_list_get(stream_list), rstream) {
- if (rstream->apm_list == NULL)
- continue;
- cras_apm_list_remove_apm(rstream->apm_list, dev);
- }
-}
-
-/*
- * If output dev has an echo reference dev associated, add a server
- * stream to read audio data from it so APM can analyze.
- */
-static void possibly_enable_echo_reference(struct cras_iodev *dev)
-{
- if (dev->direction != CRAS_STREAM_OUTPUT)
- return;
-
- if (dev->echo_reference_dev == NULL)
- return;
-
- server_stream_create(stream_list, dev->echo_reference_dev->info.idx,
- dev->format);
-}
-
-/*
- * If output dev has an echo reference dev associated, check if there
- * is server stream opened for it and remove it.
- */
-static void possibly_disable_echo_reference(struct cras_iodev *dev)
-{
- if (dev->echo_reference_dev == NULL)
- return;
-
- server_stream_destroy(stream_list, dev->echo_reference_dev->info.idx);
-}
-
-/*
- * Removes all attached streams and close dev if it's opened.
- */
-static void close_dev(struct cras_iodev *dev)
-{
- if (!cras_iodev_is_open(dev))
- return;
-
- MAINLOG(main_log, MAIN_THREAD_DEV_CLOSE, dev->info.idx, 0, 0);
- remove_all_streams_from_dev(dev);
- dev->idle_timeout.tv_sec = 0;
- /* close echo ref first to avoid underrun in hardware */
- possibly_disable_echo_reference(dev);
- cras_iodev_close(dev);
-}
-
-static void idle_dev_check(struct cras_timer *timer, void *data)
-{
- struct enabled_dev *edev;
- struct timespec now;
- struct timespec min_idle_expiration;
- unsigned int num_idle_devs = 0;
- unsigned int min_idle_timeout_ms;
-
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
- min_idle_expiration.tv_sec = 0;
- min_idle_expiration.tv_nsec = 0;
-
- DL_FOREACH (enabled_devs[CRAS_STREAM_OUTPUT], edev) {
- if (edev->dev->idle_timeout.tv_sec == 0)
- continue;
- if (timespec_after(&now, &edev->dev->idle_timeout)) {
- close_dev(edev->dev);
- continue;
- }
- num_idle_devs++;
- if (min_idle_expiration.tv_sec == 0 ||
- timespec_after(&min_idle_expiration,
- &edev->dev->idle_timeout))
- min_idle_expiration = edev->dev->idle_timeout;
- }
-
- idle_timer = NULL;
- if (!num_idle_devs)
- return;
- if (timespec_after(&now, &min_idle_expiration)) {
- min_idle_timeout_ms = 0;
- } else {
- struct timespec timeout;
- subtract_timespecs(&min_idle_expiration, &now, &timeout);
- min_idle_timeout_ms = timespec_to_ms(&timeout);
- }
- /* Wake up when it is time to close the next idle device. Sleep for a
- * minimum of 10 milliseconds. */
- idle_timer = cras_tm_create_timer(cras_system_state_get_tm(),
- MAX(min_idle_timeout_ms, 10),
- idle_dev_check, NULL);
-}
-
-/*
- * Cancel pending init tries. Called at device initialization or when device
- * is disabled.
- */
-static void cancel_pending_init_retries(unsigned int dev_idx)
-{
- struct dev_init_retry *retry;
-
- DL_FOREACH (init_retries, retry) {
- if (retry->dev_idx != dev_idx)
- continue;
- cras_tm_cancel_timer(cras_system_state_get_tm(),
- retry->init_timer);
- DL_DELETE(init_retries, retry);
- free(retry);
- }
-}
-
-/* Open the device potentially filling the output with a pre buffer. */
-static int init_device(struct cras_iodev *dev, struct cras_rstream *rstream)
-{
- int rc;
-
- cras_iodev_exit_idle(dev);
-
- if (cras_iodev_is_open(dev))
- return 0;
- cancel_pending_init_retries(dev->info.idx);
- MAINLOG(main_log, MAIN_THREAD_DEV_INIT, dev->info.idx,
- rstream->format.num_channels, rstream->format.frame_rate);
-
- rc = cras_iodev_open(dev, rstream->cb_threshold, &rstream->format);
- if (rc)
- return rc;
-
- rc = audio_thread_add_open_dev(audio_thread, dev);
- if (rc)
- cras_iodev_close(dev);
-
- possibly_enable_echo_reference(dev);
-
- return rc;
-}
-
-static void suspend_devs()
-{
- struct enabled_dev *edev;
- struct cras_rstream *rstream;
-
- MAINLOG(main_log, MAIN_THREAD_SUSPEND_DEVS, 0, 0, 0);
-
- DL_FOREACH (stream_list_get(stream_list), rstream) {
- if (rstream->is_pinned) {
- struct cras_iodev *dev;
-
- /* Skip closing hotword stream in the first pass.
- * Closing an input device may resume hotword stream
- * with its post_close_iodev_hook so we should deal
- * with hotword stream in the second pass.
- */
- if ((rstream->flags & HOTWORD_STREAM) == HOTWORD_STREAM)
- continue;
-
- dev = find_dev(rstream->pinned_dev_idx);
- if (dev) {
- audio_thread_disconnect_stream(audio_thread,
- rstream, dev);
- if (!cras_iodev_list_dev_is_enabled(dev))
- close_dev(dev);
- }
- } else {
- audio_thread_disconnect_stream(audio_thread, rstream,
- NULL);
- }
- }
- stream_list_suspended = 1;
-
- DL_FOREACH (enabled_devs[CRAS_STREAM_OUTPUT], edev) {
- close_dev(edev->dev);
- }
- DL_FOREACH (enabled_devs[CRAS_STREAM_INPUT], edev) {
- close_dev(edev->dev);
- }
-
- /* Doing this check after all the other enabled iodevs are closed to
- * ensure preempted hotword streams obey the pause_at_suspend flag.
- */
- if (cras_system_get_hotword_pause_at_suspend()) {
- cras_iodev_list_suspend_hotword_streams();
- hotword_auto_resume = 1;
- }
-}
-
-static int stream_added_cb(struct cras_rstream *rstream);
-
-static void resume_devs()
-{
- struct enabled_dev *edev;
- struct cras_rstream *rstream;
-
- int has_output_stream = 0;
- stream_list_suspended = 0;
-
- MAINLOG(main_log, MAIN_THREAD_RESUME_DEVS, 0, 0, 0);
-
- /* Auto-resume based on the local flag in case the system state flag has
- * changed.
- */
- if (hotword_auto_resume) {
- cras_iodev_list_resume_hotword_stream();
- hotword_auto_resume = 0;
- }
-
- /*
- * To remove the short popped noise caused by applications that can not
- * stop playback "right away" after resume, we mute all output devices
- * for a short time if there is any output stream.
- */
- DL_FOREACH (stream_list_get(stream_list), rstream) {
- if (rstream->direction == CRAS_STREAM_OUTPUT)
- has_output_stream++;
- }
- if (has_output_stream) {
- DL_FOREACH (enabled_devs[CRAS_STREAM_OUTPUT], edev) {
- edev->dev->initial_ramp_request =
- CRAS_IODEV_RAMP_REQUEST_RESUME_MUTE;
- }
- }
-
- DL_FOREACH (stream_list_get(stream_list), rstream) {
- if ((rstream->flags & HOTWORD_STREAM) == HOTWORD_STREAM)
- continue;
- stream_added_cb(rstream);
- }
-}
-
-/* Called when the system audio is suspended or resumed. */
-void sys_suspend_change(void *arg, int suspended)
-{
- if (suspended)
- suspend_devs();
- else
- resume_devs();
-}
-
-/* Called when the system capture mute state changes. Pass the current capture
- * mute setting to the default input if it is active. */
-static void sys_cap_mute_change(void *context, int muted, int mute_locked)
-{
- struct cras_iodev *dev;
-
- DL_FOREACH (devs[CRAS_STREAM_INPUT].iodevs, dev) {
- if (dev->set_capture_mute && cras_iodev_is_open(dev))
- dev->set_capture_mute(dev);
- }
-}
-
-static int disable_device(struct enabled_dev *edev, bool force);
-static int enable_device(struct cras_iodev *dev);
-
-static void possibly_disable_fallback(enum CRAS_STREAM_DIRECTION dir)
-{
- struct enabled_dev *edev;
-
- DL_FOREACH (enabled_devs[dir], edev) {
- if (edev->dev == fallback_devs[dir])
- disable_device(edev, false);
- }
-}
-
-/*
- * Possibly enables fallback device to handle streams.
- * dir - output or input.
- * error - true if enable fallback device because no other iodevs can be
- * initialized successfully.
- */
-static void possibly_enable_fallback(enum CRAS_STREAM_DIRECTION dir, bool error)
-{
- if (fallback_devs[dir] == NULL)
- return;
-
- /*
- * The fallback device is a special device. It doesn't have a real
- * device to get a correct node type. Therefore, we need to set it by
- * ourselves, which indicates the reason to use this device.
- * NORMAL - Use it because of nodes changed.
- * ABNORMAL - Use it because there are no other usable devices.
- */
- if (error)
- syslog(LOG_ERR,
- "Enable fallback device because there are no other usable devices.");
-
- fallback_devs[dir]->active_node->type =
- error ? CRAS_NODE_TYPE_FALLBACK_ABNORMAL :
- CRAS_NODE_TYPE_FALLBACK_NORMAL;
- if (!cras_iodev_list_dev_is_enabled(fallback_devs[dir]))
- enable_device(fallback_devs[dir]);
-}
-
-/*
- * Adds stream to one or more open iodevs. If the stream has processing effect
- * turned on, create new APM instance and add to the list. This makes sure the
- * time consuming APM creation happens in main thread.
- */
-static int add_stream_to_open_devs(struct cras_rstream *stream,
- struct cras_iodev **iodevs,
- unsigned int num_iodevs)
-{
- int i;
- if (stream->apm_list) {
- for (i = 0; i < num_iodevs; i++)
- cras_apm_list_add_apm(stream->apm_list, iodevs[i],
- iodevs[i]->format,
- cras_iodev_is_aec_use_case(
- iodevs[i]->active_node));
- }
- return audio_thread_add_stream(audio_thread, stream, iodevs,
- num_iodevs);
-}
-
-static int init_and_attach_streams(struct cras_iodev *dev)
-{
- int rc;
- enum CRAS_STREAM_DIRECTION dir = dev->direction;
- struct cras_rstream *stream;
- int dev_enabled = cras_iodev_list_dev_is_enabled(dev);
-
- /* If called after suspend, for example bluetooth
- * profile switching, don't add back the stream list. */
- if (stream_list_suspended)
- return 0;
-
- /* If there are active streams to attach to this device,
- * open it. */
- DL_FOREACH (stream_list_get(stream_list), stream) {
- bool can_attach = 0;
-
- if (stream->direction != dir)
- continue;
- /*
- * For normal stream, if device is enabled by UI then it can
- * attach to this dev.
- */
- if (!stream->is_pinned) {
- can_attach = dev_enabled;
- }
- /*
- * If this is a pinned stream, attach it if its pinned dev id
- * matches this device or any fallback dev. Note that attaching
- * a pinned stream to fallback device is temporary. When the
- * fallback dev gets disabled in possibly_disable_fallback()
- * the check stream_list_has_pinned_stream() is key to allow
- * all streams to be removed from fallback and close it.
- */
- else if ((stream->pinned_dev_idx == dev->info.idx) ||
- (SILENT_PLAYBACK_DEVICE == dev->info.idx) ||
- (SILENT_RECORD_DEVICE == dev->info.idx)) {
- can_attach = 1;
- }
-
- if (!can_attach)
- continue;
-
- /*
- * Note that the stream list is descending ordered by channel
- * count, which guarantees the first attachable stream will have
- * the highest channel count.
- */
- rc = init_device(dev, stream);
- if (rc) {
- syslog(LOG_ERR, "Enable %s failed, rc = %d",
- dev->info.name, rc);
- return rc;
- }
- add_stream_to_open_devs(stream, &dev, 1);
- }
- return 0;
-}
-
-static void init_device_cb(struct cras_timer *timer, void *arg)
-{
- int rc;
- struct dev_init_retry *retry = (struct dev_init_retry *)arg;
- struct cras_iodev *dev = find_dev(retry->dev_idx);
-
- /*
- * First of all, remove retry record to avoid confusion to the
- * actual device init work.
- */
- DL_DELETE(init_retries, retry);
- free(retry);
-
- if (!dev || cras_iodev_is_open(dev))
- return;
-
- rc = init_and_attach_streams(dev);
- if (rc < 0)
- syslog(LOG_ERR, "Init device retry failed");
- else
- possibly_disable_fallback(dev->direction);
-}
-
-static int schedule_init_device_retry(struct cras_iodev *dev)
-{
- struct dev_init_retry *retry;
- struct cras_tm *tm = cras_system_state_get_tm();
-
- retry = (struct dev_init_retry *)calloc(1, sizeof(*retry));
- if (!retry)
- return -ENOMEM;
-
- retry->dev_idx = dev->info.idx;
- retry->init_timer = cras_tm_create_timer(tm, INIT_DEV_DELAY_MS,
- init_device_cb, retry);
- DL_APPEND(init_retries, retry);
- return 0;
-}
-
-static int init_pinned_device(struct cras_iodev *dev,
- struct cras_rstream *rstream)
-{
- int rc;
-
- cras_iodev_exit_idle(dev);
-
- if (audio_thread_is_dev_open(audio_thread, dev))
- return 0;
-
- /* Make sure the active node is configured properly, it could be
- * disabled when last normal stream removed. */
- dev->update_active_node(dev, dev->active_node->idx, 1);
-
- /* Negative EAGAIN code indicates dev will be opened later. */
- rc = init_device(dev, rstream);
- if (rc)
- return rc;
- return 0;
-}
-
-/*
- * Close device enabled by pinned stream. Since it's NOT in the enabled
- * dev list, make sure update_active_node() is called to correctly
- * configure the ALSA UCM or BT profile state.
- */
-static int close_pinned_device(struct cras_iodev *dev)
-{
- close_dev(dev);
- dev->update_active_node(dev, dev->active_node->idx, 0);
- return 0;
-}
-
-static struct cras_iodev *find_pinned_device(struct cras_rstream *rstream)
-{
- struct cras_iodev *dev;
- if (!rstream->is_pinned)
- return NULL;
-
- dev = find_dev(rstream->pinned_dev_idx);
-
- if ((rstream->flags & HOTWORD_STREAM) != HOTWORD_STREAM)
- return dev;
-
- /* Double check node type for hotword stream */
- if (dev && dev->active_node->type != CRAS_NODE_TYPE_HOTWORD) {
- syslog(LOG_ERR, "Hotword stream pinned to invalid dev %u",
- dev->info.idx);
- return NULL;
- }
-
- return hotword_suspended ? empty_hotword_dev : dev;
-}
-
-static int pinned_stream_added(struct cras_rstream *rstream)
-{
- struct cras_iodev *dev;
- int rc;
-
- /* Check that the target device is valid for pinned streams. */
- dev = find_pinned_device(rstream);
- if (!dev)
- return -EINVAL;
-
- rc = init_pinned_device(dev, rstream);
- if (rc) {
- syslog(LOG_INFO, "init_pinned_device failed, rc %d", rc);
- return schedule_init_device_retry(dev);
- }
-
- return add_stream_to_open_devs(rstream, &dev, 1);
-}
-
-static int stream_added_cb(struct cras_rstream *rstream)
-{
- struct enabled_dev *edev;
- struct cras_iodev *iodevs[10];
- unsigned int num_iodevs;
- int rc;
- bool iodev_reopened;
-
- if (stream_list_suspended)
- return 0;
-
- MAINLOG(main_log, MAIN_THREAD_STREAM_ADDED, rstream->stream_id,
- rstream->direction, rstream->buffer_frames);
-
- if (rstream->is_pinned)
- return pinned_stream_added(rstream);
-
- /* Add the new stream to all enabled iodevs at once to avoid offset
- * in shm level between different ouput iodevs. */
- num_iodevs = 0;
- iodev_reopened = false;
- DL_FOREACH (enabled_devs[rstream->direction], edev) {
- if (num_iodevs >= ARRAY_SIZE(iodevs)) {
- syslog(LOG_ERR, "too many enabled devices");
- break;
- }
-
- if (cras_iodev_is_open(edev->dev) &&
- (rstream->format.num_channels >
- edev->dev->format->num_channels) &&
- (rstream->format.num_channels <=
- edev->dev->info.max_supported_channels)) {
- /* Re-open the device with the format of the attached
- * stream if it has higher channel count than the
- * current format of the device, and doesn't exceed the
- * max_supported_channels of the device.
- * Fallback device will be transciently enabled during
- * the device re-opening.
- */
- MAINLOG(main_log, MAIN_THREAD_DEV_REOPEN,
- rstream->format.num_channels,
- edev->dev->format->num_channels,
- edev->dev->format->frame_rate);
- syslog(LOG_INFO, "re-open %s for higher channel count",
- edev->dev->info.name);
- possibly_enable_fallback(rstream->direction, false);
- cras_iodev_list_suspend_dev(edev->dev->info.idx);
- cras_iodev_list_resume_dev(edev->dev->info.idx);
- possibly_disable_fallback(rstream->direction);
- iodev_reopened = true;
- } else {
- rc = init_device(edev->dev, rstream);
- if (rc) {
- /* Error log but don't return error here, because
- * stopping audio could block video playback.
- */
- syslog(LOG_ERR, "Init %s failed, rc = %d",
- edev->dev->info.name, rc);
- schedule_init_device_retry(edev->dev);
- continue;
- }
-
- iodevs[num_iodevs++] = edev->dev;
- }
- }
- if (num_iodevs) {
- rc = add_stream_to_open_devs(rstream, iodevs, num_iodevs);
- if (rc) {
- syslog(LOG_ERR, "adding stream to thread fail");
- return rc;
- }
- } else if (!iodev_reopened) {
- /* Enable fallback device if no other iodevs can be initialized
- * or re-opened successfully.
- * For error codes like EAGAIN and ENOENT, a new iodev will be
- * enabled soon so streams are going to route there. As for the
- * rest of the error cases, silence will be played or recorded
- * so client won't be blocked.
- * The enabled fallback device will be disabled when
- * cras_iodev_list_select_node() is called to re-select the
- * active node.
- */
- possibly_enable_fallback(rstream->direction, true);
- }
- return 0;
-}
-
-static int possibly_close_enabled_devs(enum CRAS_STREAM_DIRECTION dir)
-{
- struct enabled_dev *edev;
- const struct cras_rstream *s;
-
- /* Check if there are still default streams attached. */
- DL_FOREACH (stream_list_get(stream_list), s) {
- if (s->direction == dir && !s->is_pinned)
- return 0;
- }
-
- /* No more default streams, close any device that doesn't have a stream
- * pinned to it. */
- DL_FOREACH (enabled_devs[dir], edev) {
- if (stream_list_has_pinned_stream(stream_list,
- edev->dev->info.idx))
- continue;
- if (dir == CRAS_STREAM_INPUT) {
- close_dev(edev->dev);
- continue;
- }
- /* Allow output devs to drain before closing. */
- clock_gettime(CLOCK_MONOTONIC_RAW, &edev->dev->idle_timeout);
- add_timespecs(&edev->dev->idle_timeout, &idle_timeout_interval);
- idle_dev_check(NULL, NULL);
- }
-
- return 0;
-}
-
-static void pinned_stream_removed(struct cras_rstream *rstream)
-{
- struct cras_iodev *dev;
-
- dev = find_pinned_device(rstream);
- if (!dev)
- return;
- if (!cras_iodev_list_dev_is_enabled(dev) &&
- !stream_list_has_pinned_stream(stream_list, dev->info.idx))
- close_pinned_device(dev);
-}
-
-/* Returns the number of milliseconds left to drain this stream. This is passed
- * directly from the audio thread. */
-static int stream_removed_cb(struct cras_rstream *rstream)
-{
- enum CRAS_STREAM_DIRECTION direction = rstream->direction;
- int rc;
-
- rc = audio_thread_drain_stream(audio_thread, rstream);
- if (rc)
- return rc;
-
- MAINLOG(main_log, MAIN_THREAD_STREAM_REMOVED, rstream->stream_id, 0, 0);
-
- if (rstream->is_pinned)
- pinned_stream_removed(rstream);
-
- possibly_close_enabled_devs(direction);
-
- return 0;
-}
-
-static int enable_device(struct cras_iodev *dev)
-{
- int rc;
- struct enabled_dev *edev;
- enum CRAS_STREAM_DIRECTION dir = dev->direction;
- struct device_enabled_cb *callback;
-
- DL_FOREACH (enabled_devs[dir], edev) {
- if (edev->dev == dev)
- return -EEXIST;
- }
-
- edev = calloc(1, sizeof(*edev));
- edev->dev = dev;
- DL_APPEND(enabled_devs[dir], edev);
- dev->is_enabled = 1;
-
- rc = init_and_attach_streams(dev);
- if (rc < 0) {
- syslog(LOG_INFO, "Enable device fail, rc %d", rc);
- schedule_init_device_retry(dev);
- return rc;
- }
-
- DL_FOREACH (device_enable_cbs, callback)
- callback->enabled_cb(dev, callback->cb_data);
-
- return 0;
-}
-
-/* Set `force to true to flush any pinned streams before closing the device. */
-static int disable_device(struct enabled_dev *edev, bool force)
-{
- struct cras_iodev *dev = edev->dev;
- enum CRAS_STREAM_DIRECTION dir = dev->direction;
- struct cras_rstream *stream;
- struct device_enabled_cb *callback;
-
- MAINLOG(main_log, MAIN_THREAD_DEV_DISABLE, dev->info.idx, force, 0);
- /*
- * Remove from enabled dev list. However this dev could have a stream
- * pinned to it, only cancel pending init timers when force flag is set.
- */
- DL_DELETE(enabled_devs[dir], edev);
- free(edev);
- dev->is_enabled = 0;
- if (force) {
- cancel_pending_init_retries(dev->info.idx);
- }
- /* If there's a pinned stream exists, simply disconnect all the normal
- * streams off this device and return. */
- else if (stream_list_has_pinned_stream(stream_list, dev->info.idx)) {
- DL_FOREACH (stream_list_get(stream_list), stream) {
- if (stream->direction != dev->direction)
- continue;
- if (stream->is_pinned)
- continue;
- audio_thread_disconnect_stream(audio_thread, stream,
- dev);
- }
- return 0;
- }
-
- DL_FOREACH (device_enable_cbs, callback)
- callback->disabled_cb(dev, callback->cb_data);
- close_dev(dev);
- dev->update_active_node(dev, dev->active_node->idx, 0);
-
- return 0;
-}
-
-/*
- * Exported Interface.
- */
-
-void cras_iodev_list_init()
-{
- struct cras_observer_ops observer_ops;
-
- memset(&observer_ops, 0, sizeof(observer_ops));
- observer_ops.output_volume_changed = sys_vol_change;
- observer_ops.output_mute_changed = sys_mute_change;
- observer_ops.capture_mute_changed = sys_cap_mute_change;
- observer_ops.suspend_changed = sys_suspend_change;
- list_observer = cras_observer_add(&observer_ops, NULL);
- idle_timer = NULL;
-
- main_log = main_thread_event_log_init();
-
- /* Create the audio stream list for the system. */
- stream_list =
- stream_list_create(stream_added_cb, stream_removed_cb,
- cras_rstream_create, cras_rstream_destroy,
- cras_system_state_get_tm());
-
- /* Add an empty device so there is always something to play to or
- * capture from. */
- fallback_devs[CRAS_STREAM_OUTPUT] = empty_iodev_create(
- CRAS_STREAM_OUTPUT, CRAS_NODE_TYPE_FALLBACK_NORMAL);
- fallback_devs[CRAS_STREAM_INPUT] = empty_iodev_create(
- CRAS_STREAM_INPUT, CRAS_NODE_TYPE_FALLBACK_NORMAL);
- enable_device(fallback_devs[CRAS_STREAM_OUTPUT]);
- enable_device(fallback_devs[CRAS_STREAM_INPUT]);
-
- empty_hotword_dev =
- empty_iodev_create(CRAS_STREAM_INPUT, CRAS_NODE_TYPE_HOTWORD);
-
- /* Create loopback devices. */
- loopdev_post_mix = loopback_iodev_create(LOOPBACK_POST_MIX_PRE_DSP);
- loopdev_post_dsp = loopback_iodev_create(LOOPBACK_POST_DSP);
-
- audio_thread = audio_thread_create();
- if (!audio_thread) {
- syslog(LOG_ERR, "Fatal: audio thread init");
- exit(-ENOMEM);
- }
- audio_thread_start(audio_thread);
-
- cras_iodev_list_update_device_list();
-}
-
-void cras_iodev_list_deinit()
-{
- audio_thread_destroy(audio_thread);
- loopback_iodev_destroy(loopdev_post_dsp);
- loopback_iodev_destroy(loopdev_post_mix);
- empty_iodev_destroy(empty_hotword_dev);
- empty_iodev_destroy(fallback_devs[CRAS_STREAM_INPUT]);
- empty_iodev_destroy(fallback_devs[CRAS_STREAM_OUTPUT]);
- stream_list_destroy(stream_list);
- main_thread_event_log_deinit(main_log);
- if (list_observer) {
- cras_observer_remove(list_observer);
- list_observer = NULL;
- }
-}
-
-int cras_iodev_list_dev_is_enabled(const struct cras_iodev *dev)
-{
- struct enabled_dev *edev;
-
- DL_FOREACH (enabled_devs[dev->direction], edev) {
- if (edev->dev == dev)
- return 1;
- }
-
- return 0;
-}
-
-void cras_iodev_list_enable_dev(struct cras_iodev *dev)
-{
- possibly_disable_fallback(dev->direction);
- /* Enable ucm setting of active node. */
- dev->update_active_node(dev, dev->active_node->idx, 1);
- enable_device(dev);
- cras_iodev_list_notify_active_node_changed(dev->direction);
-}
-
-void cras_iodev_list_add_active_node(enum CRAS_STREAM_DIRECTION dir,
- cras_node_id_t node_id)
-{
- struct cras_iodev *new_dev;
- new_dev = find_dev(dev_index_of(node_id));
- if (!new_dev || new_dev->direction != dir)
- return;
-
- MAINLOG(main_log, MAIN_THREAD_ADD_ACTIVE_NODE, new_dev->info.idx, 0, 0);
-
- /* If the new dev is already enabled but its active node needs to be
- * changed. Disable new dev first, update active node, and then
- * re-enable it again.
- */
- if (cras_iodev_list_dev_is_enabled(new_dev)) {
- if (node_index_of(node_id) == new_dev->active_node->idx)
- return;
- else
- cras_iodev_list_disable_dev(new_dev, true);
- }
-
- new_dev->update_active_node(new_dev, node_index_of(node_id), 1);
- cras_iodev_list_enable_dev(new_dev);
-}
-
-/*
- * Disables device which may or may not be in enabled_devs list.
- */
-void cras_iodev_list_disable_dev(struct cras_iodev *dev, bool force_close)
-{
- struct enabled_dev *edev, *edev_to_disable = NULL;
-
- int is_the_only_enabled_device = 1;
-
- DL_FOREACH (enabled_devs[dev->direction], edev) {
- if (edev->dev == dev)
- edev_to_disable = edev;
- else
- is_the_only_enabled_device = 0;
- }
-
- /*
- * Disables the device for these two cases:
- * 1. Disable a device in the enabled_devs list.
- * 2. Force close a device that is not in the enabled_devs list,
- * but it is running a pinned stream.
- */
- if (!edev_to_disable) {
- if (force_close)
- close_pinned_device(dev);
- return;
- }
-
- /* If the device to be closed is the only enabled device, we should
- * enable the fallback device first then disable the target
- * device. */
- if (is_the_only_enabled_device && fallback_devs[dev->direction])
- enable_device(fallback_devs[dev->direction]);
-
- disable_device(edev_to_disable, force_close);
-
- cras_iodev_list_notify_active_node_changed(dev->direction);
- return;
-}
-
-void cras_iodev_list_suspend_dev(unsigned int dev_idx)
-{
- struct cras_iodev *dev = find_dev(dev_idx);
-
- if (!dev)
- return;
-
- /* Remove all streams including the pinned streams, and close
- * this iodev. */
- close_dev(dev);
- dev->update_active_node(dev, dev->active_node->idx, 0);
-}
-
-void cras_iodev_list_resume_dev(unsigned int dev_idx)
-{
- struct cras_iodev *dev = find_dev(dev_idx);
- int rc;
-
- if (!dev)
- return;
-
- dev->update_active_node(dev, dev->active_node->idx, 1);
- rc = init_and_attach_streams(dev);
- if (rc == 0) {
- /* If dev initialize succeeded and this is not a pinned device,
- * disable the silent fallback device because it's just
- * unnecessary. */
- if (!stream_list_has_pinned_stream(stream_list, dev_idx))
- possibly_disable_fallback(dev->direction);
- } else {
- syslog(LOG_INFO, "Enable dev fail at resume, rc %d", rc);
- schedule_init_device_retry(dev);
- }
-}
-
-void cras_iodev_list_set_dev_mute(unsigned int dev_idx)
-{
- struct cras_iodev *dev;
-
- dev = find_dev(dev_idx);
- if (!dev)
- return;
-
- cras_iodev_set_mute(dev);
-}
-
-void cras_iodev_list_rm_active_node(enum CRAS_STREAM_DIRECTION dir,
- cras_node_id_t node_id)
-{
- struct cras_iodev *dev;
-
- dev = find_dev(dev_index_of(node_id));
- if (!dev)
- return;
-
- cras_iodev_list_disable_dev(dev, false);
-}
-
-int cras_iodev_list_add_output(struct cras_iodev *output)
-{
- int rc;
-
- if (output->direction != CRAS_STREAM_OUTPUT)
- return -EINVAL;
-
- rc = add_dev_to_list(output);
- if (rc)
- return rc;
-
- MAINLOG(main_log, MAIN_THREAD_ADD_TO_DEV_LIST, output->info.idx,
- CRAS_STREAM_OUTPUT, 0);
- return 0;
-}
-
-int cras_iodev_list_add_input(struct cras_iodev *input)
-{
- int rc;
-
- if (input->direction != CRAS_STREAM_INPUT)
- return -EINVAL;
-
- rc = add_dev_to_list(input);
- if (rc)
- return rc;
-
- MAINLOG(main_log, MAIN_THREAD_ADD_TO_DEV_LIST, input->info.idx,
- CRAS_STREAM_INPUT, 0);
- return 0;
-}
-
-int cras_iodev_list_rm_output(struct cras_iodev *dev)
-{
- int res;
-
- /* Retire the current active output device before removing it from
- * list, otherwise it could be busy and remain in the list.
- */
- cras_iodev_list_disable_dev(dev, true);
- res = rm_dev_from_list(dev);
- if (res == 0)
- cras_iodev_list_update_device_list();
- return res;
-}
-
-int cras_iodev_list_rm_input(struct cras_iodev *dev)
-{
- int res;
-
- /* Retire the current active input device before removing it from
- * list, otherwise it could be busy and remain in the list.
- */
- cras_iodev_list_disable_dev(dev, true);
- res = rm_dev_from_list(dev);
- if (res == 0)
- cras_iodev_list_update_device_list();
- return res;
-}
-
-int cras_iodev_list_get_outputs(struct cras_iodev_info **list_out)
-{
- return get_dev_list(&devs[CRAS_STREAM_OUTPUT], list_out);
-}
-
-int cras_iodev_list_get_inputs(struct cras_iodev_info **list_out)
-{
- return get_dev_list(&devs[CRAS_STREAM_INPUT], list_out);
-}
-
-struct cras_iodev *
-cras_iodev_list_get_first_enabled_iodev(enum CRAS_STREAM_DIRECTION direction)
-{
- struct enabled_dev *edev = enabled_devs[direction];
-
- return edev ? edev->dev : NULL;
-}
-
-struct cras_iodev *
-cras_iodev_list_get_sco_pcm_iodev(enum CRAS_STREAM_DIRECTION direction)
-{
- struct cras_iodev *dev;
- struct cras_ionode *node;
-
- DL_FOREACH (devs[direction].iodevs, dev) {
- DL_FOREACH (dev->nodes, node) {
- if (node->is_sco_pcm)
- return dev;
- }
- }
-
- return NULL;
-}
-
-cras_node_id_t
-cras_iodev_list_get_active_node_id(enum CRAS_STREAM_DIRECTION direction)
-{
- struct enabled_dev *edev = enabled_devs[direction];
-
- if (!edev || !edev->dev || !edev->dev->active_node)
- return 0;
-
- return cras_make_node_id(edev->dev->info.idx,
- edev->dev->active_node->idx);
-}
-
-void cras_iodev_list_update_device_list()
-{
- struct cras_server_state *state;
-
- state = cras_system_state_update_begin();
- if (!state)
- return;
-
- state->num_output_devs = devs[CRAS_STREAM_OUTPUT].size;
- state->num_input_devs = devs[CRAS_STREAM_INPUT].size;
- fill_dev_list(&devs[CRAS_STREAM_OUTPUT], &state->output_devs[0],
- CRAS_MAX_IODEVS);
- fill_dev_list(&devs[CRAS_STREAM_INPUT], &state->input_devs[0],
- CRAS_MAX_IODEVS);
-
- state->num_output_nodes =
- fill_node_list(&devs[CRAS_STREAM_OUTPUT],
- &state->output_nodes[0], CRAS_MAX_IONODES);
- state->num_input_nodes =
- fill_node_list(&devs[CRAS_STREAM_INPUT], &state->input_nodes[0],
- CRAS_MAX_IONODES);
-
- cras_system_state_update_complete();
-}
-
-/* Look up the first hotword stream and the device it pins to. */
-int find_hotword_stream_dev(struct cras_iodev **dev,
- struct cras_rstream **stream)
-{
- DL_FOREACH (stream_list_get(stream_list), *stream) {
- if (((*stream)->flags & HOTWORD_STREAM) != HOTWORD_STREAM)
- continue;
-
- *dev = find_dev((*stream)->pinned_dev_idx);
- if (*dev == NULL)
- return -ENOENT;
- break;
- }
- return 0;
-}
-
-/* Suspend/resume hotword streams functions are used to provide seamless
- * experience to cras clients when there's hardware limitation about concurrent
- * DSP and normal recording. The empty hotword iodev is used to hold all
- * hotword streams during suspend, so client side will not know about the
- * transition, and can still remove or add streams. At resume, the real hotword
- * device will be initialized and opened again to re-arm the DSP.
- */
-int cras_iodev_list_suspend_hotword_streams()
-{
- struct cras_iodev *hotword_dev;
- struct cras_rstream *stream = NULL;
- int rc;
-
- rc = find_hotword_stream_dev(&hotword_dev, &stream);
- if (rc)
- return rc;
-
- if (stream == NULL) {
- hotword_suspended = 1;
- return 0;
- }
- /* Move all existing hotword streams to the empty hotword iodev. */
- init_pinned_device(empty_hotword_dev, stream);
- DL_FOREACH (stream_list_get(stream_list), stream) {
- if ((stream->flags & HOTWORD_STREAM) != HOTWORD_STREAM)
- continue;
- if (stream->pinned_dev_idx != hotword_dev->info.idx) {
- syslog(LOG_ERR,
- "Failed to suspend hotword stream on dev %u",
- stream->pinned_dev_idx);
- continue;
- }
-
- audio_thread_disconnect_stream(audio_thread, stream,
- hotword_dev);
- audio_thread_add_stream(audio_thread, stream,
- &empty_hotword_dev, 1);
- }
- close_pinned_device(hotword_dev);
- hotword_suspended = 1;
- return 0;
-}
-
-int cras_iodev_list_resume_hotword_stream()
-{
- struct cras_iodev *hotword_dev;
- struct cras_rstream *stream = NULL;
- int rc;
-
- rc = find_hotword_stream_dev(&hotword_dev, &stream);
- if (rc)
- return rc;
-
- if (stream == NULL) {
- hotword_suspended = 0;
- return 0;
- }
- /* Move all existing hotword streams to the real hotword iodev. */
- init_pinned_device(hotword_dev, stream);
- DL_FOREACH (stream_list_get(stream_list), stream) {
- if ((stream->flags & HOTWORD_STREAM) != HOTWORD_STREAM)
- continue;
- if (stream->pinned_dev_idx != hotword_dev->info.idx) {
- syslog(LOG_ERR,
- "Fail to resume hotword stream on dev %u",
- stream->pinned_dev_idx);
- continue;
- }
-
- audio_thread_disconnect_stream(audio_thread, stream,
- empty_hotword_dev);
- audio_thread_add_stream(audio_thread, stream, &hotword_dev, 1);
- }
- close_pinned_device(empty_hotword_dev);
- hotword_suspended = 0;
- return 0;
-}
-
-char *cras_iodev_list_get_hotword_models(cras_node_id_t node_id)
-{
- struct cras_iodev *dev = NULL;
-
- dev = find_dev(dev_index_of(node_id));
- if (!dev || !dev->get_hotword_models ||
- (dev->active_node->type != CRAS_NODE_TYPE_HOTWORD))
- return NULL;
-
- return dev->get_hotword_models(dev);
-}
-
-int cras_iodev_list_set_hotword_model(cras_node_id_t node_id,
- const char *model_name)
-{
- int ret;
- struct cras_iodev *dev = find_dev(dev_index_of(node_id));
- if (!dev || !dev->get_hotword_models ||
- (dev->active_node->type != CRAS_NODE_TYPE_HOTWORD))
- return -EINVAL;
-
- ret = dev->set_hotword_model(dev, model_name);
- if (!ret)
- strncpy(dev->active_node->active_hotword_model, model_name,
- sizeof(dev->active_node->active_hotword_model) - 1);
- return ret;
-}
-
-void cras_iodev_list_notify_nodes_changed()
-{
- cras_observer_notify_nodes();
-}
-
-void cras_iodev_list_notify_active_node_changed(
- enum CRAS_STREAM_DIRECTION direction)
-{
- cras_observer_notify_active_node(
- direction, cras_iodev_list_get_active_node_id(direction));
-}
-
-void cras_iodev_list_select_node(enum CRAS_STREAM_DIRECTION direction,
- cras_node_id_t node_id)
-{
- struct cras_iodev *new_dev = NULL;
- struct enabled_dev *edev;
- int new_node_already_enabled = 0;
- struct cras_rstream *rstream;
- int has_output_stream = 0;
- int rc;
-
- /* find the devices for the id. */
- new_dev = find_dev(dev_index_of(node_id));
-
- MAINLOG(main_log, MAIN_THREAD_SELECT_NODE, dev_index_of(node_id), 0, 0);
-
- /* Do nothing if the direction is mismatched. The new_dev == NULL case
- could happen if node_id is 0 (no selection), or the client tries
- to select a non-existing node (maybe it's unplugged just before
- the client selects it). We will just behave like there is no selected
- node. */
- if (new_dev && new_dev->direction != direction)
- return;
-
- /* Determine whether the new device and node are already enabled - if
- * they are, the selection algorithm should avoid disabling the new
- * device. */
- DL_FOREACH (enabled_devs[direction], edev) {
- if (edev->dev == new_dev &&
- edev->dev->active_node->idx == node_index_of(node_id)) {
- new_node_already_enabled = 1;
- break;
- }
- }
-
- /* Enable fallback device during the transition so client will not be
- * blocked in this duration, which is as long as 300 ms on some boards
- * before new device is opened.
- * Note that the fallback node is not needed if the new node is already
- * enabled - the new node will remain enabled. */
- if (!new_node_already_enabled)
- possibly_enable_fallback(direction, false);
-
- DL_FOREACH (enabled_devs[direction], edev) {
- /* Don't disable fallback devices. */
- if (edev->dev == fallback_devs[direction])
- continue;
- /*
- * Disable enabled device if it's not the new one, use non-force
- * disable call so we don't interrupt existing pinned streams on
- * it.
- */
- if (edev->dev != new_dev) {
- disable_device(edev, false);
- }
- /*
- * Otherwise if this happens to be the new device but about to
- * select to a different node (on the same dev). Force disable
- * this device to avoid any pinned stream occupies it in audio
- * thread and cause problem in later update_active_node call.
- */
- else if (!new_node_already_enabled) {
- disable_device(edev, true);
- }
- }
-
- if (new_dev && !new_node_already_enabled) {
- new_dev->update_active_node(new_dev, node_index_of(node_id), 1);
-
- /* To reduce the popped noise of active device change, mute
- * new_dev's for RAMP_SWITCH_MUTE_DURATION_SECS s.
- */
- DL_FOREACH (stream_list_get(stream_list), rstream) {
- if (rstream->direction == CRAS_STREAM_OUTPUT)
- has_output_stream++;
- }
- if (direction == CRAS_STREAM_OUTPUT && has_output_stream) {
- new_dev->initial_ramp_request =
- CRAS_IODEV_RAMP_REQUEST_SWITCH_MUTE;
- }
-
- rc = enable_device(new_dev);
- if (rc == 0) {
- /* Disable fallback device after new device is enabled.
- * Leave the fallback device enabled if new_dev failed
- * to open, or the new_dev == NULL case. */
- possibly_disable_fallback(direction);
- }
- }
-
- cras_iodev_list_notify_active_node_changed(direction);
-}
-
-static int set_node_plugged(struct cras_iodev *iodev, unsigned int node_idx,
- int plugged)
-{
- struct cras_ionode *node;
-
- node = find_node(iodev, node_idx);
- if (!node)
- return -EINVAL;
- cras_iodev_set_node_plugged(node, plugged);
- return 0;
-}
-
-static int set_node_volume(struct cras_iodev *iodev, unsigned int node_idx,
- int volume)
-{
- struct cras_ionode *node;
-
- node = find_node(iodev, node_idx);
- if (!node)
- return -EINVAL;
-
- if (iodev->ramp && cras_iodev_software_volume_needed(iodev) &&
- !cras_system_get_mute())
- cras_iodev_start_volume_ramp(iodev, node->volume, volume);
-
- node->volume = volume;
- if (iodev->set_volume)
- iodev->set_volume(iodev);
- cras_iodev_list_notify_node_volume(node);
- MAINLOG(main_log, MAIN_THREAD_OUTPUT_NODE_VOLUME, iodev->info.idx,
- volume, 0);
- return 0;
-}
-
-static int set_node_capture_gain(struct cras_iodev *iodev,
- unsigned int node_idx, int value)
-{
- struct cras_ionode *node;
- int db_scale;
-
- node = find_node(iodev, node_idx);
- if (!node)
- return -EINVAL;
-
- /* Assert value in range 0 - 100. */
- if (value < 0)
- value = 0;
- if (value > 100)
- value = 100;
-
- /* Linear maps (0, 50) to (-4000, 0) and (50, 100) to (0, 2000) dBFS.
- * Calculate and store corresponding scaler in ui_gain_scaler. */
- db_scale = (value > 50) ? 40 : 80;
- node->ui_gain_scaler =
- convert_softvol_scaler_from_dB((value - 50) * db_scale);
-
- if (iodev->set_capture_gain)
- iodev->set_capture_gain(iodev);
- cras_iodev_list_notify_node_capture_gain(node);
- MAINLOG(main_log, MAIN_THREAD_INPUT_NODE_GAIN, iodev->info.idx, value,
- 0);
- return 0;
-}
-
-static int set_node_left_right_swapped(struct cras_iodev *iodev,
- unsigned int node_idx,
- int left_right_swapped)
-{
- struct cras_ionode *node;
- int rc;
-
- if (!iodev->set_swap_mode_for_node)
- return -EINVAL;
- node = find_node(iodev, node_idx);
- if (!node)
- return -EINVAL;
-
- rc = iodev->set_swap_mode_for_node(iodev, node, left_right_swapped);
- if (rc) {
- syslog(LOG_ERR, "Failed to set swap mode on node %s to %d",
- node->name, left_right_swapped);
- return rc;
- }
- node->left_right_swapped = left_right_swapped;
- cras_iodev_list_notify_node_left_right_swapped(node);
- return 0;
-}
-
-int cras_iodev_list_set_node_attr(cras_node_id_t node_id, enum ionode_attr attr,
- int value)
-{
- struct cras_iodev *iodev;
- int rc = 0;
-
- iodev = find_dev(dev_index_of(node_id));
- if (!iodev)
- return -EINVAL;
-
- switch (attr) {
- case IONODE_ATTR_PLUGGED:
- rc = set_node_plugged(iodev, node_index_of(node_id), value);
- break;
- case IONODE_ATTR_VOLUME:
- rc = set_node_volume(iodev, node_index_of(node_id), value);
- break;
- case IONODE_ATTR_CAPTURE_GAIN:
- rc = set_node_capture_gain(iodev, node_index_of(node_id),
- value);
- break;
- case IONODE_ATTR_SWAP_LEFT_RIGHT:
- rc = set_node_left_right_swapped(iodev, node_index_of(node_id),
- value);
- break;
- default:
- return -EINVAL;
- }
-
- return rc;
-}
-
-void cras_iodev_list_notify_node_volume(struct cras_ionode *node)
-{
- cras_node_id_t id = cras_make_node_id(node->dev->info.idx, node->idx);
- cras_iodev_list_update_device_list();
- cras_observer_notify_output_node_volume(id, node->volume);
-}
-
-void cras_iodev_list_notify_node_left_right_swapped(struct cras_ionode *node)
-{
- cras_node_id_t id = cras_make_node_id(node->dev->info.idx, node->idx);
- cras_iodev_list_update_device_list();
- cras_observer_notify_node_left_right_swapped(id,
- node->left_right_swapped);
-}
-
-void cras_iodev_list_notify_node_capture_gain(struct cras_ionode *node)
-{
- cras_node_id_t id = cras_make_node_id(node->dev->info.idx, node->idx);
- cras_iodev_list_update_device_list();
- cras_observer_notify_input_node_gain(id, node->capture_gain);
-}
-
-void cras_iodev_list_add_test_dev(enum TEST_IODEV_TYPE type)
-{
- if (type != TEST_IODEV_HOTWORD)
- return;
- test_iodev_create(CRAS_STREAM_INPUT, type);
-}
-
-void cras_iodev_list_test_dev_command(unsigned int iodev_idx,
- enum CRAS_TEST_IODEV_CMD command,
- unsigned int data_len,
- const uint8_t *data)
-{
- struct cras_iodev *dev = find_dev(iodev_idx);
-
- if (!dev)
- return;
-
- test_iodev_command(dev, command, data_len, data);
-}
-
-struct audio_thread *cras_iodev_list_get_audio_thread()
-{
- return audio_thread;
-}
-
-struct stream_list *cras_iodev_list_get_stream_list()
-{
- return stream_list;
-}
-
-int cras_iodev_list_set_device_enabled_callback(
- device_enabled_callback_t enabled_cb,
- device_disabled_callback_t disabled_cb, void *cb_data)
-{
- struct device_enabled_cb *callback;
-
- DL_FOREACH (device_enable_cbs, callback) {
- if (callback->cb_data != cb_data)
- continue;
-
- DL_DELETE(device_enable_cbs, callback);
- free(callback);
- }
-
- if (enabled_cb && disabled_cb) {
- callback = (struct device_enabled_cb *)calloc(
- 1, sizeof(*callback));
- callback->enabled_cb = enabled_cb;
- callback->disabled_cb = disabled_cb;
- callback->cb_data = cb_data;
- DL_APPEND(device_enable_cbs, callback);
- }
-
- return 0;
-}
-
-void cras_iodev_list_register_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,
- unsigned int output_dev_idx,
- loopback_hook_data_t hook_data,
- loopback_hook_control_t hook_control,
- unsigned int loopback_dev_idx)
-{
- struct cras_iodev *iodev = find_dev(output_dev_idx);
- struct cras_iodev *loopback_dev;
- struct cras_loopback *loopback;
- bool dev_open;
-
- if (iodev == NULL) {
- syslog(LOG_ERR, "Output dev %u not found for loopback",
- output_dev_idx);
- return;
- }
-
- loopback_dev = find_dev(loopback_dev_idx);
- if (loopback_dev == NULL) {
- syslog(LOG_ERR, "Loopback dev %u not found", loopback_dev_idx);
- return;
- }
-
- dev_open = cras_iodev_is_open(iodev);
-
- loopback = (struct cras_loopback *)calloc(1, sizeof(*loopback));
- if (NULL == loopback) {
- syslog(LOG_ERR, "Not enough memory for loopback");
- return;
- }
-
- loopback->type = loopback_type;
- loopback->hook_data = hook_data;
- loopback->hook_control = hook_control;
- loopback->cb_data = loopback_dev;
- if (loopback->hook_control && dev_open)
- loopback->hook_control(true, loopback->cb_data);
-
- DL_APPEND(iodev->loopbacks, loopback);
-}
-
-void cras_iodev_list_unregister_loopback(enum CRAS_LOOPBACK_TYPE type,
- unsigned int output_dev_idx,
- unsigned int loopback_dev_idx)
-{
- struct cras_iodev *iodev = find_dev(output_dev_idx);
- struct cras_iodev *loopback_dev;
- struct cras_loopback *loopback;
-
- if (iodev == NULL)
- return;
-
- loopback_dev = find_dev(loopback_dev_idx);
- if (loopback_dev == NULL)
- return;
-
- DL_FOREACH (iodev->loopbacks, loopback) {
- if ((loopback->cb_data == loopback_dev) &&
- (loopback->type == type)) {
- DL_DELETE(iodev->loopbacks, loopback);
- free(loopback);
- }
- }
-}
-
-void cras_iodev_list_reset_for_noise_cancellation()
-{
- struct cras_iodev *dev;
- bool enabled = cras_system_get_noise_cancellation_enabled();
-
- DL_FOREACH (devs[CRAS_STREAM_INPUT].iodevs, dev) {
- if (!cras_iodev_is_open(dev) ||
- !cras_iodev_support_noise_cancellation(dev))
- continue;
- syslog(LOG_INFO, "Re-open %s for %s noise cancellation",
- dev->info.name, enabled ? "enabling" : "disabling");
- possibly_enable_fallback(CRAS_STREAM_INPUT, false);
- cras_iodev_list_suspend_dev(dev->info.idx);
- cras_iodev_list_resume_dev(dev->info.idx);
- possibly_disable_fallback(CRAS_STREAM_INPUT);
- }
-}
-
-void cras_iodev_list_reset()
-{
- struct enabled_dev *edev;
-
- DL_FOREACH (enabled_devs[CRAS_STREAM_OUTPUT], edev) {
- DL_DELETE(enabled_devs[CRAS_STREAM_OUTPUT], edev);
- free(edev);
- }
- enabled_devs[CRAS_STREAM_OUTPUT] = NULL;
- DL_FOREACH (enabled_devs[CRAS_STREAM_INPUT], edev) {
- DL_DELETE(enabled_devs[CRAS_STREAM_INPUT], edev);
- free(edev);
- }
- enabled_devs[CRAS_STREAM_INPUT] = NULL;
- devs[CRAS_STREAM_OUTPUT].iodevs = NULL;
- devs[CRAS_STREAM_INPUT].iodevs = NULL;
- devs[CRAS_STREAM_OUTPUT].size = 0;
- devs[CRAS_STREAM_INPUT].size = 0;
-}