summaryrefslogtreecommitdiff
path: root/cras/src/server/cras_iodev_list.c
diff options
context:
space:
mode:
authorAndrew Walbran <qwandor@google.com>2021-08-03 14:20:19 +0000
committerAndrew Walbran <qwandor@google.com>2021-08-03 14:20:19 +0000
commit8dce65084f73d40bb081312769a24b4bd533f667 (patch)
treee2118ad5dbee0370c6ab114bb3974cd9f09a2251 /cras/src/server/cras_iodev_list.c
parentbcf1f249f11b6865cff3f0d3f0ae5801e67e0e7e (diff)
downloadadhd-android-s-v2-beta-3.tar.gz
This repository will be removed from the manifest change, but Treehugger seems unable to test the manifest change, so this change first removes all files so we can test that instead. Bug: 190503456 Test: m crosvm Change-Id: I133ef3bd8b39035a68113c4da8fe4c637a40daac
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;
-}