// 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 <gtest/gtest.h> #include <map> #include <stdio.h> #include <syslog.h> #include <vector> extern "C" { #include "cras_iodev.h" #include "cras_shm.h" #include "cras_system_state.h" #include "cras_types.h" #include "cras_alsa_mixer.h" // Include C file to test static functions. #include "cras_alsa_io.c" } #define BUFFER_SIZE 8192 // Data for simulating functions stubbed below. static int cras_alsa_open_called; static int cras_iodev_append_stream_ret; static int cras_alsa_get_avail_frames_ret; static int cras_alsa_get_avail_frames_avail; static int cras_alsa_start_called; static uint8_t *cras_alsa_mmap_begin_buffer; static size_t cras_alsa_mmap_begin_frames; static size_t cras_alsa_fill_properties_called; static size_t alsa_mixer_set_dBFS_called; static int alsa_mixer_set_dBFS_value; static const struct mixer_control *alsa_mixer_set_dBFS_output; static size_t alsa_mixer_set_capture_dBFS_called; static int alsa_mixer_set_capture_dBFS_value; static const struct mixer_control *alsa_mixer_set_capture_dBFS_input; static const struct mixer_control *cras_alsa_mixer_get_minimum_capture_gain_mixer_input; static const struct mixer_control *cras_alsa_mixer_get_maximum_capture_gain_mixer_input; static size_t cras_alsa_mixer_list_outputs_called; static size_t cras_alsa_mixer_list_inputs_called; static size_t cras_alsa_mixer_get_control_for_section_called; static struct mixer_control * cras_alsa_mixer_get_control_for_section_return_value; static size_t sys_get_volume_called; static size_t sys_get_volume_return_value; static size_t sys_get_capture_gain_called; static long sys_get_capture_gain_return_value; static size_t alsa_mixer_set_mute_called; static int alsa_mixer_set_mute_value; static size_t alsa_mixer_get_dB_range_called; static long alsa_mixer_get_dB_range_value; static size_t alsa_mixer_get_output_dB_range_called; static long alsa_mixer_get_output_dB_range_value; static const struct mixer_control *alsa_mixer_set_mute_output; static size_t alsa_mixer_set_capture_mute_called; static int alsa_mixer_set_capture_mute_value; static const struct mixer_control *alsa_mixer_set_capture_mute_input; static size_t sys_get_mute_called; static int sys_get_mute_return_value; static size_t sys_get_capture_mute_called; static int sys_get_capture_mute_return_value; static struct cras_alsa_mixer *fake_mixer = (struct cras_alsa_mixer *)1; static struct cras_card_config *fake_config = (struct cras_card_config *)2; static struct mixer_control **cras_alsa_mixer_list_outputs_outputs; static size_t cras_alsa_mixer_list_outputs_outputs_length; static struct mixer_control **cras_alsa_mixer_list_inputs_outputs; static size_t cras_alsa_mixer_list_inputs_outputs_length; static size_t cras_alsa_mixer_set_output_active_state_called; static std::vector<struct mixer_control *> cras_alsa_mixer_set_output_active_state_outputs; static std::vector<int> cras_alsa_mixer_set_output_active_state_values; static cras_audio_format *fake_format; static size_t sys_set_volume_limits_called; static size_t sys_set_capture_gain_limits_called; static size_t cras_alsa_mixer_get_minimum_capture_gain_called; static size_t cras_alsa_mixer_get_maximum_capture_gain_called; static struct mixer_control *cras_alsa_jack_get_mixer_output_ret; static struct mixer_control *cras_alsa_jack_get_mixer_input_ret; static size_t cras_alsa_mixer_get_output_volume_curve_called; typedef std::map<const struct mixer_control*, std::string> ControlNameMap; static ControlNameMap cras_alsa_mixer_get_control_name_values; static size_t cras_alsa_mixer_get_control_name_called; static size_t cras_alsa_jack_list_create_called; static size_t cras_alsa_jack_list_find_jacks_by_name_matching_called; static size_t cras_alsa_jack_list_add_jack_for_section_called; static struct cras_alsa_jack * cras_alsa_jack_list_add_jack_for_section_result_jack; static size_t cras_alsa_jack_list_destroy_called; static int cras_alsa_jack_list_has_hctl_jacks_return_val; static jack_state_change_callback *cras_alsa_jack_list_create_cb; static void *cras_alsa_jack_list_create_cb_data; static char test_card_name[] = "TestCard"; static char test_dev_name[] = "TestDev"; static char test_dev_id[] = "TestDevId"; static size_t cras_iodev_add_node_called; static struct cras_ionode *cras_iodev_set_node_attr_ionode; static size_t cras_iodev_set_node_attr_called; static enum ionode_attr cras_iodev_set_node_attr_attr; static int cras_iodev_set_node_attr_value; static unsigned cras_alsa_jack_enable_ucm_called; static unsigned ucm_set_enabled_called; static size_t cras_iodev_update_dsp_called; static const char *cras_iodev_update_dsp_name; static size_t ucm_get_dsp_name_default_called; static const char *ucm_get_dsp_name_default_value; static size_t cras_alsa_jack_get_dsp_name_called; static const char *cras_alsa_jack_get_dsp_name_value; static size_t cras_iodev_free_resources_called; static size_t cras_alsa_jack_update_node_type_called; static int ucm_swap_mode_exists_ret_value; static int ucm_enable_swap_mode_ret_value; static size_t ucm_enable_swap_mode_called; static int is_utf8_string_ret_value; static const char *cras_alsa_jack_update_monitor_fake_name = 0; static int cras_alsa_jack_get_name_called; static const char *cras_alsa_jack_get_name_ret_value = 0; static char default_jack_name[] = "Something Jack"; static int auto_unplug_input_node_ret = 0; static int auto_unplug_output_node_ret = 0; static int ucm_get_min_software_gain_called; static int ucm_get_min_software_gain_ret_value; static long ucm_get_min_software_gain_value; static int ucm_get_max_software_gain_called; static int ucm_get_max_software_gain_ret_value; static long ucm_get_max_software_gain_value; static long cras_system_set_capture_gain_limits_set_value[2]; static long cras_alsa_mixer_get_minimum_capture_gain_ret_value; static long cras_alsa_mixer_get_maximum_capture_gain_ret_value; static snd_pcm_state_t snd_pcm_state_ret; static int cras_alsa_attempt_resume_called; static snd_hctl_t *fake_hctl = (snd_hctl_t *)2; static size_t ucm_get_dma_period_for_dev_called; static unsigned int ucm_get_dma_period_for_dev_ret; static int cras_card_config_get_volume_curve_for_control_called; typedef std::map<std::string, struct cras_volume_curve *> VolCurveMap; static VolCurveMap cras_card_config_get_volume_curve_vals; static int cras_alsa_mmap_get_whole_buffer_called; static int cras_iodev_fill_odev_zeros_called; static unsigned int cras_iodev_fill_odev_zeros_frames; static int cras_iodev_frames_queued_ret; static int cras_iodev_buffer_avail_ret; static int cras_alsa_resume_appl_ptr_called; static int cras_alsa_resume_appl_ptr_ahead; static int ucm_get_enable_htimestamp_flag_ret; static const struct cras_volume_curve *fake_get_dBFS_volume_curve_val; static int cras_iodev_dsp_set_swap_mode_for_node_called; static std::map<std::string, long> ucm_get_default_node_gain_values; static thread_callback audio_thread_cb; static void *audio_thread_cb_data; static int hotword_send_triggered_msg_called; void ResetStubData() { cras_alsa_open_called = 0; cras_iodev_append_stream_ret = 0; cras_alsa_get_avail_frames_ret = 0; cras_alsa_get_avail_frames_avail = 0; cras_alsa_start_called = 0; cras_alsa_fill_properties_called = 0; sys_get_volume_called = 0; sys_get_capture_gain_called = 0; alsa_mixer_set_dBFS_called = 0; alsa_mixer_set_capture_dBFS_called = 0; sys_get_mute_called = 0; sys_get_capture_mute_called = 0; alsa_mixer_set_mute_called = 0; alsa_mixer_get_dB_range_called = 0; alsa_mixer_get_output_dB_range_called = 0; alsa_mixer_set_capture_mute_called = 0; cras_alsa_mixer_get_control_for_section_called = 0; cras_alsa_mixer_get_control_for_section_return_value = NULL; cras_alsa_mixer_list_outputs_called = 0; cras_alsa_mixer_list_outputs_outputs_length = 0; cras_alsa_mixer_list_inputs_called = 0; cras_alsa_mixer_list_inputs_outputs_length = 0; cras_alsa_mixer_set_output_active_state_called = 0; cras_alsa_mixer_set_output_active_state_outputs.clear(); cras_alsa_mixer_set_output_active_state_values.clear(); sys_set_volume_limits_called = 0; sys_set_capture_gain_limits_called = 0; sys_get_capture_gain_return_value = 0; cras_alsa_mixer_get_minimum_capture_gain_called = 0; cras_alsa_mixer_get_maximum_capture_gain_called = 0; cras_alsa_mixer_get_output_volume_curve_called = 0; cras_alsa_jack_get_mixer_output_ret = NULL; cras_alsa_jack_get_mixer_input_ret = NULL; cras_alsa_mixer_get_control_name_values.clear(); cras_alsa_mixer_get_control_name_called = 0; cras_alsa_jack_list_create_called = 0; cras_alsa_jack_list_find_jacks_by_name_matching_called = 0; cras_alsa_jack_list_add_jack_for_section_called = 0; cras_alsa_jack_list_add_jack_for_section_result_jack = NULL; cras_alsa_jack_list_destroy_called = 0; cras_alsa_jack_list_has_hctl_jacks_return_val = 1; cras_iodev_add_node_called = 0; cras_iodev_set_node_attr_called = 0; cras_alsa_jack_enable_ucm_called = 0; ucm_set_enabled_called = 0; cras_iodev_update_dsp_called = 0; cras_iodev_update_dsp_name = 0; ucm_get_dsp_name_default_called = 0; ucm_get_dsp_name_default_value = NULL; cras_alsa_jack_get_dsp_name_called = 0; cras_alsa_jack_get_dsp_name_value = NULL; cras_iodev_free_resources_called = 0; cras_alsa_jack_update_node_type_called = 0; ucm_swap_mode_exists_ret_value = 0; ucm_enable_swap_mode_ret_value = 0; ucm_enable_swap_mode_called = 0; is_utf8_string_ret_value = 1; cras_alsa_jack_get_name_called = 0; cras_alsa_jack_get_name_ret_value = default_jack_name; cras_alsa_jack_update_monitor_fake_name = 0; ucm_get_min_software_gain_called = 0; ucm_get_min_software_gain_ret_value = -1; ucm_get_min_software_gain_value = 0; ucm_get_max_software_gain_called = 0; ucm_get_max_software_gain_ret_value = -1; ucm_get_max_software_gain_value = 0; cras_card_config_get_volume_curve_for_control_called = 0; cras_card_config_get_volume_curve_vals.clear(); cras_system_set_capture_gain_limits_set_value[0] = -1; cras_system_set_capture_gain_limits_set_value[1] = -1; cras_alsa_mixer_get_minimum_capture_gain_ret_value = 0; cras_alsa_mixer_get_maximum_capture_gain_ret_value = 0; snd_pcm_state_ret = SND_PCM_STATE_RUNNING; cras_alsa_attempt_resume_called = 0; ucm_get_dma_period_for_dev_called = 0; ucm_get_dma_period_for_dev_ret = 0; cras_alsa_mmap_get_whole_buffer_called = 0; cras_iodev_fill_odev_zeros_called = 0; cras_iodev_fill_odev_zeros_frames = 0; cras_iodev_frames_queued_ret = 0; cras_iodev_buffer_avail_ret = 0; cras_alsa_resume_appl_ptr_called = 0; cras_alsa_resume_appl_ptr_ahead = 0; ucm_get_enable_htimestamp_flag_ret = 0; fake_get_dBFS_volume_curve_val = NULL; cras_iodev_dsp_set_swap_mode_for_node_called = 0; ucm_get_default_node_gain_values.clear(); } static long fake_get_dBFS(const struct cras_volume_curve *curve, size_t volume) { fake_get_dBFS_volume_curve_val = curve; return (volume - 100) * 100; } static cras_volume_curve default_curve = { .get_dBFS = fake_get_dBFS, }; static struct cras_iodev *alsa_iodev_create_with_default_parameters( size_t card_index, const char *dev_id, enum CRAS_ALSA_CARD_TYPE card_type, int is_first, struct cras_alsa_mixer *mixer, struct cras_card_config *config, struct cras_use_case_mgr *ucm, enum CRAS_STREAM_DIRECTION direction) { return alsa_iodev_create(card_index, test_card_name, 0, test_dev_name, dev_id, card_type, is_first, mixer, config, ucm, fake_hctl, direction, 0, 0, (char *)"123"); } namespace { TEST(AlsaIoInit, InitializeInvalidDirection) { struct alsa_io *aio; aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL, CRAS_NUM_DIRECTIONS); ASSERT_EQ(aio, (void *)NULL); } TEST(AlsaIoInit, InitializePlayback) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; ResetStubData(); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, test_dev_id, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); /* Get volume curve twice for iodev, and default node. */ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream); EXPECT_EQ(0, cras_alsa_fill_properties_called); EXPECT_EQ(1, cras_alsa_mixer_list_outputs_called); EXPECT_EQ(0, strncmp(test_card_name, aio->base.info.name, strlen(test_card_name))); EXPECT_EQ(0, ucm_get_dsp_name_default_called); EXPECT_EQ(NULL, cras_iodev_update_dsp_name); ASSERT_NE(reinterpret_cast<const char *>(NULL), aio->dev_name); EXPECT_EQ(0, strcmp(test_dev_name, aio->dev_name)); ASSERT_NE(reinterpret_cast<const char *>(NULL), aio->dev_id); EXPECT_EQ(0, strcmp(test_dev_id, aio->dev_id)); alsa_iodev_destroy((struct cras_iodev *)aio); EXPECT_EQ(1, cras_iodev_free_resources_called); } TEST(AlsaIoInit, DefaultNodeInternalCard) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; ResetStubData(); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); ASSERT_STREQ("(default)", aio->base.active_node->name); ASSERT_EQ(1, aio->base.active_node->plugged); ASSERT_EQ((void *)no_stream, (void *)aio->base.no_stream); ASSERT_EQ((void *)output_should_wake, (void *)aio->base.output_should_wake); alsa_iodev_destroy((struct cras_iodev *)aio); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called); ASSERT_STREQ("Speaker", aio->base.active_node->name); ASSERT_EQ(1, aio->base.active_node->plugged); ASSERT_EQ((void *)no_stream, (void *)aio->base.no_stream); ASSERT_EQ((void *)output_should_wake, (void *)aio->base.output_should_wake); alsa_iodev_destroy((struct cras_iodev *)aio); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); /* No more call to get volume curve for input device. */ EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called); ASSERT_STREQ("(default)", aio->base.active_node->name); ASSERT_EQ(1, aio->base.active_node->plugged); ASSERT_EQ((void *)no_stream, (void *)aio->base.no_stream); ASSERT_EQ((void *)output_should_wake, (void *)aio->base.output_should_wake); alsa_iodev_destroy((struct cras_iodev *)aio); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called); ASSERT_STREQ("Internal Mic", aio->base.active_node->name); ASSERT_EQ(1, aio->base.active_node->plugged); ASSERT_EQ((void *)no_stream, (void *)aio->base.no_stream); ASSERT_EQ((void *)output_should_wake, (void *)aio->base.output_should_wake); alsa_iodev_destroy((struct cras_iodev *)aio); } TEST(AlsaIoInit, DefaultNodeUSBCard) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; ResetStubData(); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_USB, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); ASSERT_STREQ("(default)", aio->base.active_node->name); ASSERT_EQ(1, aio->base.active_node->plugged); EXPECT_EQ(1, cras_iodev_set_node_attr_called); EXPECT_EQ(IONODE_ATTR_PLUGGED, cras_iodev_set_node_attr_attr); EXPECT_EQ(1, cras_iodev_set_node_attr_value); alsa_iodev_destroy((struct cras_iodev *)aio); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_USB, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); ASSERT_STREQ("(default)", aio->base.active_node->name); ASSERT_EQ(1, aio->base.active_node->plugged); EXPECT_EQ(2, cras_iodev_set_node_attr_called); EXPECT_EQ(IONODE_ATTR_PLUGGED, cras_iodev_set_node_attr_attr); EXPECT_EQ(1, cras_iodev_set_node_attr_value); alsa_iodev_destroy((struct cras_iodev *)aio); } TEST(AlsaIoInit, OpenPlayback) { struct cras_iodev *iodev; struct cras_audio_format format; struct alsa_io *aio; ResetStubData(); iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); aio = (struct alsa_io *)iodev; format.frame_rate = 48000; format.num_channels = 1; cras_iodev_set_format(iodev, &format); // Test that these flags are cleared after open_dev. aio->is_free_running = 1; aio->filled_zeros_for_draining = 512; iodev->open_dev(iodev); EXPECT_EQ(1, cras_alsa_open_called); iodev->configure_dev(iodev); EXPECT_EQ(1, cras_alsa_open_called); EXPECT_EQ(1, sys_set_volume_limits_called); EXPECT_EQ(1, alsa_mixer_set_dBFS_called); EXPECT_EQ(0, cras_alsa_start_called); EXPECT_EQ(0, cras_iodev_set_node_attr_called); EXPECT_EQ(0, aio->is_free_running); EXPECT_EQ(0, aio->filled_zeros_for_draining); EXPECT_EQ(SEVERE_UNDERRUN_MS * format.frame_rate / 1000, aio->severe_underrun_frames); alsa_iodev_destroy(iodev); free(fake_format); } TEST(AlsaIoInit, UsbCardAutoPlug) { struct cras_iodev *iodev; ResetStubData(); iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(0, cras_iodev_set_node_attr_called); alsa_iodev_destroy(iodev); ResetStubData(); iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_USB, 0, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(0, cras_iodev_set_node_attr_called); alsa_iodev_destroy(iodev); ResetStubData(); iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_USB, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); // Should assume USB devs are plugged when they appear. EXPECT_EQ(1, cras_iodev_set_node_attr_called); EXPECT_EQ(IONODE_ATTR_PLUGGED, cras_iodev_set_node_attr_attr); EXPECT_EQ(1, cras_iodev_set_node_attr_value); alsa_iodev_destroy(iodev); } TEST(AlsaIoInit, UsbCardUseSoftwareVolume) { struct cras_iodev *iodev; alsa_mixer_get_dB_range_value = 1000; alsa_mixer_get_output_dB_range_value = 1000; ResetStubData(); iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_USB, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(1, alsa_mixer_get_dB_range_called); EXPECT_EQ(1, alsa_mixer_get_output_dB_range_called); EXPECT_EQ(1, iodev->active_node->software_volume_needed); alsa_iodev_destroy(iodev); alsa_mixer_get_dB_range_value = 3000; alsa_mixer_get_output_dB_range_value = 2000; ResetStubData(); iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_USB, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(1, alsa_mixer_get_dB_range_called); EXPECT_EQ(1, alsa_mixer_get_output_dB_range_called); EXPECT_EQ(0, iodev->active_node->software_volume_needed); alsa_iodev_destroy(iodev); } TEST(AlsaIoInit, UseSoftwareGain) { struct cras_iodev *iodev; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; /* MaxSoftwareGain is specified in UCM */ ResetStubData(); ucm_get_min_software_gain_ret_value = 1; ucm_get_min_software_gain_value = 1; ucm_get_max_software_gain_ret_value = 0; ucm_get_max_software_gain_value = 2000; iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(1, iodev->active_node->software_volume_needed); EXPECT_EQ(DEFAULT_MIN_CAPTURE_GAIN, iodev->active_node->min_software_gain); EXPECT_EQ(2000, iodev->active_node->max_software_gain); ASSERT_EQ(1, sys_set_capture_gain_limits_called); /* The gain range is [DEFAULT_MIN_CAPTURE_GAIN, maximum software gain]. */ ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[0], DEFAULT_MIN_CAPTURE_GAIN); ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[1], 2000); alsa_iodev_destroy(iodev); /* MaxSoftwareGain and MinSoftwareGain are specified in UCM. */ ResetStubData(); ucm_get_min_software_gain_ret_value = 0; ucm_get_min_software_gain_value = 1000; ucm_get_max_software_gain_ret_value = 0; ucm_get_max_software_gain_value = 2000; iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(1, iodev->active_node->software_volume_needed); EXPECT_EQ(1000, iodev->active_node->min_software_gain); EXPECT_EQ(2000, iodev->active_node->max_software_gain); ASSERT_EQ(1, sys_set_capture_gain_limits_called); /* The gain range is [minimum software gain, maximum software gain]. */ ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[0], 1000); ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[1], 2000); alsa_iodev_destroy(iodev); /* MinSoftwareGain is larger than MaxSoftwareGain in UCM. */ ResetStubData(); ucm_get_min_software_gain_ret_value = 0; ucm_get_min_software_gain_value = 3000; ucm_get_max_software_gain_ret_value = 0; ucm_get_max_software_gain_value = 2000; iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(1, iodev->active_node->software_volume_needed); EXPECT_EQ(DEFAULT_MIN_CAPTURE_GAIN, iodev->active_node->min_software_gain); EXPECT_EQ(2000, iodev->active_node->max_software_gain); ASSERT_EQ(1, sys_set_capture_gain_limits_called); /* The gain range is [DEFAULT_MIN_CAPTURE_GAIN, maximum software gain]. */ ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[0], DEFAULT_MIN_CAPTURE_GAIN); ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[1], 2000); alsa_iodev_destroy(iodev); /* MaxSoftwareGain is not specified in UCM. */ ResetStubData(); ucm_get_max_software_gain_ret_value = 1; ucm_get_max_software_gain_value = 1; cras_alsa_mixer_get_minimum_capture_gain_ret_value = -500; cras_alsa_mixer_get_maximum_capture_gain_ret_value = 500; iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(0, iodev->active_node->software_volume_needed); EXPECT_EQ(0, iodev->active_node->max_software_gain); ASSERT_EQ(1, sys_set_capture_gain_limits_called); /* The gain range is reported by controls. */ ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[0], -500); ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[1], 500); alsa_iodev_destroy(iodev); } TEST(AlsaIoInit, SoftwareGainWithDefaultNodeGain) { struct cras_iodev *iodev; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; long system_gain = 500; long default_node_gain = -1000; ResetStubData(); // Use software gain. ucm_get_max_software_gain_ret_value = 0; ucm_get_max_software_gain_value = 2000; // Set default node gain to -1000 * 0.01 dB. ucm_get_default_node_gain_values["Internal Mic"] = default_node_gain; // Assume this is the first device so it gets internal mic node name. iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); // Gain on node is 300 * 0.01 dB. iodev->active_node->capture_gain = default_node_gain; // cras_iodev will call cras_iodev_adjust_active_node_gain to get gain for // software gain. ASSERT_EQ(system_gain + default_node_gain, cras_iodev_adjust_active_node_gain(iodev, system_gain)); alsa_iodev_destroy(iodev); } TEST(AlsaIoInit, RouteBasedOnJackCallback) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; ResetStubData(); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_NE(aio, (void *)NULL); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream); EXPECT_EQ(0, cras_alsa_fill_properties_called); EXPECT_EQ(1, cras_alsa_mixer_list_outputs_called); EXPECT_EQ(1, cras_alsa_jack_list_create_called); EXPECT_EQ(1, cras_alsa_jack_list_find_jacks_by_name_matching_called); EXPECT_EQ(0, cras_alsa_jack_list_add_jack_for_section_called); cras_alsa_jack_list_create_cb(NULL, 1, cras_alsa_jack_list_create_cb_data); EXPECT_EQ(1, cras_iodev_set_node_attr_called); EXPECT_EQ(IONODE_ATTR_PLUGGED, cras_iodev_set_node_attr_attr); EXPECT_EQ(1, cras_iodev_set_node_attr_value); cras_alsa_jack_list_create_cb(NULL, 0, cras_alsa_jack_list_create_cb_data); EXPECT_EQ(2, cras_iodev_set_node_attr_called); EXPECT_EQ(IONODE_ATTR_PLUGGED, cras_iodev_set_node_attr_attr); EXPECT_EQ(0, cras_iodev_set_node_attr_value); alsa_iodev_destroy((struct cras_iodev *)aio); EXPECT_EQ(1, cras_alsa_jack_list_destroy_called); } TEST(AlsaIoInit, RouteBasedOnInputJackCallback) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; ResetStubData(); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL, CRAS_STREAM_INPUT); ASSERT_NE(aio, (void *)NULL); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream); EXPECT_EQ(0, cras_alsa_fill_properties_called); EXPECT_EQ(1, cras_alsa_jack_list_create_called); EXPECT_EQ(1, cras_alsa_jack_list_find_jacks_by_name_matching_called); EXPECT_EQ(0, cras_alsa_jack_list_add_jack_for_section_called); cras_alsa_jack_list_create_cb(NULL, 1, cras_alsa_jack_list_create_cb_data); EXPECT_EQ(1, cras_iodev_set_node_attr_called); EXPECT_EQ(IONODE_ATTR_PLUGGED, cras_iodev_set_node_attr_attr); EXPECT_EQ(1, cras_iodev_set_node_attr_value); cras_alsa_jack_list_create_cb(NULL, 0, cras_alsa_jack_list_create_cb_data); EXPECT_EQ(2, cras_iodev_set_node_attr_called); EXPECT_EQ(IONODE_ATTR_PLUGGED, cras_iodev_set_node_attr_attr); EXPECT_EQ(0, cras_iodev_set_node_attr_value); alsa_iodev_destroy((struct cras_iodev *)aio); EXPECT_EQ(1, cras_alsa_jack_list_destroy_called); } TEST(AlsaIoInit, InitializeCapture) { struct alsa_io *aio; ResetStubData(); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_INPUT); ASSERT_NE(aio, (void *)NULL); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream); EXPECT_EQ(0, cras_alsa_fill_properties_called); EXPECT_EQ(1, cras_alsa_mixer_list_inputs_called); alsa_iodev_destroy((struct cras_iodev *)aio); } TEST(AlsaIoInit, OpenCapture) { struct cras_iodev *iodev; struct cras_audio_format format; struct alsa_io *aio; iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); aio = (struct alsa_io *)iodev; format.frame_rate = 48000; format.num_channels = 1; cras_iodev_set_format(iodev, &format); ResetStubData(); iodev->open_dev(iodev); EXPECT_EQ(1, cras_alsa_open_called); iodev->configure_dev(iodev); EXPECT_EQ(1, cras_alsa_open_called); EXPECT_EQ(1, cras_alsa_mixer_get_minimum_capture_gain_called); EXPECT_EQ(1, cras_alsa_mixer_get_maximum_capture_gain_called); EXPECT_EQ(1, sys_set_capture_gain_limits_called); EXPECT_EQ(1, sys_get_capture_gain_called); EXPECT_EQ(1, alsa_mixer_set_capture_dBFS_called); EXPECT_EQ(1, sys_get_capture_mute_called); EXPECT_EQ(1, alsa_mixer_set_capture_mute_called); EXPECT_EQ(1, cras_alsa_start_called); EXPECT_EQ(SEVERE_UNDERRUN_MS * format.frame_rate / 1000, aio->severe_underrun_frames); alsa_iodev_destroy(iodev); free(fake_format); } TEST(AlsaIoInit, OpenCaptureSetCaptureGainWithDefaultNodeGain) { struct cras_iodev *iodev; struct cras_audio_format format; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; long system_gain = 2000; long default_node_gain = -1000; ResetStubData(); // Set default node gain to -1000 * 0.01 dB. ucm_get_default_node_gain_values["Internal Mic"] = default_node_gain; // Assume this is the first device so it gets internal mic node name. iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); cras_iodev_set_format(iodev, &format); // Check the default node gain is the same as what specified in UCM. EXPECT_EQ(default_node_gain, iodev->active_node->capture_gain); // System gain is set to 2000 * 0.01 dB. sys_get_capture_gain_return_value = system_gain; iodev->open_dev(iodev); iodev->configure_dev(iodev); iodev->close_dev(iodev); // Hardware gain is set to (2000 - 1000) * 0.01 dB. EXPECT_EQ(system_gain + default_node_gain, alsa_mixer_set_capture_dBFS_value); alsa_iodev_destroy(iodev); free(fake_format); } TEST(AlsaIoInit, OpenCaptureSetCaptureGainWithSoftwareGain) { struct cras_iodev *iodev; struct cras_audio_format format; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; /* Meet the requirements of using software gain. */ ResetStubData(); ucm_get_max_software_gain_ret_value = 0; ucm_get_max_software_gain_value = 2000; iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); format.frame_rate = 48000; format.num_channels = 1; cras_iodev_set_format(iodev, &format); /* System gain is set to 1000 * 0.01 dB */ sys_get_capture_gain_return_value = 1000; iodev->open_dev(iodev); iodev->configure_dev(iodev); iodev->close_dev(iodev); /* Hardware gain is set to 0dB when software gain is used. */ EXPECT_EQ(0, alsa_mixer_set_capture_dBFS_value); /* Test the case where software gain is not needed. */ iodev->active_node->software_volume_needed = 0; iodev->open_dev(iodev); iodev->configure_dev(iodev); iodev->close_dev(iodev); /* Hardware gain is set to 1000 * 0.01 dB as got from system capture gain.*/ EXPECT_EQ(1000, alsa_mixer_set_capture_dBFS_value); alsa_iodev_destroy(iodev); free(fake_format); } TEST(AlsaIoInit, UpdateActiveNode) { struct cras_iodev *iodev; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; ResetStubData(); iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); iodev->update_active_node(iodev, 0, 1); alsa_iodev_destroy(iodev); } TEST(AlsaIoInit, StartDevice) { struct cras_iodev *iodev; int rc; ResetStubData(); iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, NULL, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); // Return right away if it is already running. snd_pcm_state_ret = SND_PCM_STATE_RUNNING; rc = iodev->start(iodev); EXPECT_EQ(0, rc); EXPECT_EQ(0, cras_alsa_start_called); // Otherwise, start the device. snd_pcm_state_ret = SND_PCM_STATE_SETUP; rc = iodev->start(iodev); EXPECT_EQ(0, rc); EXPECT_EQ(1, cras_alsa_start_called); alsa_iodev_destroy(iodev); } TEST(AlsaIoInit, ResumeDevice) { struct cras_iodev *iodev; int rc; ResetStubData(); iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, NULL, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); // Attempt to resume if the device is suspended. snd_pcm_state_ret = SND_PCM_STATE_SUSPENDED; rc = iodev->start(iodev); EXPECT_EQ(0, rc); EXPECT_EQ(1, cras_alsa_attempt_resume_called); alsa_iodev_destroy(iodev); } TEST(AlsaIoInit, DspNameDefault) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; ResetStubData(); ucm_get_dsp_name_default_value = "hello"; aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream); EXPECT_EQ(1, ucm_get_dsp_name_default_called); EXPECT_EQ(1, cras_alsa_jack_get_dsp_name_called); EXPECT_STREQ("hello", cras_iodev_update_dsp_name); alsa_iodev_destroy((struct cras_iodev *)aio); } TEST(AlsaIoInit, DspNameJackOverride) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; const struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4; ResetStubData(); ucm_get_dsp_name_default_value = "default_dsp"; cras_alsa_jack_get_dsp_name_value = "override_dsp"; aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream); EXPECT_EQ(1, ucm_get_dsp_name_default_called); EXPECT_EQ(1, cras_alsa_jack_get_dsp_name_called); EXPECT_EQ(1, cras_iodev_update_dsp_called); EXPECT_STREQ("default_dsp", cras_iodev_update_dsp_name); // Add the jack node. cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data); EXPECT_EQ(1, ucm_get_dsp_name_default_called); // Mark the jack node as active. alsa_iodev_set_active_node(&aio->base, aio->base.nodes->next, 1); EXPECT_EQ(2, cras_alsa_jack_get_dsp_name_called); EXPECT_EQ(2, cras_iodev_update_dsp_called); EXPECT_STREQ("override_dsp", cras_iodev_update_dsp_name); // Mark the default node as active. alsa_iodev_set_active_node(&aio->base, aio->base.nodes, 1); EXPECT_EQ(1, ucm_get_dsp_name_default_called); EXPECT_EQ(3, cras_alsa_jack_get_dsp_name_called); EXPECT_EQ(3, cras_iodev_update_dsp_called); EXPECT_STREQ("default_dsp", cras_iodev_update_dsp_name); alsa_iodev_destroy((struct cras_iodev *)aio); } TEST(AlsaIoInit, NodeTypeOverride) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; const struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4; ResetStubData(); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); // Add the jack node. cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data); // Verify that cras_alsa_jack_update_node_type is called when an output device // is created. EXPECT_EQ(1, cras_alsa_jack_update_node_type_called); alsa_iodev_destroy((struct cras_iodev *)aio); } TEST(AlsaIoInit, SwapMode) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; struct cras_ionode * const fake_node = (cras_ionode *)calloc( 1, sizeof(struct cras_ionode)); ResetStubData(); // Stub replies that swap mode does not exist. ucm_swap_mode_exists_ret_value = 0; aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); aio->base.set_swap_mode_for_node((cras_iodev*)aio, fake_node, 1); /* Swap mode is implemented by dsp. */ EXPECT_EQ(1, cras_iodev_dsp_set_swap_mode_for_node_called); // Stub replies that swap mode exists. ucm_swap_mode_exists_ret_value = 1; alsa_iodev_destroy((struct cras_iodev *)aio); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); // Enable swap mode. aio->base.set_swap_mode_for_node((cras_iodev*)aio, fake_node, 1); // Verify that ucm_enable_swap_mode is called when callback to enable // swap mode is called. EXPECT_EQ(1, ucm_enable_swap_mode_called); alsa_iodev_destroy((struct cras_iodev *)aio); free(fake_node); } // Test that system settins aren't touched if no streams active. TEST(AlsaOutputNode, SystemSettingsWhenInactive) { int rc; struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct mixer_control *outputs[2]; ResetStubData(); outputs[0] = reinterpret_cast<struct mixer_control *>(3); outputs[1] = reinterpret_cast<struct mixer_control *>(4); cras_alsa_mixer_list_outputs_outputs = outputs; cras_alsa_mixer_list_outputs_outputs_length = ARRAY_SIZE(outputs); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); /* Two mixer controls calls get volume curve. */ EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream); EXPECT_EQ(1, cras_alsa_mixer_list_outputs_called); ResetStubData(); rc = alsa_iodev_set_active_node((struct cras_iodev *)aio, aio->base.nodes->next, 1); EXPECT_EQ(0, rc); EXPECT_EQ(0, alsa_mixer_set_mute_called); EXPECT_EQ(0, alsa_mixer_set_dBFS_called); ASSERT_EQ(2, cras_alsa_mixer_set_output_active_state_called); EXPECT_EQ(outputs[0], cras_alsa_mixer_set_output_active_state_outputs[0]); EXPECT_EQ(0, cras_alsa_mixer_set_output_active_state_values[0]); EXPECT_EQ(outputs[1], cras_alsa_mixer_set_output_active_state_outputs[1]); EXPECT_EQ(1, cras_alsa_mixer_set_output_active_state_values[1]); EXPECT_EQ(1, cras_iodev_update_dsp_called); // No jack is defined, and UCM is not used. EXPECT_EQ(0, cras_alsa_jack_enable_ucm_called); EXPECT_EQ(0, ucm_set_enabled_called); alsa_iodev_destroy((struct cras_iodev *)aio); } // Test handling of different amounts of outputs. TEST(AlsaOutputNode, TwoOutputs) { int rc; struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct mixer_control *outputs[2]; ResetStubData(); outputs[0] = reinterpret_cast<struct mixer_control *>(3); outputs[1] = reinterpret_cast<struct mixer_control *>(4); cras_alsa_mixer_list_outputs_outputs = outputs; cras_alsa_mixer_list_outputs_outputs_length = ARRAY_SIZE(outputs); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream); EXPECT_EQ(1, cras_alsa_mixer_list_outputs_called); aio->handle = (snd_pcm_t *)0x24; ResetStubData(); rc = alsa_iodev_set_active_node((struct cras_iodev *)aio, aio->base.nodes->next, 1); EXPECT_EQ(0, rc); EXPECT_EQ(2, alsa_mixer_set_mute_called); EXPECT_EQ(outputs[1], alsa_mixer_set_mute_output); EXPECT_EQ(1, alsa_mixer_set_dBFS_called); EXPECT_EQ(outputs[1], alsa_mixer_set_dBFS_output); ASSERT_EQ(2, cras_alsa_mixer_set_output_active_state_called); EXPECT_EQ(outputs[0], cras_alsa_mixer_set_output_active_state_outputs[0]); EXPECT_EQ(0, cras_alsa_mixer_set_output_active_state_values[0]); EXPECT_EQ(outputs[1], cras_alsa_mixer_set_output_active_state_outputs[1]); EXPECT_EQ(1, cras_alsa_mixer_set_output_active_state_values[1]); EXPECT_EQ(1, cras_iodev_update_dsp_called); // No jacks defined, and UCM is not used. EXPECT_EQ(0, cras_alsa_jack_enable_ucm_called); EXPECT_EQ(0, ucm_set_enabled_called); alsa_iodev_destroy((struct cras_iodev *)aio); } TEST(AlsaOutputNode, TwoJacksHeadphoneLineout) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer *)2; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr *)3; struct cras_iodev *iodev; struct mixer_control *output; struct ucm_section *section; ResetStubData(); output = reinterpret_cast<struct mixer_control *>(3); cras_alsa_mixer_get_control_name_values[output] = "Headphone"; // Create the iodev iodev = alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_NE(iodev, (void *)NULL); aio = reinterpret_cast<struct alsa_io *>(iodev); EXPECT_EQ(1, cras_card_config_get_volume_curve_for_control_called); // First node 'Headphone' section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT, "fake-jack", "gpio"); ucm_section_set_mixer_name(section, "Headphone"); cras_alsa_jack_list_add_jack_for_section_result_jack = reinterpret_cast<struct cras_alsa_jack *>(10); cras_alsa_mixer_get_control_for_section_return_value = output; ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called); ucm_section_free_list(section); // Second node 'Line Out' section = ucm_section_create("Line Out", 0, CRAS_STREAM_OUTPUT, "fake-jack", "gpio"); ucm_section_set_mixer_name(section, "Headphone"); cras_alsa_jack_list_add_jack_for_section_result_jack = reinterpret_cast<struct cras_alsa_jack *>(20); cras_alsa_mixer_get_control_for_section_return_value = output; ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); EXPECT_EQ(7, cras_card_config_get_volume_curve_for_control_called); ucm_section_free_list(section); // Both nodes are associated with the same mixer output. Different jack plug // report should trigger different node attribute change. cras_alsa_jack_get_mixer_output_ret = output; jack_output_plug_event(reinterpret_cast<struct cras_alsa_jack *>(10), 0, aio); EXPECT_STREQ(cras_iodev_set_node_attr_ionode->name, "Headphone"); jack_output_plug_event(reinterpret_cast<struct cras_alsa_jack *>(20), 0, aio); EXPECT_STREQ(cras_iodev_set_node_attr_ionode->name, "Line Out"); alsa_iodev_destroy(iodev); } TEST(AlsaOutputNode, OutputsFromUCM) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; struct cras_iodev *iodev; static const char *jack_name = "TestCard - Headset Jack"; struct mixer_control *outputs[2]; int rc; struct ucm_section *section; ResetStubData(); outputs[0] = reinterpret_cast<struct mixer_control *>(3); outputs[1] = reinterpret_cast<struct mixer_control *>(4); cras_alsa_mixer_list_outputs_outputs = outputs; cras_alsa_mixer_list_outputs_outputs_length = ARRAY_SIZE(outputs); cras_alsa_mixer_get_control_name_values[outputs[0]] = INTERNAL_SPEAKER; cras_alsa_mixer_get_control_name_values[outputs[1]] = "Headphone"; ucm_get_dma_period_for_dev_ret = 1000; // Create the IO device. iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_NE(iodev, (void *)NULL); aio = reinterpret_cast<struct alsa_io *>(iodev); EXPECT_EQ(1, cras_card_config_get_volume_curve_for_control_called); // First node. section = ucm_section_create(INTERNAL_SPEAKER, 0, CRAS_STREAM_OUTPUT, NULL, NULL); ucm_section_set_mixer_name(section, INTERNAL_SPEAKER); cras_alsa_jack_list_add_jack_for_section_result_jack = reinterpret_cast<struct cras_alsa_jack *>(1); cras_alsa_mixer_get_control_for_section_return_value = outputs[0]; ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); ucm_section_free_list(section); EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called); // Add a second node (will use the same iodev). section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT, jack_name, "hctl"); ucm_section_add_coupled(section, "HP-L", MIXER_NAME_VOLUME); ucm_section_add_coupled(section, "HP-R", MIXER_NAME_VOLUME); cras_alsa_jack_list_add_jack_for_section_result_jack = NULL; cras_alsa_mixer_get_control_for_section_return_value = outputs[1]; ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); ucm_section_free_list(section); /* New nodes creation calls get volume curve once, NULL jack doesn't make * more calls. */ EXPECT_EQ(5, cras_card_config_get_volume_curve_for_control_called); // Jack plug of an unkonwn device should do nothing. cras_alsa_jack_get_mixer_output_ret = NULL; cras_alsa_jack_get_name_ret_value = "Some other jack"; jack_output_plug_event(reinterpret_cast<struct cras_alsa_jack *>(4), 0, aio); EXPECT_EQ(0, cras_iodev_set_node_attr_called); // Complete initialization, and make first node active. alsa_iodev_ucm_complete_init(iodev); EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream); EXPECT_EQ(2, cras_alsa_jack_list_add_jack_for_section_called); EXPECT_EQ(2, cras_alsa_mixer_get_control_for_section_called); EXPECT_EQ(1, ucm_get_dma_period_for_dev_called); EXPECT_EQ(ucm_get_dma_period_for_dev_ret, aio->dma_period_set_microsecs); aio->handle = (snd_pcm_t *)0x24; ResetStubData(); rc = alsa_iodev_set_active_node(iodev, aio->base.nodes->next, 1); EXPECT_EQ(0, rc); EXPECT_EQ(2, alsa_mixer_set_mute_called); EXPECT_EQ(outputs[1], alsa_mixer_set_mute_output); EXPECT_EQ(1, alsa_mixer_set_dBFS_called); EXPECT_EQ(outputs[1], alsa_mixer_set_dBFS_output); ASSERT_EQ(2, cras_alsa_mixer_set_output_active_state_called); EXPECT_EQ(outputs[0], cras_alsa_mixer_set_output_active_state_outputs[0]); EXPECT_EQ(0, cras_alsa_mixer_set_output_active_state_values[0]); EXPECT_EQ(outputs[1], cras_alsa_mixer_set_output_active_state_outputs[1]); EXPECT_EQ(1, cras_alsa_mixer_set_output_active_state_values[1]); EXPECT_EQ(1, cras_iodev_update_dsp_called); EXPECT_EQ(1, cras_alsa_jack_enable_ucm_called); EXPECT_EQ(1, ucm_set_enabled_called); // Simulate jack plug event. cras_alsa_jack_get_mixer_output_ret = outputs[1]; cras_alsa_jack_get_name_ret_value = jack_name; jack_output_plug_event(reinterpret_cast<struct cras_alsa_jack *>(4), 0, aio); EXPECT_EQ(1, cras_iodev_set_node_attr_called); alsa_iodev_destroy(iodev); } TEST(AlsaOutputNode, OutputNoControlsUCM) { struct alsa_io *aio; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; struct cras_iodev *iodev; struct ucm_section *section; ResetStubData(); // Create the IO device. iodev = alsa_iodev_create_with_default_parameters(1, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_NE(iodev, (void *)NULL); aio = reinterpret_cast<struct alsa_io *>(iodev); EXPECT_EQ(1, cras_card_config_get_volume_curve_for_control_called); // Node without controls or jacks. section = ucm_section_create(INTERNAL_SPEAKER, 1, CRAS_STREAM_OUTPUT, NULL, NULL); // Device index doesn't match. EXPECT_EQ(-22, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); section->dev_idx = 0; ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(1, cras_alsa_mixer_get_control_for_section_called); EXPECT_EQ(1, cras_iodev_add_node_called); ucm_section_free_list(section); // Complete initialization, and make first node active. alsa_iodev_ucm_complete_init(iodev); EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream); EXPECT_EQ(0, cras_alsa_mixer_get_control_name_called); EXPECT_EQ(1, cras_iodev_update_dsp_called); EXPECT_EQ(0, cras_alsa_jack_enable_ucm_called); EXPECT_EQ(1, ucm_set_enabled_called); alsa_iodev_destroy(iodev); } TEST(AlsaOutputNode, OutputFromJackUCM) { struct alsa_io *aio; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; struct cras_iodev *iodev; static const char *jack_name = "TestCard - Headset Jack"; struct ucm_section *section; ResetStubData(); // Create the IO device. iodev = alsa_iodev_create_with_default_parameters(1, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_NE(iodev, (void *)NULL); aio = reinterpret_cast<struct alsa_io *>(iodev); EXPECT_EQ(1, cras_card_config_get_volume_curve_for_control_called); // Node without controls or jacks. cras_alsa_jack_list_add_jack_for_section_result_jack = reinterpret_cast<struct cras_alsa_jack *>(1); section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT, jack_name, "hctl"); ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(1, cras_alsa_mixer_get_control_for_section_called); EXPECT_EQ(1, cras_iodev_add_node_called); EXPECT_EQ(1, cras_alsa_jack_list_add_jack_for_section_called); ucm_section_free_list(section); // Complete initialization, and make first node active. alsa_iodev_ucm_complete_init(iodev); EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream); EXPECT_EQ(0, cras_alsa_mixer_get_control_name_called); EXPECT_EQ(1, cras_iodev_update_dsp_called); EXPECT_EQ(1, cras_alsa_jack_enable_ucm_called); EXPECT_EQ(0, ucm_set_enabled_called); alsa_iodev_destroy(iodev); } TEST(AlsaOutputNode, InputsFromUCM) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; struct mixer_control *inputs[2]; struct cras_iodev *iodev; static const char *jack_name = "TestCard - Headset Jack"; int rc; struct ucm_section *section; ResetStubData(); inputs[0] = reinterpret_cast<struct mixer_control *>(3); inputs[1] = reinterpret_cast<struct mixer_control *>(4); cras_alsa_mixer_list_inputs_outputs = inputs; cras_alsa_mixer_list_inputs_outputs_length = ARRAY_SIZE(inputs); cras_alsa_mixer_get_control_name_values[inputs[0]] = "Internal Mic"; cras_alsa_mixer_get_control_name_values[inputs[1]] = "Mic"; // Create the IO device. iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_NE(iodev, (void *)NULL); aio = reinterpret_cast<struct alsa_io *>(iodev); // First node. cras_alsa_mixer_get_control_for_section_return_value = inputs[0]; ucm_get_max_software_gain_ret_value = -1; section = ucm_section_create(INTERNAL_MICROPHONE, 0, CRAS_STREAM_INPUT, NULL, NULL); ucm_section_add_coupled(section, "MIC-L", MIXER_NAME_VOLUME); ucm_section_add_coupled(section, "MIC-R", MIXER_NAME_VOLUME); ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); ucm_section_free_list(section); // Add a second node (will use the same iodev). cras_alsa_mixer_get_control_name_called = 0; ucm_get_max_software_gain_ret_value = 0; ucm_get_max_software_gain_value = 2000; cras_alsa_jack_list_add_jack_for_section_result_jack = reinterpret_cast<struct cras_alsa_jack *>(1); cras_alsa_mixer_get_control_for_section_return_value = inputs[1]; section = ucm_section_create("Mic", 0, CRAS_STREAM_INPUT, jack_name, "hctl"); ucm_section_set_mixer_name(section, "Mic"); ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); ucm_section_free_list(section); // Jack plug of an unkonwn device should do nothing. cras_alsa_jack_get_mixer_input_ret = NULL; cras_alsa_jack_get_name_ret_value = "Some other jack"; jack_input_plug_event(reinterpret_cast<struct cras_alsa_jack *>(4), 0, aio); EXPECT_EQ(0, cras_iodev_set_node_attr_called); // Simulate jack plug event. cras_alsa_jack_get_mixer_input_ret = inputs[1]; cras_alsa_jack_get_name_ret_value = jack_name; jack_input_plug_event(reinterpret_cast<struct cras_alsa_jack *>(4), 0, aio); EXPECT_EQ(1, cras_iodev_set_node_attr_called); // Complete initialization, and make first node active. alsa_iodev_ucm_complete_init(iodev); EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream); EXPECT_EQ(2, cras_alsa_jack_list_add_jack_for_section_called); EXPECT_EQ(2, cras_alsa_mixer_get_control_for_section_called); EXPECT_EQ(1, cras_alsa_mixer_get_control_name_called); EXPECT_EQ(1, sys_set_capture_gain_limits_called); EXPECT_EQ(2, cras_iodev_add_node_called); EXPECT_EQ(2, ucm_get_dma_period_for_dev_called); EXPECT_EQ(0, aio->dma_period_set_microsecs); aio->handle = (snd_pcm_t *)0x24; ResetStubData(); rc = alsa_iodev_set_active_node(iodev, aio->base.nodes->next, 1); EXPECT_EQ(0, rc); EXPECT_EQ(1, alsa_mixer_set_capture_dBFS_called); EXPECT_EQ(inputs[1], alsa_mixer_set_capture_dBFS_input); EXPECT_EQ(0, alsa_mixer_set_capture_dBFS_value); EXPECT_EQ(1, cras_iodev_update_dsp_called); EXPECT_EQ(1, cras_alsa_jack_enable_ucm_called); EXPECT_EQ(1, ucm_set_enabled_called); EXPECT_EQ(1, sys_set_capture_gain_limits_called); EXPECT_EQ(1, alsa_mixer_set_capture_mute_called); EXPECT_EQ(1, iodev->active_node->software_volume_needed); EXPECT_EQ(2000, iodev->active_node->max_software_gain); alsa_iodev_destroy(iodev); } TEST(AlsaOutputNode, InputNoControlsUCM) { struct alsa_io *aio; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; struct cras_iodev *iodev; struct ucm_section *section; ResetStubData(); // Create the IO device. iodev = alsa_iodev_create_with_default_parameters(1, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_NE(iodev, (void *)NULL); aio = reinterpret_cast<struct alsa_io *>(iodev); // Node without controls or jacks. section = ucm_section_create(INTERNAL_MICROPHONE, 1, CRAS_STREAM_INPUT, NULL, NULL); // Device index doesn't match. EXPECT_EQ(-22, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); section->dev_idx = 0; ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); EXPECT_EQ(1, cras_alsa_jack_list_add_jack_for_section_called); EXPECT_EQ(1, cras_alsa_mixer_get_control_for_section_called); EXPECT_EQ(0, cras_alsa_mixer_get_control_name_called); EXPECT_EQ(1, cras_iodev_add_node_called); ucm_section_free_list(section); // Complete initialization, and make first node active. alsa_iodev_ucm_complete_init(iodev); EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream); EXPECT_EQ(0, cras_alsa_mixer_get_control_name_called); EXPECT_EQ(1, cras_iodev_update_dsp_called); EXPECT_EQ(0, cras_alsa_jack_enable_ucm_called); EXPECT_EQ(1, ucm_set_enabled_called); alsa_iodev_destroy(iodev); } TEST(AlsaOutputNode, InputFromJackUCM) { struct alsa_io *aio; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; struct cras_iodev *iodev; static const char *jack_name = "TestCard - Headset Jack"; struct ucm_section *section; ResetStubData(); // Create the IO device. iodev = alsa_iodev_create_with_default_parameters(1, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_NE(iodev, (void *)NULL); aio = reinterpret_cast<struct alsa_io *>(iodev); // Node without controls or jacks. cras_alsa_jack_list_add_jack_for_section_result_jack = reinterpret_cast<struct cras_alsa_jack *>(1); section = ucm_section_create("Mic", 0, CRAS_STREAM_INPUT, jack_name, "hctl"); ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section)); EXPECT_EQ(1, cras_alsa_mixer_get_control_for_section_called); EXPECT_EQ(1, cras_iodev_add_node_called); EXPECT_EQ(1, cras_alsa_jack_list_add_jack_for_section_called); ucm_section_free_list(section); // Complete initialization, and make first node active. alsa_iodev_ucm_complete_init(iodev); EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream); EXPECT_EQ(0, cras_alsa_mixer_get_control_name_called); EXPECT_EQ(1, cras_iodev_update_dsp_called); EXPECT_EQ(1, cras_alsa_jack_enable_ucm_called); EXPECT_EQ(0, ucm_set_enabled_called); alsa_iodev_destroy(iodev); } TEST(AlsaOutputNode, AutoUnplugOutputNode) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; struct mixer_control *outputs[2]; const struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4; ResetStubData(); outputs[0] = reinterpret_cast<struct mixer_control *>(5); outputs[1] = reinterpret_cast<struct mixer_control *>(6); cras_alsa_mixer_list_outputs_outputs = outputs; cras_alsa_mixer_list_outputs_outputs_length = ARRAY_SIZE(outputs); cras_alsa_mixer_get_control_name_values[outputs[0]] = INTERNAL_SPEAKER; cras_alsa_mixer_get_control_name_values[outputs[1]] = "Headphone"; auto_unplug_output_node_ret = 1; aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(3, cras_card_config_get_volume_curve_for_control_called); EXPECT_EQ(1, cras_alsa_mixer_list_outputs_called); EXPECT_EQ(2, cras_alsa_mixer_get_control_name_called); // Assert that the the internal speaker is plugged and other nodes aren't. ASSERT_NE(aio->base.nodes, (void *)NULL); EXPECT_EQ(aio->base.nodes->plugged, 1); ASSERT_NE(aio->base.nodes->next, (void *)NULL); EXPECT_EQ(aio->base.nodes->next->plugged, 0); // Plug headphone jack cras_alsa_jack_get_name_ret_value = "Headphone Jack"; is_utf8_string_ret_value = 1; cras_alsa_jack_get_mixer_output_ret = outputs[1]; cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data); // Assert internal speaker is auto unplugged EXPECT_EQ(aio->base.nodes->plugged, 0); EXPECT_EQ(aio->base.nodes->next->plugged, 1); alsa_iodev_destroy((struct cras_iodev *)aio); } TEST(AlsaOutputNode, AutoUnplugInputNode) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; struct mixer_control *inputs[2]; const struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4; ResetStubData(); inputs[0] = reinterpret_cast<struct mixer_control *>(5); inputs[1] = reinterpret_cast<struct mixer_control *>(6); cras_alsa_mixer_list_inputs_outputs = inputs; cras_alsa_mixer_list_inputs_outputs_length = ARRAY_SIZE(inputs); cras_alsa_mixer_get_control_name_values[inputs[0]] = INTERNAL_MICROPHONE; cras_alsa_mixer_get_control_name_values[inputs[1]] = "Mic"; auto_unplug_input_node_ret = 1; aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_INPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); EXPECT_EQ(1, cras_alsa_mixer_list_inputs_called); EXPECT_EQ(2, cras_alsa_mixer_get_control_name_called); // Assert that the the internal speaker is plugged and other nodes aren't. ASSERT_NE(aio->base.nodes, (void *)NULL); EXPECT_EQ(aio->base.nodes->plugged, 1); ASSERT_NE(aio->base.nodes->next, (void *)NULL); EXPECT_EQ(aio->base.nodes->next->plugged, 0); // Plug headphone jack cras_alsa_jack_get_name_ret_value = "Mic Jack"; is_utf8_string_ret_value = 1; cras_alsa_jack_get_mixer_input_ret = inputs[1]; cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data); // Assert internal speaker is auto unplugged EXPECT_EQ(aio->base.nodes->plugged, 0); EXPECT_EQ(aio->base.nodes->next->plugged, 1); alsa_iodev_destroy((struct cras_iodev *)aio); } TEST(AlsaInitNode, SetNodeInitialState) { struct cras_ionode node; struct cras_iodev dev; memset(&dev, 0, sizeof(dev)); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Unknown"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(0, node.plugged_time.tv_sec); ASSERT_EQ(CRAS_NODE_TYPE_UNKNOWN, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Speaker"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(1, node.plugged); ASSERT_GT(node.plugged_time.tv_sec, 0); ASSERT_EQ(CRAS_NODE_TYPE_INTERNAL_SPEAKER, node.type); ASSERT_EQ(NODE_POSITION_INTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Internal Mic"); dev.direction = CRAS_STREAM_INPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(1, node.plugged); ASSERT_GT(node.plugged_time.tv_sec, 0); ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type); ASSERT_EQ(NODE_POSITION_INTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "HDMI"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(0, node.plugged_time.tv_sec); ASSERT_EQ(CRAS_NODE_TYPE_HDMI, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "IEC958"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_HDMI, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "HDMI Jack"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_HDMI, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Something HDMI Jack"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_HDMI, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Headphone"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_HEADPHONE, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Headphone Jack"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_HEADPHONE, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Mic"); dev.direction = CRAS_STREAM_INPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Front Mic"); dev.direction = CRAS_STREAM_INPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(1, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type); ASSERT_EQ(NODE_POSITION_FRONT, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Rear Mic"); dev.direction = CRAS_STREAM_INPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(1, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type); ASSERT_EQ(NODE_POSITION_REAR, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Mic Jack"); dev.direction = CRAS_STREAM_INPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Unknown"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_USB); ASSERT_EQ(0, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_USB, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; dev.direction = CRAS_STREAM_INPUT; strcpy(node.name, "DAISY-I2S Mic Jack"); set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); // Node name is changed to "Mic". ASSERT_EQ(0, strcmp(node.name, "Mic")); memset(&node, 0, sizeof(node)); node.dev = &dev; dev.direction = CRAS_STREAM_OUTPUT; strcpy(node.name, "DAISY-I2S Headphone Jack"); set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(0, node.plugged); ASSERT_EQ(CRAS_NODE_TYPE_HEADPHONE, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); // Node name is changed to "Headphone". ASSERT_EQ(0, strcmp(node.name, "Headphone")); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Speaker"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_USB); ASSERT_EQ(1, node.plugged); ASSERT_GT(node.plugged_time.tv_sec, 0); ASSERT_EQ(CRAS_NODE_TYPE_USB, node.type); ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Haptic"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(1, node.plugged); ASSERT_GT(node.plugged_time.tv_sec, 0); ASSERT_EQ(CRAS_NODE_TYPE_HAPTIC, node.type); ASSERT_EQ(NODE_POSITION_INTERNAL, node.position); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Rumbler"); dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(1, node.plugged); ASSERT_GT(node.plugged_time.tv_sec, 0); ASSERT_EQ(CRAS_NODE_TYPE_HAPTIC, node.type); ASSERT_EQ(NODE_POSITION_INTERNAL, node.position); } TEST(AlsaInitNode, SetNodeInitialStateDropInvalidUTF8NodeName) { struct cras_ionode node; struct cras_iodev dev; memset(&dev, 0, sizeof(dev)); memset(&node, 0, sizeof(node)); node.dev = &dev; memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Something USB"); //0xfe can not appear in a valid UTF-8 string. node.name[0] = 0xfe; is_utf8_string_ret_value = 0; dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_USB); ASSERT_EQ(CRAS_NODE_TYPE_USB, node.type); ASSERT_STREQ("USB", node.name); memset(&node, 0, sizeof(node)); node.dev = &dev; strcpy(node.name, "Something HDMI Jack"); //0xfe can not appear in a valid UTF-8 string. node.name[0] = 0xfe; is_utf8_string_ret_value = 0; dev.direction = CRAS_STREAM_OUTPUT; set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL); ASSERT_EQ(CRAS_NODE_TYPE_HDMI, node.type); ASSERT_STREQ("HDMI", node.name); } TEST(AlsaIoInit, HDMIJackUpdateInvalidUTF8MonitorName) { struct alsa_io *aio; struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2; struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3; const struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4; ResetStubData(); aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm, CRAS_STREAM_OUTPUT); ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio)); // Prepare the stub data such that the jack will be identified as an // HDMI jack, and thus the callback creates an HDMI node. cras_alsa_jack_get_name_ret_value = "HDMI Jack"; // Set the jack name updated from monitor to be an invalid UTF8 string. cras_alsa_jack_update_monitor_fake_name = "\xfeomething"; is_utf8_string_ret_value = 0; // Add the jack node. cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data); EXPECT_EQ(2, cras_alsa_jack_get_name_called); ASSERT_EQ(CRAS_NODE_TYPE_HDMI, aio->base.nodes->next->type); // The node name should be "HDMI". ASSERT_STREQ("HDMI", aio->base.nodes->next->name); alsa_iodev_destroy((struct cras_iodev *)aio); } // Test thread add/rm stream, open_alsa, and iodev config. class AlsaVolumeMuteSuite : public testing::Test { protected: virtual void SetUp() { ResetStubData(); output_control_ = reinterpret_cast<struct mixer_control *>(10); cras_alsa_mixer_list_outputs_outputs = &output_control_; cras_alsa_mixer_list_outputs_outputs_length = 1; cras_alsa_mixer_get_control_name_values[output_control_] = "Speaker"; cras_alsa_mixer_list_outputs_outputs_length = 1; aio_output_ = (struct alsa_io *)alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL, CRAS_STREAM_OUTPUT); alsa_iodev_legacy_complete_init((struct cras_iodev *)aio_output_); EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called); struct cras_ionode *node; int count = 0; DL_FOREACH(aio_output_->base.nodes, node) { printf("node %d \n", count); } aio_output_->base.direction = CRAS_STREAM_OUTPUT; fmt_.frame_rate = 44100; fmt_.num_channels = 2; fmt_.format = SND_PCM_FORMAT_S16_LE; aio_output_->base.format = &fmt_; cras_alsa_get_avail_frames_ret = -1; } virtual void TearDown() { alsa_iodev_destroy((struct cras_iodev *)aio_output_); cras_alsa_get_avail_frames_ret = 0; } struct mixer_control *output_control_; struct alsa_io *aio_output_; struct cras_audio_format fmt_; }; TEST_F(AlsaVolumeMuteSuite, GetDefaultVolumeCurve) { int rc; struct cras_audio_format *fmt; fmt = (struct cras_audio_format *)malloc(sizeof(*fmt)); memcpy(fmt, &fmt_, sizeof(fmt_)); aio_output_->base.format = fmt; aio_output_->handle = (snd_pcm_t *)0x24; rc = aio_output_->base.configure_dev(&aio_output_->base); ASSERT_EQ(0, rc); EXPECT_EQ(&default_curve, fake_get_dBFS_volume_curve_val); aio_output_->base.set_volume(&aio_output_->base); EXPECT_EQ(&default_curve, fake_get_dBFS_volume_curve_val); free(fmt); } TEST_F(AlsaVolumeMuteSuite, GetVolumeCurveFromNode) { int rc; struct cras_audio_format *fmt; struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4; struct cras_ionode *node; struct cras_volume_curve hp_curve = { .get_dBFS = fake_get_dBFS, }; fmt = (struct cras_audio_format *)malloc(sizeof(*fmt)); memcpy(fmt, &fmt_, sizeof(fmt_)); aio_output_->base.format = fmt; aio_output_->handle = (snd_pcm_t *)0x24; // Headphone jack plugged and has its own volume curve. cras_alsa_jack_get_mixer_output_ret = NULL; cras_alsa_jack_get_name_ret_value = "Headphone"; cras_card_config_get_volume_curve_vals["Headphone"] = &hp_curve; cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data); EXPECT_EQ(1, cras_alsa_jack_update_node_type_called); EXPECT_EQ(3, cras_card_config_get_volume_curve_for_control_called); // Switch to node 'Headphone'. node = aio_output_->base.nodes->next; aio_output_->base.active_node = node; rc = aio_output_->base.configure_dev(&aio_output_->base); ASSERT_EQ(0, rc); EXPECT_EQ(&hp_curve, fake_get_dBFS_volume_curve_val); aio_output_->base.set_volume(&aio_output_->base); EXPECT_EQ(&hp_curve, fake_get_dBFS_volume_curve_val); free(fmt); } TEST_F(AlsaVolumeMuteSuite, SetVolume) { int rc; struct cras_audio_format *fmt; const size_t fake_system_volume = 55; const size_t fake_system_volume_dB = (fake_system_volume - 100) * 100; fmt = (struct cras_audio_format *)malloc(sizeof(*fmt)); memcpy(fmt, &fmt_, sizeof(fmt_)); aio_output_->base.format = fmt; aio_output_->handle = (snd_pcm_t *)0x24; aio_output_->num_underruns = 3; // Something non-zero. sys_get_volume_return_value = fake_system_volume; rc = aio_output_->base.configure_dev(&aio_output_->base); ASSERT_EQ(0, rc); EXPECT_EQ(1, alsa_mixer_set_dBFS_called); EXPECT_EQ(fake_system_volume_dB, alsa_mixer_set_dBFS_value); alsa_mixer_set_dBFS_called = 0; alsa_mixer_set_dBFS_value = 0; sys_get_volume_return_value = 50; sys_get_volume_called = 0; aio_output_->base.set_volume(&aio_output_->base); EXPECT_EQ(1, sys_get_volume_called); EXPECT_EQ(1, alsa_mixer_set_dBFS_called); EXPECT_EQ(-5000, alsa_mixer_set_dBFS_value); EXPECT_EQ(output_control_, alsa_mixer_set_dBFS_output); alsa_mixer_set_dBFS_called = 0; alsa_mixer_set_dBFS_value = 0; sys_get_volume_return_value = 0; sys_get_volume_called = 0; aio_output_->base.set_volume(&aio_output_->base); EXPECT_EQ(1, sys_get_volume_called); EXPECT_EQ(1, alsa_mixer_set_dBFS_called); EXPECT_EQ(-10000, alsa_mixer_set_dBFS_value); sys_get_volume_return_value = 80; aio_output_->base.active_node->volume = 90; aio_output_->base.set_volume(&aio_output_->base); EXPECT_EQ(-3000, alsa_mixer_set_dBFS_value); // close the dev. rc = aio_output_->base.close_dev(&aio_output_->base); EXPECT_EQ(0, rc); EXPECT_EQ((void *)NULL, aio_output_->handle); free(fmt); } TEST_F(AlsaVolumeMuteSuite, SetMute) { int muted; aio_output_->handle = (snd_pcm_t *)0x24; // Test mute. ResetStubData(); muted = 1; sys_get_mute_return_value = muted; aio_output_->base.set_mute(&aio_output_->base); EXPECT_EQ(1, sys_get_mute_called); EXPECT_EQ(1, alsa_mixer_set_mute_called); EXPECT_EQ(muted, alsa_mixer_set_mute_value); EXPECT_EQ(output_control_, alsa_mixer_set_mute_output); // Test unmute. ResetStubData(); muted = 0; sys_get_mute_return_value = muted; aio_output_->base.set_mute(&aio_output_->base); EXPECT_EQ(1, sys_get_mute_called); EXPECT_EQ(1, alsa_mixer_set_mute_called); EXPECT_EQ(muted, alsa_mixer_set_mute_value); EXPECT_EQ(output_control_, alsa_mixer_set_mute_output); } // Test free run. class AlsaFreeRunTestSuite: public testing::Test { protected: virtual void SetUp() { ResetStubData(); memset(&aio, 0, sizeof(aio)); fmt_.format = SND_PCM_FORMAT_S16_LE; fmt_.frame_rate = 48000; fmt_.num_channels = 2; aio.base.frames_queued = frames_queued; aio.base.direction = CRAS_STREAM_OUTPUT; aio.base.format = &fmt_; aio.base.buffer_size = BUFFER_SIZE; aio.base.min_cb_level = 240; aio.base.min_buffer_level = 0; aio.filled_zeros_for_draining = 0; cras_alsa_mmap_begin_buffer = (uint8_t *)calloc( BUFFER_SIZE * 2 * 2, sizeof(*cras_alsa_mmap_begin_buffer)); memset(cras_alsa_mmap_begin_buffer, 0xff, sizeof(*cras_alsa_mmap_begin_buffer)); } virtual void TearDown() { free(cras_alsa_mmap_begin_buffer); } struct alsa_io aio; struct cras_audio_format fmt_; }; TEST_F(AlsaFreeRunTestSuite, FillWholeBufferWithZeros) { int rc; int16_t *zeros; rc = fill_whole_buffer_with_zeros(&aio.base); EXPECT_EQ(0, rc); zeros = (int16_t *)calloc(BUFFER_SIZE * 2, sizeof(*zeros)); EXPECT_EQ(0, memcmp(zeros, cras_alsa_mmap_begin_buffer, BUFFER_SIZE * 2 * 2)); free(zeros); } TEST_F(AlsaFreeRunTestSuite, EnterFreeRunAlreadyFreeRunning) { int rc; // Device is in free run state, no need to fill zeros or fill whole buffer. aio.is_free_running = 1; rc = no_stream(&aio.base, 1); EXPECT_EQ(0, rc); EXPECT_EQ(0, cras_alsa_mmap_get_whole_buffer_called); EXPECT_EQ(0, cras_iodev_fill_odev_zeros_called); EXPECT_EQ(0, cras_iodev_fill_odev_zeros_frames); } TEST_F(AlsaFreeRunTestSuite, EnterFreeRunNotDrainedYetNeedToFillZeros) { int rc, real_hw_level; struct timespec hw_tstamp; // Device is not in free run state. There are still valid samples to play. // The number of valid samples is less than min_cb_level * 2. // Need to fill zeros targeting min_cb_level * 2 = 480. // The number of zeros to be filled is 480 - 200 = 280. real_hw_level = 200; cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level; rc = aio.base.frames_queued(&aio.base, &hw_tstamp); EXPECT_EQ(200, rc); rc = no_stream(&aio.base, 1); EXPECT_EQ(0, rc); EXPECT_EQ(0, cras_alsa_mmap_get_whole_buffer_called); EXPECT_EQ(1, cras_iodev_fill_odev_zeros_called); EXPECT_EQ(280, cras_iodev_fill_odev_zeros_frames); EXPECT_EQ(280, aio.filled_zeros_for_draining); EXPECT_EQ(0, aio.is_free_running); } TEST_F(AlsaFreeRunTestSuite, EnterFreeRunNotDrainedYetNoNeedToFillZeros) { int rc, real_hw_level; // Device is not in free run state. There are still valid samples to play. // The number of valid samples is more than min_cb_level * 2. // No need to fill zeros. real_hw_level = 500; cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level; rc = no_stream(&aio.base, 1); EXPECT_EQ(0, rc); EXPECT_EQ(0, cras_alsa_mmap_get_whole_buffer_called); EXPECT_EQ(0, cras_iodev_fill_odev_zeros_called); EXPECT_EQ(0, aio.is_free_running); } TEST_F(AlsaFreeRunTestSuite, EnterFreeRunDrained) { int rc, real_hw_level; // Device is not in free run state. There are still valid samples to play. // The number of valid samples is less than filled zeros. // Should enter free run state and fill whole buffer with zeros. real_hw_level = 40; cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level; aio.filled_zeros_for_draining = 100; rc = no_stream(&aio.base, 1); EXPECT_EQ(0, rc); EXPECT_EQ(1, cras_alsa_mmap_get_whole_buffer_called); EXPECT_EQ(0, cras_iodev_fill_odev_zeros_called); EXPECT_EQ(1, aio.is_free_running); } TEST_F(AlsaFreeRunTestSuite, EnterFreeRunNoSamples) { int rc, real_hw_level; // Device is not in free run state. There is no sample to play. // Should enter free run state and fill whole buffer with zeros. real_hw_level = 0; cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level; rc = no_stream(&aio.base, 1); EXPECT_EQ(0, rc); EXPECT_EQ(1, cras_alsa_mmap_get_whole_buffer_called); EXPECT_EQ(0, cras_iodev_fill_odev_zeros_called); EXPECT_EQ(1, aio.is_free_running); } TEST_F(AlsaFreeRunTestSuite, OutputShouldWake) { aio.is_free_running = 1; EXPECT_EQ(0, output_should_wake(&aio.base)); aio.is_free_running = 0; aio.base.state = CRAS_IODEV_STATE_NO_STREAM_RUN; EXPECT_EQ(1, output_should_wake(&aio.base)); aio.base.state = CRAS_IODEV_STATE_NORMAL_RUN; EXPECT_EQ(1, output_should_wake(&aio.base)); aio.base.state = CRAS_IODEV_STATE_OPEN; EXPECT_EQ(0, output_should_wake(&aio.base)); } TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunNotInFreeRunMoreRemain) { int rc, real_hw_level; // Compare min_buffer_level + min_cb_level with valid samples left. // 240 + 512 < 900 - 100, so we will get 900 - 100 in appl_ptr_ahead. aio.is_free_running = 0; aio.filled_zeros_for_draining = 100; aio.base.min_buffer_level = 512; real_hw_level = 900; cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level; rc = no_stream(&aio.base, 0); EXPECT_EQ(0, rc); EXPECT_EQ(1, cras_alsa_resume_appl_ptr_called); EXPECT_EQ(800, cras_alsa_resume_appl_ptr_ahead); EXPECT_EQ(0, cras_iodev_fill_odev_zeros_frames); EXPECT_EQ(0, aio.is_free_running); EXPECT_EQ(0, aio.filled_zeros_for_draining); } TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunNotInFreeRunLessRemain) { int rc, real_hw_level; // Compare min_buffer_level + min_cb_level with valid samples left. // 240 + 256 > 400 - 500, so we will get 240 + 256 in appl_ptr_ahead. // And it will fill 240 + 256 - 400 = 96 zeros frames into device. aio.is_free_running = 0; aio.filled_zeros_for_draining = 500; aio.base.min_buffer_level = 256; real_hw_level = 400; cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level; rc = no_stream(&aio.base, 0); EXPECT_EQ(0, rc); EXPECT_EQ(1, cras_alsa_resume_appl_ptr_called); EXPECT_EQ(aio.base.min_buffer_level + aio.base.min_cb_level, cras_alsa_resume_appl_ptr_ahead); EXPECT_EQ(96, cras_iodev_fill_odev_zeros_frames); EXPECT_EQ(0, aio.is_free_running); EXPECT_EQ(0, aio.filled_zeros_for_draining); } TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunInFreeRun) { int rc; aio.is_free_running = 1; aio.filled_zeros_for_draining = 100; aio.base.min_buffer_level = 512; rc = no_stream(&aio.base, 0); EXPECT_EQ(0, rc); EXPECT_EQ(1, cras_alsa_resume_appl_ptr_called); EXPECT_EQ(aio.base.min_buffer_level + aio.base.min_cb_level, cras_alsa_resume_appl_ptr_ahead); EXPECT_EQ(0, aio.is_free_running); EXPECT_EQ(0, aio.filled_zeros_for_draining); } // Reuse AlsaFreeRunTestSuite for output underrun handling because they are // similar. TEST_F(AlsaFreeRunTestSuite, OutputUnderrun) { int rc; int16_t *zeros; aio.num_underruns = 0; // Ask alsa_io to handle output underrun. rc = alsa_output_underrun(&aio.base); EXPECT_EQ(0, rc); EXPECT_EQ(1, aio.num_underruns); // mmap buffer should be filled with zeros. zeros = (int16_t *)calloc(BUFFER_SIZE * 2, sizeof(*zeros)); EXPECT_EQ(0, memcmp(zeros, cras_alsa_mmap_begin_buffer, BUFFER_SIZE * 2 * 2)); // appl_ptr should be moved to min_buffer_level + min_cb_level ahead of // hw_ptr. EXPECT_EQ(1, cras_alsa_resume_appl_ptr_called); EXPECT_EQ(aio.base.min_buffer_level + aio.base.min_cb_level, cras_alsa_resume_appl_ptr_ahead); free(zeros); } TEST(AlsaHotwordNode, HotwordTriggeredSendMessage) { struct cras_iodev *iodev; struct cras_audio_format format; struct alsa_input_node alsa_node; struct cras_ionode *node = &alsa_node.base; int rc; ResetStubData(); iodev = alsa_iodev_create_with_default_parameters( 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL, CRAS_STREAM_INPUT); format.frame_rate = 16000; format.num_channels = 1; cras_iodev_set_format(iodev, &format); memset(&alsa_node, 0, sizeof(alsa_node)); node->dev = iodev; strcpy(node->name, "Wake on Voice"); set_node_initial_state(node, ALSA_CARD_TYPE_INTERNAL); EXPECT_EQ(CRAS_NODE_TYPE_HOTWORD, node->type); iodev->active_node = node; iodev->open_dev(iodev); rc = iodev->configure_dev(iodev); free(fake_format); ASSERT_EQ(0, rc); ASSERT_NE(reinterpret_cast<thread_callback>(NULL), audio_thread_cb); audio_thread_cb(audio_thread_cb_data); EXPECT_EQ(1, hotword_send_triggered_msg_called); alsa_iodev_destroy(iodev); } } // namespace int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); openlog(NULL, LOG_PERROR, LOG_USER); return RUN_ALL_TESTS(); } // Stubs extern "C" { // From iodev. int cras_iodev_list_add_output(struct cras_iodev *output) { return 0; } int cras_iodev_list_rm_output(struct cras_iodev *dev) { return 0; } int cras_iodev_list_add_input(struct cras_iodev *input) { return 0; } int cras_iodev_list_rm_input(struct cras_iodev *dev) { return 0; } char *cras_iodev_list_get_hotword_models(cras_node_id_t node_id) { return NULL; } int cras_iodev_list_set_hotword_model(cras_node_id_t node_id, const char *model_name) { return 0; } int cras_iodev_list_suspend_hotword_streams() { return 0; } int cras_iodev_list_resume_hotword_stream() { return 0; } struct audio_thread *cras_iodev_list_get_audio_thread() { return NULL; } // From alsa helper. int cras_alsa_set_channel_map(snd_pcm_t *handle, struct cras_audio_format *fmt) { return 0; } int cras_alsa_get_channel_map(snd_pcm_t *handle, struct cras_audio_format *fmt) { return 0; } int cras_alsa_pcm_open(snd_pcm_t **handle, const char *dev, snd_pcm_stream_t stream) { *handle = (snd_pcm_t *)0x24; cras_alsa_open_called++; return 0; } int cras_alsa_pcm_close(snd_pcm_t *handle) { return 0; } int cras_alsa_pcm_start(snd_pcm_t *handle) { cras_alsa_start_called++; return 0; } int cras_alsa_pcm_drain(snd_pcm_t *handle) { return 0; } int cras_alsa_fill_properties(snd_pcm_t *handle, size_t **rates, size_t **channel_counts, snd_pcm_format_t **formats) { *rates = (size_t *)malloc(sizeof(**rates) * 3); (*rates)[0] = 44100; (*rates)[1] = 48000; (*rates)[2] = 0; *channel_counts = (size_t *)malloc(sizeof(**channel_counts) * 2); (*channel_counts)[0] = 2; (*channel_counts)[1] = 0; *formats = (snd_pcm_format_t *)malloc(sizeof(**formats) * 2); (*formats)[0] = SND_PCM_FORMAT_S16_LE; (*formats)[1] = (snd_pcm_format_t)0; cras_alsa_fill_properties_called++; return 0; } int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format, snd_pcm_uframes_t *buffer_size, int period_wakeup, unsigned int dma_period_time) { return 0; } int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp) { return 0; } int cras_alsa_get_avail_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size, snd_pcm_uframes_t severe_underrun_frames, const char* dev_name, snd_pcm_uframes_t *used, struct timespec *tstamp) { *used = cras_alsa_get_avail_frames_avail; clock_gettime(CLOCK_MONOTONIC_RAW, tstamp); return cras_alsa_get_avail_frames_ret; } int cras_alsa_get_delay_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size, snd_pcm_sframes_t *delay) { *delay = 0; return 0; } int cras_alsa_mmap_begin(snd_pcm_t *handle, unsigned int format_bytes, uint8_t **dst, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames) { *dst = cras_alsa_mmap_begin_buffer; *frames = cras_alsa_mmap_begin_frames; return 0; } int cras_alsa_mmap_commit(snd_pcm_t *handle, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames) { return 0; } int cras_alsa_attempt_resume(snd_pcm_t *handle) { cras_alsa_attempt_resume_called++; return 0; } // ALSA stubs. int snd_pcm_format_physical_width(snd_pcm_format_t format) { return 16; } snd_pcm_state_t snd_pcm_state(snd_pcm_t *handle) { return snd_pcm_state_ret; } const char *snd_strerror(int errnum) { return "Alsa Error in UT"; } struct mixer_control *cras_alsa_mixer_get_control_for_section( struct cras_alsa_mixer *cras_mixer, const struct ucm_section *section) { cras_alsa_mixer_get_control_for_section_called++; return cras_alsa_mixer_get_control_for_section_return_value; } const char *cras_alsa_mixer_get_control_name( const struct mixer_control *control) { ControlNameMap::iterator it; cras_alsa_mixer_get_control_name_called++; it = cras_alsa_mixer_get_control_name_values.find(control); if (it == cras_alsa_mixer_get_control_name_values.end()) return ""; return it->second.c_str(); } // From system_state. size_t cras_system_get_volume() { sys_get_volume_called++; return sys_get_volume_return_value; } long cras_system_get_capture_gain() { sys_get_capture_gain_called++; return sys_get_capture_gain_return_value; } int cras_system_get_mute() { sys_get_mute_called++; return sys_get_mute_return_value; } int cras_system_get_capture_mute() { sys_get_capture_mute_called++; return sys_get_capture_mute_return_value; } void cras_system_set_volume_limits(long min, long max) { sys_set_volume_limits_called++; } void cras_system_set_capture_gain_limits(long min, long max) { cras_system_set_capture_gain_limits_set_value[0] = min; cras_system_set_capture_gain_limits_set_value[1] = max; sys_set_capture_gain_limits_called++; } // From cras_alsa_mixer. void cras_alsa_mixer_set_dBFS(struct cras_alsa_mixer *m, long dB_level, struct mixer_control *output) { alsa_mixer_set_dBFS_called++; alsa_mixer_set_dBFS_value = dB_level; alsa_mixer_set_dBFS_output = output; } void cras_alsa_mixer_set_mute(struct cras_alsa_mixer *cras_mixer, int muted, struct mixer_control *mixer_output) { alsa_mixer_set_mute_called++; alsa_mixer_set_mute_value = muted; alsa_mixer_set_mute_output = mixer_output; } long cras_alsa_mixer_get_dB_range(struct cras_alsa_mixer *cras_mixer) { alsa_mixer_get_dB_range_called++; return alsa_mixer_get_dB_range_value; } long cras_alsa_mixer_get_output_dB_range( struct mixer_control *mixer_output) { alsa_mixer_get_output_dB_range_called++; return alsa_mixer_get_output_dB_range_value; } void cras_alsa_mixer_set_capture_dBFS(struct cras_alsa_mixer *m, long dB_level, struct mixer_control *mixer_input) { alsa_mixer_set_capture_dBFS_called++; alsa_mixer_set_capture_dBFS_value = dB_level; alsa_mixer_set_capture_dBFS_input = mixer_input; } void cras_alsa_mixer_set_capture_mute(struct cras_alsa_mixer *m, int mute, struct mixer_control *mixer_input) { alsa_mixer_set_capture_mute_called++; alsa_mixer_set_capture_mute_value = mute; alsa_mixer_set_capture_mute_input = mixer_input; } void cras_alsa_mixer_list_outputs(struct cras_alsa_mixer *cras_mixer, cras_alsa_mixer_control_callback cb, void *callback_arg) { cras_alsa_mixer_list_outputs_called++; for (size_t i = 0; i < cras_alsa_mixer_list_outputs_outputs_length; i++) { cb(cras_alsa_mixer_list_outputs_outputs[i], callback_arg); } } void cras_alsa_mixer_list_inputs(struct cras_alsa_mixer *cras_mixer, cras_alsa_mixer_control_callback cb, void *callback_arg) { cras_alsa_mixer_list_inputs_called++; for (size_t i = 0; i < cras_alsa_mixer_list_inputs_outputs_length; i++) { cb(cras_alsa_mixer_list_inputs_outputs[i], callback_arg); } } int cras_alsa_mixer_set_output_active_state( struct mixer_control *output, int active) { cras_alsa_mixer_set_output_active_state_called++; cras_alsa_mixer_set_output_active_state_outputs.push_back(output); cras_alsa_mixer_set_output_active_state_values.push_back(active); return 0; } void cras_volume_curve_destroy(struct cras_volume_curve *curve) { } long cras_alsa_mixer_get_minimum_capture_gain(struct cras_alsa_mixer *cmix, struct mixer_control *mixer_input) { cras_alsa_mixer_get_minimum_capture_gain_called++; cras_alsa_mixer_get_minimum_capture_gain_mixer_input = mixer_input; return cras_alsa_mixer_get_minimum_capture_gain_ret_value; } long cras_alsa_mixer_get_maximum_capture_gain(struct cras_alsa_mixer *cmix, struct mixer_control *mixer_input) { cras_alsa_mixer_get_maximum_capture_gain_called++; cras_alsa_mixer_get_maximum_capture_gain_mixer_input = mixer_input; return cras_alsa_mixer_get_maximum_capture_gain_ret_value; } int cras_alsa_mixer_has_main_volume(const struct cras_alsa_mixer *cras_mixer) { return 1; } int cras_alsa_mixer_has_volume(const struct mixer_control *mixer_control) { return 1; } // From cras_alsa_jack struct cras_alsa_jack_list *cras_alsa_jack_list_create( unsigned int card_index, const char *card_name, unsigned int device_index, int check_gpio_jack, struct cras_alsa_mixer *mixer, struct cras_use_case_mgr *ucm, snd_hctl_t *hctl, enum CRAS_STREAM_DIRECTION direction, jack_state_change_callback *cb, void *cb_data) { cras_alsa_jack_list_create_called++; cras_alsa_jack_list_create_cb = cb; cras_alsa_jack_list_create_cb_data = cb_data; return (struct cras_alsa_jack_list *)0xfee; } int cras_alsa_jack_list_find_jacks_by_name_matching( struct cras_alsa_jack_list *jack_list) { cras_alsa_jack_list_find_jacks_by_name_matching_called++; return 0; } int cras_alsa_jack_list_add_jack_for_section( struct cras_alsa_jack_list *jack_list, struct ucm_section *ucm_section, struct cras_alsa_jack **result_jack) { cras_alsa_jack_list_add_jack_for_section_called++; if (result_jack) *result_jack = cras_alsa_jack_list_add_jack_for_section_result_jack; return 0; } void cras_alsa_jack_list_destroy(struct cras_alsa_jack_list *jack_list) { cras_alsa_jack_list_destroy_called++; } int cras_alsa_jack_list_has_hctl_jacks(struct cras_alsa_jack_list *jack_list) { return cras_alsa_jack_list_has_hctl_jacks_return_val; } void cras_alsa_jack_list_report(const struct cras_alsa_jack_list *jack_list) { } void cras_alsa_jack_enable_ucm(const struct cras_alsa_jack *jack, int enable) { cras_alsa_jack_enable_ucm_called++; } const char *cras_alsa_jack_get_name(const struct cras_alsa_jack *jack) { cras_alsa_jack_get_name_called++; return cras_alsa_jack_get_name_ret_value; } const char *cras_alsa_jack_get_dsp_name(const struct cras_alsa_jack *jack) { cras_alsa_jack_get_dsp_name_called++; return jack ? cras_alsa_jack_get_dsp_name_value : NULL; } const char *ucm_get_dsp_name_default(struct cras_use_case_mgr *mgr, int direction) { ucm_get_dsp_name_default_called++; if (ucm_get_dsp_name_default_value) return strdup(ucm_get_dsp_name_default_value); else return NULL; } struct mixer_control *cras_alsa_jack_get_mixer_output( const struct cras_alsa_jack *jack) { return cras_alsa_jack_get_mixer_output_ret; } struct mixer_control *cras_alsa_jack_get_mixer_input( const struct cras_alsa_jack *jack) { return cras_alsa_jack_get_mixer_input_ret; } int ucm_set_enabled( struct cras_use_case_mgr *mgr, const char *dev, int enabled) { ucm_set_enabled_called++; return 0; } char *ucm_get_flag(struct cras_use_case_mgr *mgr, const char *flag_name) { if ((!strcmp(flag_name, "AutoUnplugInputNode") && auto_unplug_input_node_ret) || (!strcmp(flag_name, "AutoUnplugOutputNode") && auto_unplug_output_node_ret)) { char *ret = (char *)malloc(8); snprintf(ret, 8, "%s", "1"); return ret; } return NULL; } char *ucm_get_mic_positions(struct cras_use_case_mgr *mgr) { return NULL; } int ucm_swap_mode_exists(struct cras_use_case_mgr *mgr) { return ucm_swap_mode_exists_ret_value; } int ucm_enable_swap_mode(struct cras_use_case_mgr *mgr, const char *node_name, int enable) { ucm_enable_swap_mode_called++; return ucm_enable_swap_mode_ret_value; } int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr, unsigned int *level) { *level = 0; return 0; } unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr) { return ucm_get_enable_htimestamp_flag_ret; } unsigned int ucm_get_disable_software_volume(struct cras_use_case_mgr *mgr) { return 0; } int ucm_get_min_software_gain(struct cras_use_case_mgr *mgr, const char *dev, long *gain) { ucm_get_min_software_gain_called++; *gain = ucm_get_min_software_gain_value; return ucm_get_min_software_gain_ret_value; } int ucm_get_max_software_gain(struct cras_use_case_mgr *mgr, const char *dev, long *gain) { ucm_get_max_software_gain_called++; *gain = ucm_get_max_software_gain_value; return ucm_get_max_software_gain_ret_value; } char *ucm_get_hotword_models(struct cras_use_case_mgr *mgr) { return NULL; } int ucm_set_hotword_model(struct cras_use_case_mgr *mgr, const char *model) { return 0; } unsigned int ucm_get_dma_period_for_dev(struct cras_use_case_mgr *mgr, const char *dev) { ucm_get_dma_period_for_dev_called++; return ucm_get_dma_period_for_dev_ret; } int ucm_get_sample_rate_for_dev(struct cras_use_case_mgr *mgr, const char *dev, enum CRAS_STREAM_DIRECTION direction) { return -EINVAL; } int ucm_get_capture_chmap_for_dev(struct cras_use_case_mgr *mgr, const char *dev, int8_t *channel_layout) { return -EINVAL; } int ucm_get_preempt_hotword(struct cras_use_case_mgr *mgr, const char *dev) { return 0; } struct cras_volume_curve *cras_volume_curve_create_default() { return &default_curve; } struct cras_volume_curve *cras_card_config_get_volume_curve_for_control( const struct cras_card_config *card_config, const char *control_name) { VolCurveMap::iterator it; cras_card_config_get_volume_curve_for_control_called++; if (!control_name) return NULL; it = cras_card_config_get_volume_curve_vals.find(control_name); if (it == cras_card_config_get_volume_curve_vals.end()) return NULL; return it->second; } void cras_iodev_free_format(struct cras_iodev *iodev) { } int cras_iodev_set_format(struct cras_iodev *iodev, const struct cras_audio_format *fmt) { fake_format = (struct cras_audio_format *)calloc( 1, sizeof(cras_audio_format)); // Copy the content of format from fmt into format of iodev. memcpy(fake_format, fmt, sizeof(cras_audio_format)); iodev->format = fake_format; return 0; } struct audio_thread *audio_thread_create() { return reinterpret_cast<audio_thread*>(0x323); } void audio_thread_destroy(audio_thread* thread) { } void cras_iodev_update_dsp(struct cras_iodev *iodev) { cras_iodev_update_dsp_called++; cras_iodev_update_dsp_name = iodev->dsp_name; } int cras_iodev_set_node_attr(struct cras_ionode *ionode, enum ionode_attr attr, int value) { cras_iodev_set_node_attr_called++; cras_iodev_set_node_attr_ionode = ionode; cras_iodev_set_node_attr_attr = attr; cras_iodev_set_node_attr_value = value; if (ionode && (attr == IONODE_ATTR_PLUGGED)) ionode->plugged = value; return 0; } void cras_iodev_add_node(struct cras_iodev *iodev, struct cras_ionode *node) { cras_iodev_add_node_called++; DL_APPEND(iodev->nodes, node); } void cras_iodev_rm_node(struct cras_iodev *iodev, struct cras_ionode *node) { DL_DELETE(iodev->nodes, node); } void cras_iodev_set_active_node(struct cras_iodev *iodev, struct cras_ionode *node) { iodev->active_node = node; } void cras_iodev_free_resources(struct cras_iodev *iodev) { cras_iodev_free_resources_called++; } void cras_alsa_jack_update_monitor_name(const struct cras_alsa_jack *jack, char *name_buf, unsigned int buf_size) { if (cras_alsa_jack_update_monitor_fake_name) strcpy(name_buf, cras_alsa_jack_update_monitor_fake_name); } void cras_alsa_jack_update_node_type(const struct cras_alsa_jack *jack, enum CRAS_NODE_TYPE *type) { cras_alsa_jack_update_node_type_called++; } const char *cras_alsa_jack_get_ucm_device(const struct cras_alsa_jack *jack) { return NULL; } int ucm_get_default_node_gain(struct cras_use_case_mgr *mgr, const char *dev, long *gain) { if (ucm_get_default_node_gain_values.find(dev) == ucm_get_default_node_gain_values.end()) return 1; *gain = ucm_get_default_node_gain_values[dev]; return 0; } void cras_iodev_init_audio_area(struct cras_iodev *iodev, int num_channels) { } void cras_iodev_free_audio_area(struct cras_iodev *iodev) { } int cras_iodev_reset_rate_estimator(const struct cras_iodev *iodev) { return 0; } int cras_iodev_frames_queued(struct cras_iodev *iodev, struct timespec *tstamp) { clock_gettime(CLOCK_MONOTONIC_RAW, tstamp); return cras_iodev_frames_queued_ret; } int cras_iodev_buffer_avail(struct cras_iodev *iodev, unsigned hw_level) { return cras_iodev_buffer_avail_ret; } int cras_iodev_fill_odev_zeros(struct cras_iodev *odev, unsigned int frames) { cras_iodev_fill_odev_zeros_called++; cras_iodev_fill_odev_zeros_frames = frames; return 0; } void cras_audio_area_config_buf_pointers(struct cras_audio_area *area, const struct cras_audio_format *fmt, uint8_t *base_buffer) { } void audio_thread_add_callback(int fd, thread_callback cb, void *data) { audio_thread_cb = cb; audio_thread_cb_data = data; } void audio_thread_rm_callback(int fd) { } int audio_thread_rm_callback_sync(struct audio_thread *thread, int fd) { return 0; } int cras_hotword_send_triggered_msg() { hotword_send_triggered_msg_called++; return 0; } int snd_pcm_poll_descriptors_count(snd_pcm_t *pcm) { return 1; } int snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space) { if (space >= 1) { pfds[0].events = POLLIN; pfds[0].fd = 99; } return 0; } int is_utf8_string(const char* string) { return is_utf8_string_ret_value; } int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst) { snd_pcm_uframes_t offset, frames; cras_alsa_mmap_get_whole_buffer_called++; return cras_alsa_mmap_begin(handle, 0, dst, &offset, &frames); } int cras_alsa_resume_appl_ptr(snd_pcm_t *handle, snd_pcm_uframes_t ahead) { cras_alsa_resume_appl_ptr_called++; cras_alsa_resume_appl_ptr_ahead = ahead; return 0; } int cras_iodev_default_no_stream_playback(struct cras_iodev *odev, int enable) { return 0; } enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev) { return iodev->state; } int cras_iodev_dsp_set_swap_mode_for_node(struct cras_iodev *iodev, struct cras_ionode *node, int enable) { cras_iodev_dsp_set_swap_mode_for_node_called++; return 0; } struct cras_ramp* cras_ramp_create() { return (struct cras_ramp*)0x1; } }