blob: b3059a23e7625473142f21b41288ce516bdc34ad [file] [log] [blame]
// 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 <stdio.h>
#include <syslog.h>
#include <map>
#include <vector>
extern "C" {
#include "cras_alsa_mixer.h"
#include "cras_iodev.h"
#include "cras_shm.h"
#include "cras_system_state.h"
#include "cras_types.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 bool cras_alsa_support_8_channels;
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 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 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_pcm_name[] = "TestPCM";
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_plugged_ionode;
static size_t cras_iodev_set_node_plugged_called;
static int cras_iodev_set_node_plugged_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;
typedef std::map<const char*, std::string> DspNameMap;
static size_t ucm_get_dsp_name_for_dev_called;
static DspNameMap ucm_get_dsp_name_for_dev_values;
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 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 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 std::map<std::string, long> ucm_get_intrinsic_sensitivity_values;
static thread_callback audio_thread_cb;
static void* audio_thread_cb_data;
static int hotword_send_triggered_msg_called;
static struct timespec clock_gettime_retspec;
static unsigned cras_iodev_reset_rate_estimator_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;
cras_alsa_support_8_channels = false;
sys_get_volume_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;
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_plugged_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_for_dev_called = 0;
ucm_get_dsp_name_for_dev_values.clear();
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;
cras_card_config_get_volume_curve_for_control_called = 0;
cras_card_config_get_volume_curve_vals.clear();
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;
fake_get_dBFS_volume_curve_val = NULL;
cras_iodev_dsp_set_swap_mode_for_node_called = 0;
ucm_get_default_node_gain_values.clear();
ucm_get_intrinsic_sensitivity_values.clear();
cras_iodev_reset_rate_estimator_called = 0;
}
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_pcm_name,
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);
/* Call cras_alsa_fill_properties once on update_max_supported_channels. */
EXPECT_EQ(1, 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(1, cras_iodev_update_dsp_called);
EXPECT_EQ("", 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*)is_free_running, (void*)aio->base.is_free_running);
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(INTERNAL_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*)is_free_running, (void*)aio->base.is_free_running);
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*)is_free_running, (void*)aio->base.is_free_running);
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_MICROPHONE, 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*)is_free_running, (void*)aio->base.is_free_running);
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_plugged_called);
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_plugged_called);
/* No extra gain applied. */
ASSERT_EQ(DEFAULT_CAPTURE_VOLUME_DBFS,
aio->base.active_node->intrinsic_sensitivity);
ASSERT_EQ(0, aio->base.active_node->capture_gain);
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));
/* Call open_dev once on update_max_supported_channels. */
EXPECT_EQ(1, cras_alsa_open_called);
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->free_running = 1;
aio->filled_zeros_for_draining = 512;
iodev->open_dev(iodev);
EXPECT_EQ(2, cras_alsa_open_called);
iodev->configure_dev(iodev);
EXPECT_EQ(2, 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_plugged_called);
EXPECT_EQ(0, aio->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_plugged_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_plugged_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_plugged_called);
EXPECT_EQ(1, iodev->active_node->plugged);
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, SoftwareGainIntrinsicSensitivity) {
struct cras_iodev* iodev;
struct cras_use_case_mgr* const fake_ucm = (struct cras_use_case_mgr*)3;
long intrinsic_sensitivity = -2700;
ResetStubData();
// Set intrinsic sensitivity to -2700 * 0.01 dBFS/Pa.
ucm_get_intrinsic_sensitivity_values[INTERNAL_MICROPHONE] =
intrinsic_sensitivity;
// 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));
ASSERT_EQ(intrinsic_sensitivity, iodev->active_node->intrinsic_sensitivity);
ASSERT_EQ(DEFAULT_CAPTURE_VOLUME_DBFS - intrinsic_sensitivity,
iodev->active_node->capture_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);
/* Call cras_alsa_fill_properties once on update_max_supported_channels. */
EXPECT_EQ(1, 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_plugged_called);
EXPECT_EQ(1, cras_iodev_set_node_plugged_value);
cras_alsa_jack_list_create_cb(NULL, 0, cras_alsa_jack_list_create_cb_data);
EXPECT_EQ(2, cras_iodev_set_node_plugged_called);
EXPECT_EQ(0, cras_iodev_set_node_plugged_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);
/* Call cras_alsa_fill_properties once on update_max_supported_channels. */
EXPECT_EQ(1, 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_plugged_called);
EXPECT_EQ(1, cras_iodev_set_node_plugged_value);
cras_alsa_jack_list_create_cb(NULL, 0, cras_alsa_jack_list_create_cb_data);
EXPECT_EQ(2, cras_iodev_set_node_plugged_called);
EXPECT_EQ(0, cras_iodev_set_node_plugged_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);
/* Call cras_alsa_fill_properties once on update_max_supported_channels. */
EXPECT_EQ(1, 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, 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 default_node_gain = 1000;
ResetStubData();
// Set default node gain to -1000 * 0.01 dB.
ucm_get_default_node_gain_values[INTERNAL_MICROPHONE] = 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);
cras_alsa_mixer_get_minimum_capture_gain_ret_value = 0;
cras_alsa_mixer_get_maximum_capture_gain_ret_value = 2000;
iodev->open_dev(iodev);
iodev->configure_dev(iodev);
iodev->close_dev(iodev);
// Hardware gain is in the hardware gain range and set to 1000 * 0.01 dB.
EXPECT_EQ(default_node_gain, alsa_mixer_set_capture_dBFS_value);
// Check we do respect the hardware maximum capture gain.
cras_alsa_mixer_get_maximum_capture_gain_ret_value = 500;
iodev->open_dev(iodev);
iodev->configure_dev(iodev);
iodev->close_dev(iodev);
EXPECT_EQ(500, 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();
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);
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->active_node->capture_gain = 1000;
iodev->open_dev(iodev);
iodev->configure_dev(iodev);
iodev->close_dev(iodev);
/* Hardware gain is set to 1000 * 0.01 dB as got from catpure_gain.*/
EXPECT_EQ(0, alsa_mixer_set_capture_dBFS_value);
alsa_iodev_destroy(iodev);
free(fake_format);
}
TEST(AlsaIoInit, OpenCaptureSetCaptureGainWithDefaultUsbDevice) {
struct cras_iodev* iodev;
struct cras_audio_format format;
iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_USB,
0, fake_mixer, fake_config,
NULL, 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);
iodev->active_node->intrinsic_sensitivity = DEFAULT_CAPTURE_VOLUME_DBFS;
iodev->active_node->capture_gain = 0;
ResetStubData();
iodev->open_dev(iodev);
iodev->configure_dev(iodev);
EXPECT_EQ(1, sys_get_capture_mute_called);
EXPECT_EQ(1, alsa_mixer_set_capture_mute_called);
/* Not change mixer controls for USB devices without UCM config. */
EXPECT_EQ(0, alsa_mixer_set_capture_dBFS_called);
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();
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_for_dev_called);
EXPECT_STREQ("", cras_iodev_update_dsp_name);
alsa_iodev_destroy((struct cras_iodev*)aio);
}
TEST(AlsaIoInit, DspName) {
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_for_dev_values[DEFAULT] = "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_for_dev_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;
static const char* jack_name = "jack";
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));
EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
EXPECT_EQ(1, ucm_get_dsp_name_for_dev_called);
EXPECT_EQ(1, cras_iodev_update_dsp_called);
EXPECT_STREQ("", cras_iodev_update_dsp_name);
cras_alsa_jack_get_name_ret_value = jack_name;
ucm_get_dsp_name_for_dev_values[jack_name] = "override_dsp";
// 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);
EXPECT_EQ(2, ucm_get_dsp_name_for_dev_called);
// Mark the jack node as active.
alsa_iodev_set_active_node(&aio->base, aio->base.nodes->next, 1);
EXPECT_EQ(2, ucm_get_dsp_name_for_dev_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(2, ucm_get_dsp_name_for_dev_called);
EXPECT_EQ(3, cras_iodev_update_dsp_called);
EXPECT_STREQ("", 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(AlsaIoInit, MaxSupportedChannels) {
struct alsa_io* aio;
struct cras_alsa_mixer* const fake_mixer = (struct cras_alsa_mixer*)2;
int i;
// i = 0: cras_alsa_support_8_channels is false, support 2 channels only.
// i = 1: cras_alsa_support_8_channels is true, support up to 8 channels.
for (i = 0; i < 2; i++) {
ResetStubData();
cras_alsa_support_8_channels = (bool)i;
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));
/* Call cras_alsa_fill_properties once on update_max_supported_channels. */
EXPECT_EQ(1, cras_alsa_fill_properties_called);
uint32_t max_channels = (cras_alsa_support_8_channels) ? 8 : 2;
EXPECT_EQ(max_channels, aio->base.info.max_supported_channels);
alsa_iodev_destroy((struct cras_iodev*)aio);
EXPECT_EQ(1, cras_iodev_free_resources_called);
}
}
// 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(1, 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, "hw:0,1", 0, -1, 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", "hw:0.1", 0, -1, 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_plugged_ionode->name, HEADPHONE);
jack_output_plug_event(reinterpret_cast<struct cras_alsa_jack*>(20), 0, aio);
EXPECT_STREQ(cras_iodev_set_node_plugged_ionode->name, "Line Out");
alsa_iodev_destroy(iodev);
}
TEST(AlsaOutputNode, MaxSupportedChannels) {
struct cras_use_case_mgr* const fake_ucm = (struct cras_use_case_mgr*)3;
struct cras_iodev* iodev;
struct ucm_section* section;
int i;
// i = 0: cras_alsa_support_8_channels is false, support 2 channels only.
// i = 1: cras_alsa_support_8_channels is true, support up to 8 channels.
for (i = 0; i < 2; i++) {
ResetStubData();
cras_alsa_support_8_channels = (bool)i;
// 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);
// Node without controls or jacks.
section = ucm_section_create(INTERNAL_SPEAKER, "hw:0,1", 1, -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));
ucm_section_free_list(section);
// Complete initialization, and make first node active.
alsa_iodev_ucm_complete_init(iodev);
/* Call cras_alsa_fill_properties once on update_max_supported_channels. */
EXPECT_EQ(1, cras_alsa_fill_properties_called);
uint32_t max_channels = (cras_alsa_support_8_channels) ? 8 : 2;
EXPECT_EQ(max_channels, iodev->info.max_supported_channels);
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, "hw:0,1", 0, -1,
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, "hw:0,2", 0, -1, 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_plugged_called);
// Complete initialization, and make first node active.
cras_alsa_support_8_channels = false; // Support 2 channels only.
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);
/* Call cras_alsa_fill_properties once on update_max_supported_channels. */
EXPECT_EQ(1, cras_alsa_fill_properties_called);
EXPECT_EQ(2, iodev->info.max_supported_channels);
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_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_support_8_channels = true; // Support up to 8 channels.
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_plugged_called);
/* Headphone plug event shouldn't trigger update_max_supported_channels. */
EXPECT_EQ(0, cras_alsa_fill_properties_called);
EXPECT_EQ(2, iodev->info.max_supported_channels);
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, "hw:0,1", 1, -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, "hw:0,1", 0, -1, 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;
long intrinsic_sensitivity = -2700;
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_MICROPHONE;
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];
section = ucm_section_create(INTERNAL_MICROPHONE, "hw:0,1", 0, -1,
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;
// Set intrinsic sensitivity to enable software gain.
ucm_get_intrinsic_sensitivity_values[MIC] = intrinsic_sensitivity;
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, "hw:0,2", 0, -1, 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 unknown 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_plugged_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_plugged_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(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, alsa_mixer_set_capture_mute_called);
ASSERT_EQ(DEFAULT_CAPTURE_VOLUME_DBFS - intrinsic_sensitivity,
iodev->active_node->capture_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, "hw:0,1", 1, -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, "hw:0,1", 0, -1, 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(AlsaLoopback, InitializePlayback) {
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 - Alsa Loopback";
struct mixer_control* outputs[1];
struct ucm_section* section;
ResetStubData();
outputs[0] = reinterpret_cast<struct mixer_control*>(3);
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]] = LOOPBACK_PLAYBACK;
// 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);
// Add node.
section = ucm_section_create(LOOPBACK_PLAYBACK, "hw:0,1", 0, -1,
CRAS_STREAM_OUTPUT, jack_name, NULL);
ucm_section_set_mixer_name(section, LOOPBACK_PLAYBACK);
cras_alsa_jack_list_add_jack_for_section_result_jack = NULL;
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);
// Complete initialization, and check the loopback playback node is plugged as
// the active node.
alsa_iodev_ucm_complete_init(iodev);
EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
ASSERT_NE(aio->base.active_node, (void*)NULL);
EXPECT_STREQ(LOOPBACK_PLAYBACK, aio->base.active_node->name);
EXPECT_EQ(1, aio->base.active_node->plugged);
alsa_iodev_destroy(iodev);
}
TEST(AlsaLoopback, InitializeCapture) {
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 - Alsa Loopback";
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(LOOPBACK_CAPTURE, "hw:0,1", 0, -1,
CRAS_STREAM_INPUT, jack_name, NULL);
ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
ucm_section_free_list(section);
// Complete initialization, and check the loopback capture node is plugged as
// the active node.
alsa_iodev_ucm_complete_init(iodev);
EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream);
ASSERT_NE(aio->base.active_node, (void*)NULL);
EXPECT_STREQ(LOOPBACK_CAPTURE, aio->base.active_node->name);
EXPECT_EQ(1, aio->base.active_node->plugged);
alsa_iodev_destroy(iodev);
}
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, INTERNAL_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_MICROPHONE);
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, INTERNAL_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_] = INTERNAL_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,
};
// 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);
// These settings should be placed after plugging jacks to make it safer.
// If is HDMI jack, plug event will trigger update_max_supported_channels()
// and do open_dev() and close_dev() once. close_dev() will perform alsa_io
// cleanup.
// Headphone jack won't trigger, but we still place here due to coherence.
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;
// 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;
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.output_underrun = alsa_output_underrun;
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.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;
int fill_zeros_duration = 50;
// Device is not in free run state. There are still valid samples to play.
// In cras_alsa_io.c, we defined there are 50ms zeros to be filled.
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(fmt_.frame_rate / 1000 * fill_zeros_duration,
cras_iodev_fill_odev_zeros_frames);
EXPECT_EQ(fmt_.frame_rate / 1000 * fill_zeros_duration,
aio.filled_zeros_for_draining);
EXPECT_EQ(0, aio.free_running);
}
TEST_F(AlsaFreeRunTestSuite, EnterFreeRunNotDrainedYetFillZerosExceedBuffer) {
int rc, real_hw_level;
// Device is not in free run state. There are still valid samples to play.
// If frames avail is smaller than 50ms(48 * 50 = 2400 zeros), only fill
// zeros until buffer size.
real_hw_level = 7000;
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(1, cras_iodev_fill_odev_zeros_called);
EXPECT_EQ(cras_alsa_get_avail_frames_avail,
cras_iodev_fill_odev_zeros_frames);
EXPECT_EQ(cras_alsa_get_avail_frames_avail, aio.filled_zeros_for_draining);
EXPECT_EQ(0, aio.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.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.free_running);
}
TEST_F(AlsaFreeRunTestSuite, IsFreeRunning) {
aio.free_running = 1;
EXPECT_EQ(1, is_free_running(&aio.base));
aio.free_running = 0;
EXPECT_EQ(0, is_free_running(&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.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.free_running);
EXPECT_EQ(0, aio.filled_zeros_for_draining);
EXPECT_EQ(1, cras_iodev_reset_rate_estimator_called);
}
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.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.free_running);
EXPECT_EQ(0, aio.filled_zeros_for_draining);
EXPECT_EQ(1, cras_iodev_reset_rate_estimator_called);
}
TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunInFreeRun) {
int rc;
aio.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.free_running);
EXPECT_EQ(0, aio.filled_zeros_for_draining);
EXPECT_EQ(1, cras_iodev_reset_rate_estimator_called);
}
// Reuse AlsaFreeRunTestSuite for output underrun handling because they are
// similar.
TEST_F(AlsaFreeRunTestSuite, OutputUnderrun) {
int rc;
int16_t* zeros;
snd_pcm_uframes_t offset;
// Ask alsa_io to handle output underrun.
rc = alsa_output_underrun(&aio.base);
EXPECT_EQ(0, rc);
// 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 + 1.5 * min_cb_level ahead of
// hw_ptr.
offset = aio.base.min_buffer_level + aio.base.min_cb_level +
aio.base.min_cb_level / 2;
EXPECT_EQ(1, cras_alsa_resume_appl_ptr_called);
EXPECT_EQ(offset, 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, HOTWORD_DEV);
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, POLLIN);
EXPECT_EQ(1, hotword_send_triggered_msg_called);
alsa_iodev_destroy(iodev);
}
TEST(AlsaGetValidFrames, GetValidFramesNormalState) {
struct cras_iodev* iodev;
struct alsa_io* aio;
struct timespec tstamp;
int rc;
ResetStubData();
iodev = alsa_iodev_create_with_default_parameters(
0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL,
CRAS_STREAM_OUTPUT);
aio = (struct alsa_io*)iodev;
aio->free_running = 0;
aio->filled_zeros_for_draining = 200;
cras_alsa_get_avail_frames_avail = iodev->buffer_size - 500;
cras_alsa_get_avail_frames_ret = 0;
clock_gettime_retspec.tv_sec = 123;
clock_gettime_retspec.tv_nsec = 321;
rc = iodev->get_valid_frames(iodev, &tstamp);
EXPECT_EQ(rc, 300);
EXPECT_EQ(tstamp.tv_sec, clock_gettime_retspec.tv_sec);
EXPECT_EQ(tstamp.tv_nsec, clock_gettime_retspec.tv_nsec);
alsa_iodev_destroy(iodev);
}
TEST(AlsaGetValidFrames, GetValidFramesFreeRunning) {
struct cras_iodev* iodev;
struct alsa_io* aio;
struct timespec tstamp;
int rc;
ResetStubData();
iodev = alsa_iodev_create_with_default_parameters(
0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL,
CRAS_STREAM_OUTPUT);
aio = (struct alsa_io*)iodev;
aio->free_running = 1;
clock_gettime_retspec.tv_sec = 123;
clock_gettime_retspec.tv_nsec = 321;
rc = iodev->get_valid_frames(iodev, &tstamp);
EXPECT_EQ(rc, 0);
EXPECT_EQ(tstamp.tv_sec, clock_gettime_retspec.tv_sec);
EXPECT_EQ(tstamp.tv_nsec, clock_gettime_retspec.tv_nsec);
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;
if (cras_alsa_support_8_channels) { // Support up to 8 channels.
*channel_counts = (size_t*)malloc(sizeof(**channel_counts) * 6);
(*channel_counts)[0] = 6;
(*channel_counts)[1] = 4;
(*channel_counts)[2] = 2;
(*channel_counts)[3] = 1;
(*channel_counts)[4] = 8;
(*channel_counts)[5] = 0;
} else { // Support 2 channels only.
*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) {
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;
}
int cras_system_get_mute() {
sys_get_mute_called++;
return sys_get_mute_return_value;
}