blob: 1aa95491694d94f70f908e959404c82702847659 [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 <linux/input.h>
#include <poll.h>
#include <stdio.h>
#include <sys/param.h>
#include <syslog.h>
#include <deque>
#include <map>
#include <string>
#include <vector>
extern "C" {
#include "cras_alsa_jack.h"
#include "cras_alsa_ucm_section.h"
#include "cras_gpio_jack.h"
#include "cras_tm.h"
#include "cras_types.h"
#include "cras_util.h"
}
namespace {
#define BITS_PER_BYTE (8)
#define BITS_PER_LONG (sizeof(long) * BITS_PER_BYTE)
#define NBITS(x) ((((x)-1) / BITS_PER_LONG) + 1)
#define OFF(x) ((x) % BITS_PER_LONG)
#define BIT(x) (1UL << OFF(x))
#define LONG(x) ((x) / BITS_PER_LONG)
#define IS_BIT_SET(bit, array) !!((array[LONG(bit)]) & (1UL << OFF(bit)))
static int fake_jack_cb_plugged;
static void* fake_jack_cb_data;
static size_t fake_jack_cb_called;
unsigned int snd_hctl_elem_get_device_return_val;
unsigned int snd_hctl_elem_get_device_called;
static size_t snd_hctl_first_elem_called;
static snd_hctl_elem_t* snd_hctl_first_elem_return_val;
static size_t snd_hctl_elem_next_called;
std::deque<snd_hctl_elem_t*> snd_hctl_elem_next_ret_vals;
std::deque<snd_hctl_elem_t*> snd_hctl_elem_next_ret_vals_poped;
static size_t snd_hctl_elem_get_name_called;
static size_t snd_hctl_elem_set_callback_called;
static snd_hctl_elem_t* snd_hctl_elem_set_callback_obj;
static snd_hctl_elem_callback_t snd_hctl_elem_set_callback_value;
static size_t snd_hctl_find_elem_called;
static std::vector<snd_hctl_elem_t*> snd_hctl_find_elem_return_vals;
static std::map<std::string, size_t> snd_ctl_elem_id_set_name_map;
static size_t cras_system_add_select_fd_called;
static std::vector<int> cras_system_add_select_fd_values;
static size_t cras_system_rm_select_fd_called;
static std::vector<int> cras_system_rm_select_fd_values;
static size_t snd_hctl_elem_set_callback_private_called;
static void* snd_hctl_elem_set_callback_private_value;
static size_t snd_hctl_elem_get_hctl_called;
static snd_hctl_t* snd_hctl_elem_get_hctl_return_value;
static size_t snd_ctl_elem_value_get_boolean_called;
static int snd_ctl_elem_value_get_boolean_return_value;
static void* fake_jack_cb_arg;
static struct cras_alsa_mixer* fake_mixer;
static size_t cras_alsa_mixer_get_output_matching_name_called;
static size_t cras_alsa_mixer_get_input_matching_name_called;
static size_t cras_alsa_mixer_get_control_for_section_called;
static struct mixer_control*
cras_alsa_mixer_get_output_matching_name_return_value;
static struct mixer_control*
cras_alsa_mixer_get_input_matching_name_return_value;
static struct mixer_control*
cras_alsa_mixer_get_control_for_section_return_value;
static size_t gpio_switch_list_for_each_called;
static std::vector<std::string> gpio_switch_list_for_each_dev_paths;
static std::vector<std::string> gpio_switch_list_for_each_dev_names;
static size_t gpio_switch_open_called;
static size_t gpio_switch_eviocgsw_called;
static size_t gpio_switch_eviocgbit_called;
static unsigned ucm_get_dev_for_jack_called;
static unsigned ucm_get_cap_control_called;
static char* ucm_get_cap_control_value;
static bool ucm_get_dev_for_jack_return;
static int ucm_set_enabled_value;
static unsigned long eviocbit_ret[NBITS(SW_CNT)];
static int gpio_switch_eviocgbit_fd;
static const char* edid_file_ret;
static unsigned ucm_get_override_type_name_called;
static int ucm_get_alsa_dev_idx_for_dev_value;
static snd_hctl_t* fake_hctl = (snd_hctl_t*)2;
static void ResetStubData() {
gpio_switch_list_for_each_called = 0;
gpio_switch_list_for_each_dev_paths.clear();
gpio_switch_list_for_each_dev_paths.push_back("/dev/input/event3");
gpio_switch_list_for_each_dev_paths.push_back("/dev/input/event2");
gpio_switch_list_for_each_dev_names.clear();
gpio_switch_open_called = 0;
gpio_switch_eviocgsw_called = 0;
gpio_switch_eviocgbit_called = 0;
snd_hctl_elem_get_device_return_val = 0;
snd_hctl_elem_get_device_called = 0;
snd_hctl_first_elem_called = 0;
snd_hctl_first_elem_return_val = reinterpret_cast<snd_hctl_elem_t*>(0x87);
snd_hctl_elem_next_called = 0;
snd_hctl_elem_next_ret_vals.clear();
snd_hctl_elem_next_ret_vals_poped.clear();
snd_hctl_elem_get_name_called = 0;
snd_hctl_elem_set_callback_called = 0;
snd_hctl_elem_set_callback_obj = NULL;
snd_hctl_elem_set_callback_value = NULL;
snd_hctl_find_elem_called = 0;
snd_hctl_find_elem_return_vals.clear();
snd_ctl_elem_id_set_name_map.clear();
cras_system_add_select_fd_called = 0;
cras_system_add_select_fd_values.clear();
cras_system_rm_select_fd_called = 0;
cras_system_rm_select_fd_values.clear();
snd_hctl_elem_set_callback_private_called = 0;
snd_hctl_elem_get_hctl_called = 0;
snd_ctl_elem_value_get_boolean_called = 0;
fake_jack_cb_called = 0;
fake_jack_cb_plugged = 0;
fake_jack_cb_arg = reinterpret_cast<void*>(0x987);
fake_mixer = reinterpret_cast<struct cras_alsa_mixer*>(0x789);
cras_alsa_mixer_get_output_matching_name_called = 0;
cras_alsa_mixer_get_input_matching_name_called = 0;
cras_alsa_mixer_get_control_for_section_called = 0;
cras_alsa_mixer_get_output_matching_name_return_value =
reinterpret_cast<struct mixer_control*>(0x456);
cras_alsa_mixer_get_input_matching_name_return_value = NULL;
cras_alsa_mixer_get_control_for_section_return_value =
reinterpret_cast<struct mixer_control*>(0x456);
ucm_get_dev_for_jack_called = 0;
ucm_get_cap_control_called = 0;
ucm_get_cap_control_value = NULL;
ucm_get_dev_for_jack_return = false;
edid_file_ret = NULL;
ucm_get_override_type_name_called = 0;
ucm_get_alsa_dev_idx_for_dev_value = -1;
memset(eviocbit_ret, 0, sizeof(eviocbit_ret));
}
static void fake_jack_cb(const struct cras_alsa_jack* jack,
int plugged,
void* data) {
fake_jack_cb_called++;
fake_jack_cb_plugged = plugged;
fake_jack_cb_data = data;
// Check that jack enable callback is called if there is a ucm device.
ucm_set_enabled_value = !plugged;
cras_alsa_jack_enable_ucm(jack, plugged);
EXPECT_EQ(ucm_get_dev_for_jack_return ? plugged : !plugged,
ucm_set_enabled_value);
}
TEST(AlsaJacks, CreateNullHctl) {
struct cras_alsa_jack_list* jack_list;
ResetStubData();
jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1, fake_mixer, NULL, NULL,
CRAS_STREAM_OUTPUT, fake_jack_cb,
fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_EQ(0, gpio_switch_open_called);
EXPECT_EQ(0, gpio_switch_eviocgsw_called);
EXPECT_EQ(0, gpio_switch_eviocgbit_called);
cras_alsa_jack_list_destroy(jack_list);
}
TEST(AlsaJacks, CreateNoElements) {
struct cras_alsa_jack_list* jack_list;
ResetStubData();
snd_hctl_first_elem_return_val = NULL;
jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1, fake_mixer, NULL,
fake_hctl, CRAS_STREAM_OUTPUT,
fake_jack_cb, fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_EQ(0, gpio_switch_open_called);
EXPECT_EQ(0, gpio_switch_eviocgsw_called);
EXPECT_EQ(0, gpio_switch_eviocgbit_called);
EXPECT_EQ(1, snd_hctl_first_elem_called);
EXPECT_EQ(0, snd_hctl_elem_next_called);
cras_alsa_jack_list_destroy(jack_list);
}
static struct cras_alsa_jack_list* run_test_with_elem_list(
CRAS_STREAM_DIRECTION direction,
std::string* elems,
unsigned int device_index,
struct cras_use_case_mgr* ucm,
size_t nelems,
size_t nhdmi_jacks,
size_t njacks) {
struct cras_alsa_jack_list* jack_list;
snd_hctl_first_elem_return_val =
reinterpret_cast<snd_hctl_elem_t*>(&elems[0]);
for (unsigned int i = 1; i < nelems; i++)
snd_hctl_elem_next_ret_vals.push_front(
reinterpret_cast<snd_hctl_elem_t*>(&elems[i]));
jack_list = cras_alsa_jack_list_create(0, "card_name", device_index, 1,
fake_mixer, ucm, fake_hctl, direction,
fake_jack_cb, fake_jack_cb_arg);
if (jack_list == NULL)
return jack_list;
EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
EXPECT_EQ(ucm ? njacks : 0, ucm_get_dev_for_jack_called);
EXPECT_EQ(ucm ? njacks : 0, ucm_get_override_type_name_called);
EXPECT_EQ(1, snd_hctl_first_elem_called);
EXPECT_EQ(njacks, snd_hctl_elem_set_callback_called);
EXPECT_EQ(nhdmi_jacks, snd_hctl_find_elem_called);
/* For some functions, the number of calls to them could
* be larger then expected count if there is ELD control
* in given elements. */
EXPECT_GE(snd_hctl_elem_next_called, nelems);
EXPECT_GE(snd_hctl_elem_get_name_called, nelems);
if (direction == CRAS_STREAM_OUTPUT) {
EXPECT_EQ(njacks, cras_alsa_mixer_get_output_matching_name_called);
}
if (direction == CRAS_STREAM_INPUT && ucm_get_dev_for_jack_return) {
EXPECT_EQ(njacks, ucm_get_cap_control_called);
}
return jack_list;
}
static struct cras_alsa_jack_list* run_test_with_section(
CRAS_STREAM_DIRECTION direction,
std::string* elems,
size_t nelems,
unsigned int device_index,
struct cras_use_case_mgr* ucm,
struct ucm_section* ucm_section,
int add_jack_rc,
size_t njacks) {
struct cras_alsa_jack_list* jack_list;
struct cras_alsa_jack* jack;
for (size_t i = 0; i < nelems; i++) {
snd_ctl_elem_id_set_name_map[elems[i]] = i;
snd_hctl_find_elem_return_vals.push_back(
reinterpret_cast<snd_hctl_elem_t*>(&elems[i]));
}
jack_list = cras_alsa_jack_list_create(0, "card_name", device_index, 1,
fake_mixer, ucm, fake_hctl, direction,
fake_jack_cb, fake_jack_cb_arg);
if (jack_list == NULL)
return jack_list;
EXPECT_EQ(add_jack_rc, cras_alsa_jack_list_add_jack_for_section(
jack_list, ucm_section, &jack));
if (add_jack_rc == 0) {
EXPECT_NE(jack, reinterpret_cast<struct cras_alsa_jack*>(NULL));
} else {
EXPECT_EQ(jack, reinterpret_cast<struct cras_alsa_jack*>(NULL));
}
if (add_jack_rc != 0) {
cras_alsa_jack_list_destroy(jack_list);
return NULL;
}
EXPECT_EQ(njacks, snd_hctl_elem_set_callback_called);
EXPECT_EQ(njacks, cras_alsa_mixer_get_control_for_section_called);
return jack_list;
}
TEST(AlsaJacks, ReportNull) {
cras_alsa_jack_list_report(NULL);
}
TEST(AlsaJacks, CreateNoJacks) {
static std::string elem_names[] = {
"Mic Jack",
"foo",
"bar",
};
struct cras_alsa_jack_list* jack_list;
ResetStubData();
jack_list = run_test_with_elem_list(CRAS_STREAM_OUTPUT, elem_names, 0, NULL,
ARRAY_SIZE(elem_names), 0, 0);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
cras_alsa_jack_list_destroy(jack_list);
EXPECT_EQ(0, cras_system_rm_select_fd_called);
}
TEST(AlsaJacks, CreateGPIOHp) {
struct cras_alsa_jack_list* jack_list;
ResetStubData();
gpio_switch_list_for_each_dev_names.push_back("some-other-device");
gpio_switch_list_for_each_dev_names.push_back("c1 Headphone Jack");
eviocbit_ret[LONG(SW_HEADPHONE_INSERT)] |= 1 << OFF(SW_HEADPHONE_INSERT);
gpio_switch_eviocgbit_fd = 2;
snd_hctl_first_elem_return_val = NULL;
jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1, fake_mixer, NULL,
fake_hctl, CRAS_STREAM_OUTPUT,
fake_jack_cb, fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
cras_alsa_jack_list_destroy(jack_list);
EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_GT(gpio_switch_open_called, 1);
EXPECT_EQ(1, gpio_switch_eviocgsw_called);
EXPECT_GT(gpio_switch_eviocgbit_called, 1);
EXPECT_EQ(1, cras_system_add_select_fd_called);
EXPECT_EQ(1, cras_system_rm_select_fd_called);
}
TEST(AlsaJacks, CreateGPIOMic) {
struct cras_alsa_jack_list* jack_list;
ResetStubData();
ucm_get_dev_for_jack_return = true;
gpio_switch_list_for_each_dev_names.push_back("c1 Mic Jack");
gpio_switch_list_for_each_dev_names.push_back("c1 Headphone Jack");
eviocbit_ret[LONG(SW_MICROPHONE_INSERT)] |= 1 << OFF(SW_MICROPHONE_INSERT);
gpio_switch_eviocgbit_fd = 3;
snd_hctl_first_elem_return_val = NULL;
ucm_get_cap_control_value = reinterpret_cast<char*>(0x1);
cras_alsa_mixer_get_input_matching_name_return_value =
reinterpret_cast<struct mixer_control*>(malloc(1));
jack_list = cras_alsa_jack_list_create(
0, "c1", 0, 1, fake_mixer,
reinterpret_cast<struct cras_use_case_mgr*>(0x55), fake_hctl,
CRAS_STREAM_INPUT, fake_jack_cb, fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
EXPECT_EQ(ucm_get_cap_control_called, 1);
EXPECT_EQ(cras_alsa_mixer_get_input_matching_name_called, 1);
cras_alsa_jack_list_destroy(jack_list);
// Mixer will be free by alsa_card_destroy, we should free it explicitly here
free(cras_alsa_mixer_get_input_matching_name_return_value);
}
TEST(AlsaJacks, CreateGPIOHdmi) {
struct cras_alsa_jack_list* jack_list;
ResetStubData();
gpio_switch_list_for_each_dev_names.push_back("c1 HDMI Jack");
gpio_switch_list_for_each_dev_names.push_back("c1 Mic Jack");
eviocbit_ret[LONG(SW_LINEOUT_INSERT)] |= 1 << OFF(SW_LINEOUT_INSERT);
gpio_switch_eviocgbit_fd = 3;
snd_hctl_first_elem_return_val = NULL;
jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1, fake_mixer, NULL,
fake_hctl, CRAS_STREAM_OUTPUT,
fake_jack_cb, fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
EXPECT_EQ(1, gpio_switch_eviocgsw_called);
fake_jack_cb_called = 0;
cras_alsa_jack_list_report(jack_list);
EXPECT_EQ(1, fake_jack_cb_plugged);
EXPECT_EQ(1, fake_jack_cb_called);
cras_alsa_jack_list_destroy(jack_list);
EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_GT(gpio_switch_open_called, 1);
EXPECT_GT(gpio_switch_eviocgbit_called, 1);
EXPECT_EQ(1, cras_system_add_select_fd_called);
EXPECT_EQ(1, cras_system_rm_select_fd_called);
}
void run_gpio_jack_test(int device_index,
int is_first_device,
enum CRAS_STREAM_DIRECTION direction,
int should_create_jack,
const char* jack_name) {
struct cras_alsa_jack_list* jack_list;
struct cras_use_case_mgr* ucm =
reinterpret_cast<struct cras_use_case_mgr*>(0x55);
gpio_switch_list_for_each_dev_names.push_back("some-other-device one");
gpio_switch_eviocgbit_fd = 2;
if (direction == CRAS_STREAM_OUTPUT) {
eviocbit_ret[LONG(SW_HEADPHONE_INSERT)] |= 1 << OFF(SW_HEADPHONE_INSERT);
} else {
eviocbit_ret[LONG(SW_MICROPHONE_INSERT)] |= 1 << OFF(SW_MICROPHONE_INSERT);
}
gpio_switch_list_for_each_dev_names.push_back(jack_name);
snd_hctl_first_elem_return_val = NULL;
jack_list = cras_alsa_jack_list_create(0, "c1", device_index, is_first_device,
fake_mixer, ucm, fake_hctl, direction,
fake_jack_cb, fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
cras_alsa_jack_list_report(jack_list);
EXPECT_EQ(should_create_jack, fake_jack_cb_plugged);
EXPECT_EQ(should_create_jack, fake_jack_cb_called);
cras_alsa_jack_list_destroy(jack_list);
}
TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMMatched) {
int device_index = 1;
int is_first_device = 0;
enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_OUTPUT;
int should_create_jack = 1;
ResetStubData();
/* PlaybackPCM matched, so create jack even if this is not the first device.*/
ucm_get_dev_for_jack_return = true;
ucm_get_alsa_dev_idx_for_dev_value = 1;
run_gpio_jack_test(device_index, is_first_device, direction,
should_create_jack, "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOHpUCMCapturePCMMatched) {
int device_index = 1;
int is_first_device = 0;
enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_INPUT;
int should_create_jack = 1;
ResetStubData();
/* CapturePCM matched, so create jack even if this is not the first device.*/
ucm_get_dev_for_jack_return = true;
ucm_get_alsa_dev_idx_for_dev_value = 1;
run_gpio_jack_test(device_index, is_first_device, direction,
should_create_jack, "c1 Mic Jack");
}
TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMNotMatched) {
int device_index = 0;
int is_first_device = 1;
enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_OUTPUT;
int should_create_jack = 0;
ResetStubData();
/* PlaybackPCM not matched, do not create jack. */
ucm_get_dev_for_jack_return = true;
ucm_get_alsa_dev_idx_for_dev_value = 2;
run_gpio_jack_test(device_index, is_first_device, direction,
should_create_jack, "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMNotSpecifiedFirstDevice) {
int device_index = 1;
int is_first_device = 1;
enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_OUTPUT;
int should_create_jack = 1;
ResetStubData();
/* PlaybackPCM not specified, create jack for the first device. */
ucm_get_dev_for_jack_return = true;
ucm_get_alsa_dev_idx_for_dev_value = -1;
run_gpio_jack_test(device_index, is_first_device, direction,
should_create_jack, "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMNotSpecifiedSecondDevice) {
int device_index = 1;
int is_first_device = 0;
enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_OUTPUT;
int should_create_jack = 0;
ResetStubData();
/* PlaybackPCM not specified, do not create jack for the second device. */
ucm_get_dev_for_jack_return = true;
ucm_get_alsa_dev_idx_for_dev_value = -1;
run_gpio_jack_test(device_index, is_first_device, direction,
should_create_jack, "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOHpNoUCMFirstDevice) {
int device_index = 1;
int is_first_device = 1;
enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_OUTPUT;
int should_create_jack = 1;
ResetStubData();
/* No UCM for this jack, create jack for the first device. */
ucm_get_dev_for_jack_return = false;
ucm_get_alsa_dev_idx_for_dev_value = -1;
run_gpio_jack_test(device_index, is_first_device, direction,
should_create_jack, "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOHpNoUCMSecondDevice) {
int device_index = 1;
int is_first_device = 0;
enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_OUTPUT;
int should_create_jack = 0;
ResetStubData();
/* No UCM for this jack, dot not create jack for the second device. */
ucm_get_dev_for_jack_return = false;
ucm_get_alsa_dev_idx_for_dev_value = -1;
run_gpio_jack_test(device_index, is_first_device, direction,
should_create_jack, "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOMicNoUCMFirstDeviceMicJack) {
int device_index = 1;
int is_first_device = 1;
enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_INPUT;
int should_create_jack = 1;
ResetStubData();
// No UCM for this jack, create jack for the first device.
ucm_get_dev_for_jack_return = false;
ucm_get_alsa_dev_idx_for_dev_value = -1;
// Mic Jack is a valid name for microphone jack.
run_gpio_jack_test(device_index, is_first_device, direction,
should_create_jack, "c1 Mic Jack");
}
TEST(AlsaJacks, CreateGPIOMicNoUCMFirstDeviceHeadsetJack) {
int device_index = 1;
int is_first_device = 1;
enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_INPUT;
int should_create_jack = 1;
ResetStubData();
// No UCM for this jack, create jack for the first device.
ucm_get_dev_for_jack_return = false;
ucm_get_alsa_dev_idx_for_dev_value = -1;
// Headset Jack is a valid name for microphone jack.
run_gpio_jack_test(device_index, is_first_device, direction,
should_create_jack, "c1 Headset Jack");
}
TEST(AlsaJacks, GPIOHdmiWithEdid) {
cras_alsa_jack_list* jack_list;
ResetStubData();
ucm_get_dev_for_jack_return = 1;
edid_file_ret = static_cast<char*>(calloc(1, 1)); // Freed in destroy.
gpio_switch_list_for_each_dev_names.push_back("c1 HDMI Jack");
eviocbit_ret[LONG(SW_LINEOUT_INSERT)] |= 1 << OFF(SW_LINEOUT_INSERT);
gpio_switch_eviocgbit_fd = 3;
snd_hctl_first_elem_return_val = NULL;
jack_list = cras_alsa_jack_list_create(
0, "c1", 0, 1, fake_mixer,
reinterpret_cast<struct cras_use_case_mgr*>(0x55), fake_hctl,
CRAS_STREAM_OUTPUT, fake_jack_cb, fake_jack_cb_arg);
ASSERT_NE(static_cast<cras_alsa_jack_list*>(NULL), jack_list);
EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
EXPECT_EQ(1, gpio_switch_eviocgsw_called);
// EDID shouldn't open, callback should be skipped until re-try.
fake_jack_cb_called = 0;
cras_alsa_jack_list_report(jack_list);
EXPECT_EQ(0, fake_jack_cb_called);
cras_alsa_jack_list_destroy(jack_list);
EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_GT(gpio_switch_open_called, 1);
EXPECT_GT(gpio_switch_eviocgbit_called, 1);
EXPECT_EQ(1, cras_system_add_select_fd_called);
EXPECT_EQ(1, cras_system_rm_select_fd_called);
}
TEST(AlsaJacks, CreateGPIOHpNoNameMatch) {
struct cras_alsa_jack_list* jack_list;
ResetStubData();
gpio_switch_list_for_each_dev_names.push_back("some-other-device one");
gpio_switch_list_for_each_dev_names.push_back("some-other-device two");
snd_hctl_first_elem_return_val = NULL;
jack_list = cras_alsa_jack_list_create(0, "c2", 0, 1, fake_mixer, NULL,
fake_hctl, CRAS_STREAM_OUTPUT,
fake_jack_cb, fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
cras_alsa_jack_list_destroy(jack_list);
EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_EQ(0, gpio_switch_open_called);
EXPECT_EQ(0, cras_system_add_select_fd_called);
EXPECT_EQ(0, cras_system_rm_select_fd_called);
}
TEST(AlsaJacks, CreateOneHpJack) {
std::string elem_names[] = {
"asdf",
"Headphone Jack, klasdjf",
"Mic Jack",
};
struct cras_alsa_jack_list* jack_list;
ResetStubData();
jack_list = run_test_with_elem_list(CRAS_STREAM_OUTPUT, elem_names, 0, NULL,
ARRAY_SIZE(elem_names), 0, 1);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
ASSERT_NE(reinterpret_cast<snd_hctl_elem_callback_t>(NULL),
snd_hctl_elem_set_callback_value);
EXPECT_EQ(1, snd_hctl_elem_set_callback_called);
snd_hctl_elem_get_hctl_return_value = reinterpret_cast<snd_hctl_t*>(0x33);
snd_hctl_elem_get_name_called = 0;
snd_ctl_elem_value_get_boolean_return_value = 1;
snd_hctl_elem_set_callback_value(
reinterpret_cast<snd_hctl_elem_t*>(&elem_names[1]), 0);
EXPECT_EQ(1, snd_hctl_elem_get_name_called);
EXPECT_EQ(1, fake_jack_cb_plugged);
EXPECT_EQ(1, fake_jack_cb_called);
EXPECT_EQ(fake_jack_cb_arg, fake_jack_cb_data);
EXPECT_EQ(reinterpret_cast<snd_hctl_elem_t*>(&elem_names[1]),
snd_hctl_elem_set_callback_obj);
fake_jack_cb_called = 0;
cras_alsa_jack_list_report(jack_list);
EXPECT_EQ(1, fake_jack_cb_plugged);
EXPECT_EQ(1, fake_jack_cb_called);
cras_alsa_jack_list_destroy(jack_list);
EXPECT_EQ(2, snd_hctl_elem_set_callback_called);
EXPECT_EQ(reinterpret_cast<snd_hctl_elem_callback_t>(NULL),
snd_hctl_elem_set_callback_value);
}
TEST(AlsaJacks, CreateOneMicJack) {
static std::string elem_names[] = {
"asdf", "Headphone Jack", "HDMI/DP,pcm=5 Jack", "HDMI/DP,pcm=6 Jack",
"Mic Jack",
};
struct cras_alsa_jack_list* jack_list;
ResetStubData();
jack_list = run_test_with_elem_list(CRAS_STREAM_INPUT, elem_names, 0, NULL,
ARRAY_SIZE(elem_names), 0, 1);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
ASSERT_NE(reinterpret_cast<snd_hctl_elem_callback_t>(NULL),
snd_hctl_elem_set_callback_value);
EXPECT_EQ(1, snd_hctl_elem_set_callback_called);
cras_alsa_jack_list_destroy(jack_list);
EXPECT_EQ(0, cras_system_rm_select_fd_called);
EXPECT_EQ(2, snd_hctl_elem_set_callback_called);
EXPECT_EQ(reinterpret_cast<snd_hctl_elem_callback_t>(NULL),
snd_hctl_elem_set_callback_value);
}
TEST(AlsaJacks, CreateHDMIJacksWithELD) {
std::string elem_names[] = {"asdf", "HDMI/DP,pcm=3 Jack", "ELD",
"HDMI/DP,pcm=4 Jack"};
struct cras_alsa_jack_list* jack_list;
ResetStubData();
snd_hctl_elem_get_device_return_val = 3;
jack_list = run_test_with_elem_list(CRAS_STREAM_OUTPUT, elem_names, 3, NULL,
ARRAY_SIZE(elem_names), 1, 1);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
/* Assert get device is called for the ELD control */
EXPECT_EQ(1, snd_hctl_find_elem_called);
cras_alsa_jack_list_destroy(jack_list);
}
TEST(AlsaJacks, CreateOneHpTwoHDMIJacks) {
std::string elem_names[] = {
"asdf",
"Headphone Jack, klasdjf",
"HDMI/DP,pcm=5 Jack",
"HDMI/DP,pcm=6 Jack",
"Mic Jack",
};
struct cras_alsa_jack_list* jack_list;
ResetStubData();
ucm_get_dev_for_jack_return = true;
jack_list =
run_test_with_elem_list(CRAS_STREAM_OUTPUT, elem_names, 5,
reinterpret_cast<struct cras_use_case_mgr*>(0x55),
ARRAY_SIZE(elem_names), 1, 1);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
snd_hctl_elem_get_hctl_return_value = reinterpret_cast<snd_hctl_t*>(0x33);
snd_hctl_elem_get_name_called = 0;
snd_ctl_elem_value_get_boolean_return_value = 1;
snd_hctl_elem_set_callback_value(
reinterpret_cast<snd_hctl_elem_t*>(&elem_names[2]), 0);
EXPECT_EQ(1, snd_hctl_elem_get_name_called);
EXPECT_EQ(1, fake_jack_cb_plugged);
EXPECT_EQ(1, fake_jack_cb_called);
EXPECT_EQ(fake_jack_cb_arg, fake_jack_cb_data);
EXPECT_EQ(reinterpret_cast<snd_hctl_elem_t*>(&elem_names[2]),
snd_hctl_elem_set_callback_obj);
fake_jack_cb_called = 0;
cras_alsa_jack_list_report(jack_list);
EXPECT_EQ(1, fake_jack_cb_plugged);
EXPECT_EQ(1, fake_jack_cb_called);
cras_alsa_jack_list_destroy(jack_list);
}
TEST(AlsaJacks, CreateHCTLHeadphoneJackFromUCM) {
std::string elem_names[] = {
"HP/DP,pcm=5 Jack",
"Headphone Jack",
};
struct cras_alsa_jack_list* jack_list;
struct ucm_section* section;
section = ucm_section_create("Headphone", "hw:0,1", 0, -1, CRAS_STREAM_OUTPUT,
"Headphone Jack", "hctl");
ResetStubData();
ucm_get_dev_for_jack_return = true;
jack_list = run_test_with_section(
CRAS_STREAM_OUTPUT, elem_names, ARRAY_SIZE(elem_names), 5,
reinterpret_cast<struct cras_use_case_mgr*>(0x55), section, 0, 1);
ASSERT_NE(reinterpret_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
snd_hctl_elem_get_hctl_return_value = reinterpret_cast<snd_hctl_t*>(0x33);
snd_ctl_elem_value_get_boolean_return_value = 1;
snd_hctl_elem_set_callback_value(
reinterpret_cast<snd_hctl_elem_t*>(&elem_names[1]), 0);
EXPECT_EQ(1, snd_hctl_elem_get_name_called);
EXPECT_EQ(1, fake_jack_cb_plugged);
EXPECT_EQ(1, fake_jack_cb_called);
EXPECT_EQ(fake_jack_cb_arg, fake_jack_cb_data);
EXPECT_EQ(reinterpret_cast<snd_hctl_elem_t*>(&elem_names[1]),
snd_hctl_elem_set_callback_obj);
fake_jack_cb_called = 0;
cras_alsa_jack_list_report(jack_list);
EXPECT_EQ(1, fake_jack_cb_plugged);
EXPECT_EQ(1, fake_jack_cb_called);
ucm_section_free_list(section);
cras_alsa_jack_list_destroy(jack_list);
}
TEST(AlsaJacks, CreateGPIOHeadphoneJackFromUCM) {
struct cras_alsa_jack_list* jack_list;
struct cras_alsa_jack* jack;
struct ucm_section* section;
section = ucm_section_create("Headphone", "hw:0,1", 0, -1, CRAS_STREAM_OUTPUT,
"c1 Headphone Jack", "gpio");
ResetStubData();
gpio_switch_list_for_each_dev_names.push_back("some-other-device");
gpio_switch_list_for_each_dev_names.push_back("c1 Headphone Jack");
eviocbit_ret[LONG(SW_HEADPHONE_INSERT)] |= 1 << OFF(SW_HEADPHONE_INSERT);
gpio_switch_eviocgbit_fd = 2;
snd_hctl_first_elem_return_val = NULL;
jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1, fake_mixer, NULL,
fake_hctl, CRAS_STREAM_OUTPUT,
fake_jack_cb, fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
EXPECT_EQ(
0, cras_alsa_jack_list_add_jack_for_section(jack_list, section, &jack));
EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_GT(gpio_switch_open_called, 1);
EXPECT_EQ(1, gpio_switch_eviocgsw_called);
EXPECT_GT(gpio_switch_eviocgbit_called, 1);
EXPECT_EQ(1, cras_system_add_select_fd_called);
EXPECT_EQ(1, cras_alsa_mixer_get_control_for_section_called);
fake_jack_cb_called = 0;
ucm_get_dev_for_jack_return = true;
cras_alsa_jack_list_report(jack_list);
EXPECT_EQ(1, fake_jack_cb_plugged);
EXPECT_EQ(1, fake_jack_cb_called);
EXPECT_EQ(fake_jack_cb_arg, fake_jack_cb_data);
ucm_section_free_list(section);
cras_alsa_jack_list_destroy(jack_list);
EXPECT_EQ(1, cras_system_rm_select_fd_called);
}
TEST(AlsaJacks, BadJackTypeFromUCM) {
std::string elem_names[] = {
"HP/DP,pcm=5 Jack",
"Headphone Jack",
};
struct cras_alsa_jack_list* jack_list;
struct ucm_section* section;
section = ucm_section_create("Headphone", "hw:0,1", 0, -1, CRAS_STREAM_OUTPUT,
"Headphone Jack", "badtype");
ResetStubData();
ucm_get_dev_for_jack_return = true;
jack_list = run_test_with_section(
CRAS_STREAM_OUTPUT, elem_names, ARRAY_SIZE(elem_names), 5,
reinterpret_cast<struct cras_use_case_mgr*>(0x55), section, -22, 1);
EXPECT_EQ(reinterpret_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
ucm_section_free_list(section);
}
TEST(AlsaJacks, NoJackTypeFromUCM) {
std::string elem_names[] = {
"HP/DP,pcm=5 Jack",
"Headphone Jack",
};
struct cras_alsa_jack_list* jack_list;
struct ucm_section* section;
section = ucm_section_create("Headphone", "hw:0,1", 0, -1, CRAS_STREAM_OUTPUT,
"Headphone Jack", NULL);
ResetStubData();
ucm_get_dev_for_jack_return = true;
jack_list = run_test_with_section(
CRAS_STREAM_OUTPUT, elem_names, ARRAY_SIZE(elem_names), 5,
reinterpret_cast<struct cras_use_case_mgr*>(0x55), section, -22, 1);
EXPECT_EQ(reinterpret_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
ucm_section_free_list(section);
}
/* Stubs */
extern "C" {
// From cras_system_state
int cras_system_add_select_fd(int fd,
void (*callback)(void* data),
void* callback_data) {
cras_system_add_select_fd_called++;
cras_system_add_select_fd_values.push_back(fd);
return 0;
}
void cras_system_rm_select_fd(int fd) {
cras_system_rm_select_fd_called++;
cras_system_rm_select_fd_values.push_back(fd);
}
// From alsa-lib hcontrol.c
unsigned int snd_hctl_elem_get_device(const snd_hctl_elem_t* obj) {
snd_hctl_elem_get_device_called = 1;
return snd_hctl_elem_get_device_return_val;
}
snd_hctl_elem_t* snd_hctl_first_elem(snd_hctl_t* hctl) {
snd_hctl_first_elem_called++;
/* When first elem is called, restored the poped ret values */
while (!snd_hctl_elem_next_ret_vals_poped.empty()) {
snd_hctl_elem_t* tmp = snd_hctl_elem_next_ret_vals_poped.back();
snd_hctl_elem_next_ret_vals_poped.pop_back();
snd_hctl_elem_next_ret_vals.push_back(tmp);
}
return snd_hctl_first_elem_return_val;
}
snd_hctl_elem_t* snd_hctl_elem_next(snd_hctl_elem_t* elem) {
snd_hctl_elem_next_called++;
if (snd_hctl_elem_next_ret_vals.empty())
return NULL;
snd_hctl_elem_t* ret_elem = snd_hctl_elem_next_ret_vals.back();
snd_hctl_elem_next_ret_vals.pop_back();
snd_hctl_elem_next_ret_vals_poped.push_back(ret_elem);
return ret_elem;
}
const char* snd_hctl_elem_get_name(const snd_hctl_elem_t* obj) {
snd_hctl_elem_get_name_called++;
const std::string* name = reinterpret_cast<const std::string*>(obj);
return name->c_str();
}
snd_ctl_elem_iface_t snd_hctl_elem_get_interface(const snd_hctl_elem_t* obj) {
return SND_CTL_ELEM_IFACE_CARD;
}
void snd_hctl_elem_set_callback(snd_hctl_elem_t* obj,
snd_hctl_elem_callback_t val) {
snd_hctl_elem_set_callback_called++;
snd_hctl_elem_set_callback_obj = obj;
snd_hctl_elem_set_callback_value = val;
}
void snd_hctl_elem_set_callback_private(snd_hctl_elem_t* obj, void* val) {
snd_hctl_elem_set_callback_private_called++;
snd_hctl_elem_set_callback_private_value = val;
}
void* snd_hctl_elem_get_callback_private(const snd_hctl_elem_t* obj) {
return snd_hctl_elem_set_callback_private_value;
}
snd_hctl_t* snd_hctl_elem_get_hctl(snd_hctl_elem_t* elem) {
snd_hctl_elem_get_hctl_called++;
return snd_hctl_elem_get_hctl_return_value;
}
int snd_hctl_elem_read(snd_hctl_elem_t* elem, snd_ctl_elem_value_t* value) {
return 0;
}
snd_hctl_elem_t* snd_hctl_find_elem(snd_hctl_t* hctl,
const snd_ctl_elem_id_t* id) {
const size_t* index = reinterpret_cast<const size_t*>(id);
snd_hctl_find_elem_called++;
if (*index < snd_hctl_find_elem_return_vals.size())
return snd_hctl_find_elem_return_vals[*index];
return NULL;
}
void snd_ctl_elem_id_set_interface(snd_ctl_elem_id_t* obj,
snd_ctl_elem_iface_t val) {}
void snd_ctl_elem_id_set_device(snd_ctl_elem_id_t* obj, unsigned int val) {}
void snd_ctl_elem_id_set_name(snd_ctl_elem_id_t* obj, const char* val) {
size_t* obj_id = reinterpret_cast<size_t*>(obj);
std::map<std::string, size_t>::iterator id_name_it =
snd_ctl_elem_id_set_name_map.find(val);
if (id_name_it != snd_ctl_elem_id_set_name_map.end())
*obj_id = id_name_it->second;
else
*obj_id = INT_MAX;
}
// From alsa-lib control.c
int snd_ctl_elem_value_get_boolean(const snd_ctl_elem_value_t* obj,
unsigned int idx) {
snd_ctl_elem_value_get_boolean_called++;
return snd_ctl_elem_value_get_boolean_return_value;
}
// From cras_alsa_mixer
struct mixer_control* cras_alsa_mixer_get_output_matching_name(
const struct cras_alsa_mixer* cras_mixer,
size_t device_index,
const char* const name) {
cras_alsa_mixer_get_output_matching_name_called++;
return cras_alsa_mixer_get_output_matching_name_return_value;
}
struct mixer_control* cras_alsa_mixer_get_input_matching_name(
struct cras_alsa_mixer* cras_mixer,
const char* control_name) {
cras_alsa_mixer_get_input_matching_name_called++;
return cras_alsa_mixer_get_input_matching_name_return_value;
}
struct mixer_control* cras_alsa_mixer_get_control_for_section(
struct cras_alsa_mixer* cras_mixer,
struct ucm_section* section) {
cras_alsa_mixer_get_control_for_section_called++;
return cras_alsa_mixer_get_control_for_section_return_value;
}
int gpio_switch_eviocgbit(int fd, void* buf, size_t n_bytes) {
unsigned char* p = (unsigned char*)buf;
/* Returns >= 0 if 'sw' is supported, negative if not.
*
* Set the bit corresponding to 'sw' in 'buf'. 'buf' must have
* been allocated by the caller to accommodate this.
*/
if (fd == gpio_switch_eviocgbit_fd)
memcpy(p, eviocbit_ret, n_bytes);
else
memset(p, 0, n_bytes);
gpio_switch_eviocgbit_called++;
return 1;
}
int gpio_switch_eviocgsw(int fd, void* bits, size_t n_bytes) {
/* Bits set to '1' indicate a switch is enabled.
* Bits set to '0' indicate a switch is disabled
*/
gpio_switch_eviocgsw_called++;
memset(bits, 0xff, n_bytes);
return 1;
}
int gpio_switch_read(int fd, void* buf, size_t n_bytes) {
/* This function is only invoked when the 'switch has changed'
* callback is invoked. That code is not exercised by this
* unittest.
*/
assert(0);
return 0;
}
int gpio_switch_open(const char* pathname) {
++gpio_switch_open_called;
if (strstr(pathname, "event2"))
return 2;
if (strstr(pathname, "event3"))
return 3;
return 0;
}
void gpio_switch_list_for_each(gpio_switch_list_callback callback, void* arg) {
size_t i = 0;
++gpio_switch_list_for_each_called;
while (i < gpio_switch_list_for_each_dev_names.size() &&
i < gpio_switch_list_for_each_dev_paths.size()) {
callback(gpio_switch_list_for_each_dev_paths[i].c_str(),
gpio_switch_list_for_each_dev_names[i].c_str(), arg);
i++;
}
}
int ucm_set_enabled(struct cras_use_case_mgr* mgr,
const char* dev,
int enable) {
ucm_set_enabled_value = enable;
return 0;
}
char* ucm_get_cap_control(struct cras_use_case_mgr* mgr, const char* ucm_dev) {
++ucm_get_cap_control_called;
return ucm_get_cap_control_value;
}
char* ucm_get_dev_for_jack(struct cras_use_case_mgr* mgr,
const char* jack,
CRAS_STREAM_DIRECTION direction) {
++ucm_get_dev_for_jack_called;
if (ucm_get_dev_for_jack_return)
return static_cast<char*>(
malloc(1)); // Will be freed in jack_list_destroy.
return NULL;
}
const char* ucm_get_edid_file_for_dev(struct cras_use_case_mgr* mgr,
const char* dev) {
return edid_file_ret;
}
const char* ucm_get_override_type_name(struct cras_use_case_mgr* mgr,
const char* ucm_dev) {
++ucm_get_override_type_name_called;
return NULL;
}
int ucm_get_alsa_dev_idx_for_dev(struct cras_use_case_mgr* mgr,
const char* dev,
enum CRAS_STREAM_DIRECTION direction) {
return ucm_get_alsa_dev_idx_for_dev_value;
}
cras_timer* cras_tm_create_timer(cras_tm* tm,
unsigned int ms,
void (*cb)(cras_timer* t, void* data),
void* cb_data) {
return reinterpret_cast<cras_timer*>(0x55);
}
void cras_tm_cancel_timer(cras_tm* tm, cras_timer* t) {}
cras_tm* cras_system_state_get_tm() {
return reinterpret_cast<cras_tm*>(0x66);
}
int edid_valid(const unsigned char* edid_data) {
return 0;
}
int edid_lpcm_support(const unsigned char* edid_data, int ext) {
return 0;
}
int edid_get_monitor_name(const unsigned char* edid_data,
char* buf,
unsigned int buf_size) {
return 0;
}
// Overwrite this function so unittest can run without 2 seconds of wait
// in find_gpio_jacks.
int wait_for_dev_input_access() {
return 0;
}
} /* extern "C" */
} // namespace
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
openlog(NULL, LOG_PERROR, LOG_USER);
return RUN_ALL_TESTS();
}