| // 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 <deque> |
| #include <linux/input.h> |
| #include <poll.h> |
| #include <stdio.h> |
| #include <sys/param.h> |
| #include <gtest/gtest.h> |
| #include <string> |
| |
| extern "C" { |
| #include "cras_alsa_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 size_t snd_hctl_open_called; |
| static int snd_hctl_open_return_value; |
| static snd_hctl_t *snd_hctl_open_pointer_val; |
| static size_t snd_hctl_load_called; |
| static int snd_hctl_load_return_value; |
| static int fake_jack_cb_plugged; |
| static int snd_hctl_close_called; |
| 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 struct pollfd *snd_hctl_poll_descriptors_fds; |
| static size_t snd_hctl_poll_descriptors_num_fds; |
| static size_t snd_hctl_poll_descriptors_called; |
| 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_handle_events_called; |
| 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 size_t snd_hctl_nonblock_called; |
| 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 struct mixer_output_control * |
| cras_alsa_mixer_get_output_matching_name_return_value; |
| struct mixer_volume_control * |
| cras_alsa_mixer_get_input_matching_name_return_value; |
| static size_t gpio_get_switch_names_called; |
| static size_t gpio_get_switch_names_count; |
| static size_t gpio_switch_open_called; |
| static size_t gpio_switch_eviocgsw_called; |
| static size_t gpio_switch_eviocgbit_called; |
| static size_t sys_input_get_device_name_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 size_t ucm_get_dsp_name_called; |
| static unsigned ucm_get_override_type_name_called; |
| static char *ucm_get_device_name_for_dev_value; |
| |
| static void ResetStubData() { |
| gpio_get_switch_names_called = 0; |
| gpio_get_switch_names_count = 0; |
| gpio_switch_open_called = 0; |
| gpio_switch_eviocgsw_called = 0; |
| gpio_switch_eviocgbit_called = 0; |
| sys_input_get_device_name_called = 0; |
| snd_hctl_open_called = 0; |
| snd_hctl_open_return_value = 0; |
| snd_hctl_open_pointer_val = reinterpret_cast<snd_hctl_t *>(0x4323); |
| snd_hctl_load_called = 0; |
| snd_hctl_load_return_value = 0; |
| snd_hctl_close_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_poll_descriptors_num_fds = 0; |
| snd_hctl_poll_descriptors_called = 0; |
| 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_handle_events_called = 0; |
| 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); |
| snd_hctl_nonblock_called = 0; |
| 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_output_matching_name_return_value = |
| reinterpret_cast<struct mixer_output_control *>(0x456); |
| cras_alsa_mixer_get_input_matching_name_return_value = NULL; |
| 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_dsp_name_called = 0; |
| ucm_get_override_type_name_called = 0; |
| ucm_get_device_name_for_dev_value = NULL; |
| |
| 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, CreateFailInvalidParams) { |
| EXPECT_EQ(NULL, cras_alsa_jack_list_create(32, "c1", 0, 1, |
| fake_mixer, |
| NULL, |
| CRAS_STREAM_OUTPUT, |
| fake_jack_cb, |
| fake_jack_cb_arg)); |
| EXPECT_EQ(0, snd_hctl_open_called); |
| EXPECT_EQ(NULL, cras_alsa_jack_list_create(0, "c1", 32, 1, |
| fake_mixer, |
| NULL, |
| CRAS_STREAM_OUTPUT, |
| fake_jack_cb, |
| fake_jack_cb_arg)); |
| EXPECT_EQ(0, snd_hctl_open_called); |
| } |
| |
| TEST(AlsaJacks, CreateFailOpen) { |
| ResetStubData(); |
| snd_hctl_open_return_value = -1; |
| snd_hctl_open_pointer_val = NULL; |
| EXPECT_EQ(NULL, cras_alsa_jack_list_create(0, "c1", 0, 1, |
| fake_mixer, |
| NULL, |
| CRAS_STREAM_OUTPUT, |
| fake_jack_cb, |
| fake_jack_cb_arg)); |
| EXPECT_EQ(1, snd_hctl_open_called); |
| } |
| |
| TEST(AlsaJacks, CreateFailLoad) { |
| ResetStubData(); |
| snd_hctl_load_return_value = -1; |
| gpio_get_switch_names_count = ~0; |
| EXPECT_EQ(NULL, cras_alsa_jack_list_create(0, "c1", 0, 1, |
| fake_mixer, |
| NULL, |
| CRAS_STREAM_OUTPUT, |
| fake_jack_cb, |
| fake_jack_cb_arg)); |
| EXPECT_EQ(0, gpio_get_switch_names_called); |
| EXPECT_EQ(0, gpio_switch_open_called); |
| EXPECT_EQ(0, gpio_switch_eviocgsw_called); |
| EXPECT_EQ(0, gpio_switch_eviocgbit_called); |
| EXPECT_EQ(0, sys_input_get_device_name_called); |
| EXPECT_EQ(1, snd_hctl_open_called); |
| EXPECT_EQ(1, snd_hctl_load_called); |
| EXPECT_EQ(1, snd_hctl_close_called); |
| } |
| |
| TEST(AlsaJacks, CreateNoElements) { |
| struct cras_alsa_jack_list *jack_list; |
| |
| ResetStubData(); |
| snd_hctl_first_elem_return_val = NULL; |
| gpio_get_switch_names_count = 0; |
| jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1, |
| fake_mixer, |
| 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(1, gpio_get_switch_names_called); |
| EXPECT_EQ(0, gpio_switch_open_called); |
| EXPECT_EQ(0, gpio_switch_eviocgsw_called); |
| EXPECT_EQ(0, gpio_switch_eviocgbit_called); |
| EXPECT_EQ(0, sys_input_get_device_name_called); |
| EXPECT_EQ(1, snd_hctl_open_called); |
| EXPECT_EQ(1, snd_hctl_load_called); |
| EXPECT_EQ(1, snd_hctl_first_elem_called); |
| EXPECT_EQ(0, snd_hctl_elem_next_called); |
| |
| cras_alsa_jack_list_destroy(jack_list); |
| EXPECT_EQ(1, snd_hctl_close_called); |
| } |
| |
| static struct cras_alsa_jack_list *run_test_with_elem_list( |
| CRAS_STREAM_DIRECTION direction, |
| std::string *elems, |
| unsigned int device_index, |
| snd_use_case_mgr_t *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, |
| direction, |
| fake_jack_cb, |
| fake_jack_cb_arg); |
| if (jack_list == NULL) |
| return 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_open_called); |
| EXPECT_EQ(1, snd_hctl_load_called); |
| EXPECT_EQ(1 + nhdmi_jacks, snd_hctl_first_elem_called); |
| EXPECT_EQ(njacks, snd_hctl_elem_set_callback_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 + nhdmi_jacks); |
| EXPECT_GE(snd_hctl_elem_get_name_called, nelems + njacks); |
| |
| 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; |
| } |
| |
| 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(1, snd_hctl_close_called); |
| } |
| |
| TEST(AlsaJacks, CreateGPIOHp) { |
| struct cras_alsa_jack_list *jack_list; |
| |
| ResetStubData(); |
| gpio_get_switch_names_count = ~0; |
| 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, |
| CRAS_STREAM_OUTPUT, |
| fake_jack_cb, |
| fake_jack_cb_arg); |
| ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list); |
| |
| cras_alsa_jack_list_destroy(jack_list); |
| EXPECT_EQ(1, gpio_get_switch_names_called); |
| EXPECT_GT(gpio_switch_open_called, 1); |
| EXPECT_EQ(1, gpio_switch_eviocgsw_called); |
| EXPECT_GT(gpio_switch_eviocgbit_called, 1); |
| EXPECT_GT(sys_input_get_device_name_called, 1); |
| EXPECT_EQ(1, cras_system_add_select_fd_called); |
| EXPECT_EQ(1, snd_hctl_close_called); |
| } |
| |
| TEST(AlsaJacks, CreateGPIOMic) { |
| struct cras_alsa_jack_list *jack_list; |
| ResetStubData(); |
| ucm_get_dev_for_jack_return = true; |
| gpio_get_switch_names_count = ~0; |
| 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); |
| |
| // Freed in destroy. |
| cras_alsa_mixer_get_input_matching_name_return_value = |
| reinterpret_cast<struct mixer_volume_control *>(malloc(1)); |
| |
| jack_list = cras_alsa_jack_list_create( |
| 0, |
| "c1", |
| 0, |
| 1, |
| fake_mixer, |
| reinterpret_cast<snd_use_case_mgr_t*>(0x55), |
| CRAS_STREAM_INPUT, |
| fake_jack_cb, |
| fake_jack_cb_arg); |
| ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), 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); |
| } |
| |
| TEST(AlsaJacks, CreateGPIOHdmi) { |
| struct cras_alsa_jack_list *jack_list; |
| |
| ResetStubData(); |
| gpio_get_switch_names_count = ~0; |
| 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, |
| CRAS_STREAM_OUTPUT, |
| fake_jack_cb, |
| fake_jack_cb_arg); |
| ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), 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_get_switch_names_called); |
| EXPECT_GT(gpio_switch_open_called, 1); |
| EXPECT_GT(gpio_switch_eviocgbit_called, 1); |
| EXPECT_GT(sys_input_get_device_name_called, 1); |
| EXPECT_EQ(1, cras_system_add_select_fd_called); |
| EXPECT_EQ(1, snd_hctl_close_called); |
| } |
| |
| void run_gpio_jack_test( |
| int device_index, |
| int is_first_device, |
| enum CRAS_STREAM_DIRECTION direction, |
| int should_create_jack) |
| { |
| struct cras_alsa_jack_list *jack_list; |
| snd_use_case_mgr_t *ucm = reinterpret_cast<snd_use_case_mgr_t*>(0x55); |
| |
| gpio_get_switch_names_count = ~0; |
| 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); |
| snd_hctl_first_elem_return_val = NULL; |
| |
| jack_list = cras_alsa_jack_list_create(0, "c1", device_index, is_first_device, |
| fake_mixer, |
| ucm, |
| direction, |
| fake_jack_cb, |
| fake_jack_cb_arg); |
| ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), 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_device_name_for_dev_value = strdup("hw:c1,1"); |
| |
| run_gpio_jack_test( |
| device_index, is_first_device, direction, should_create_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_device_name_for_dev_value = strdup("hw:c1,1"); |
| |
| run_gpio_jack_test( |
| device_index, is_first_device, direction, should_create_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_device_name_for_dev_value = strdup("hw:c1,2"); |
| |
| run_gpio_jack_test( |
| device_index, is_first_device, direction, should_create_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_device_name_for_dev_value = NULL; |
| |
| run_gpio_jack_test( |
| device_index, is_first_device, direction, should_create_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_device_name_for_dev_value = NULL; |
| |
| run_gpio_jack_test( |
| device_index, is_first_device, direction, should_create_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_device_name_for_dev_value = NULL; |
| |
| run_gpio_jack_test( |
| device_index, is_first_device, direction, should_create_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_device_name_for_dev_value = NULL; |
| |
| run_gpio_jack_test( |
| device_index, is_first_device, direction, should_create_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_get_switch_names_count = ~0; |
| 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<snd_use_case_mgr_t*>(0x55), |
| CRAS_STREAM_OUTPUT, |
| fake_jack_cb, |
| fake_jack_cb_arg); |
| ASSERT_NE(static_cast<cras_alsa_jack_list*>(NULL), 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_get_switch_names_called); |
| EXPECT_GT(gpio_switch_open_called, 1); |
| EXPECT_GT(gpio_switch_eviocgbit_called, 1); |
| EXPECT_GT(sys_input_get_device_name_called, 1); |
| EXPECT_EQ(1, cras_system_add_select_fd_called); |
| EXPECT_EQ(1, snd_hctl_close_called); |
| } |
| |
| TEST(AlsaJacks, CreateGPIOHpNoNameMatch) { |
| struct cras_alsa_jack_list *jack_list; |
| |
| ResetStubData(); |
| gpio_get_switch_names_count = ~0; |
| snd_hctl_first_elem_return_val = NULL; |
| jack_list = cras_alsa_jack_list_create(0, "c2", 0, 1, |
| fake_mixer, |
| NULL, |
| CRAS_STREAM_OUTPUT, |
| fake_jack_cb, |
| fake_jack_cb_arg); |
| ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list); |
| |
| cras_alsa_jack_list_destroy(jack_list); |
| EXPECT_EQ(1, gpio_get_switch_names_called); |
| EXPECT_GT(gpio_switch_open_called, 1); |
| EXPECT_GT(sys_input_get_device_name_called, 1); |
| EXPECT_EQ(0, cras_system_add_select_fd_called); |
| EXPECT_EQ(1, snd_hctl_close_called); |
| } |
| |
| TEST(AlsaJacks, CreateOneHpJack) { |
| std::string elem_names[] = { |
| "asdf", |
| "Headphone Jack, klasdjf", |
| "Mic Jack", |
| }; |
| struct pollfd poll_fds[] = { |
| {3, 0, 0}, |
| }; |
| struct cras_alsa_jack_list *jack_list; |
| |
| ResetStubData(); |
| snd_hctl_poll_descriptors_fds = poll_fds; |
| snd_hctl_poll_descriptors_num_fds = ARRAY_SIZE(poll_fds); |
| 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); |
| EXPECT_EQ(ARRAY_SIZE(poll_fds), cras_system_add_select_fd_called); |
| EXPECT_EQ(3, cras_system_add_select_fd_values[0]); |
| |
| 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(ARRAY_SIZE(poll_fds), cras_system_rm_select_fd_called); |
| EXPECT_EQ(3, cras_system_rm_select_fd_values[0]); |
| EXPECT_EQ(1, snd_hctl_close_called); |
| } |
| |
| 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); |
| |
| cras_alsa_jack_list_destroy(jack_list); |
| EXPECT_EQ(1, snd_hctl_close_called); |
| } |
| |
| TEST(AlsaJacks, CreateHDMIJacksWithELD) { |
| std::string elem_names[] = { |
| "asdf", |
| "HDMI/DP,pcm=3 Jack", |
| "ELD", |
| "HDMI/DP,pcm=4 Jack" |
| }; |
| struct pollfd poll_fds[] = { |
| {0}, |
| }; |
| struct cras_alsa_jack_list *jack_list; |
| |
| ResetStubData(); |
| snd_hctl_poll_descriptors_fds = poll_fds; |
| snd_hctl_poll_descriptors_num_fds = ARRAY_SIZE(poll_fds); |
| 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_elem_get_device_called); |
| cras_alsa_jack_list_destroy(jack_list); |
| EXPECT_EQ(1, snd_hctl_close_called); |
| } |
| |
| TEST(AlsaJacks, CreateOneHpTwoHDMIJacks) { |
| std::string elem_names[] = { |
| "asdf", |
| "Headphone Jack, klasdjf", |
| "HDMI/DP,pcm=5 Jack", |
| "HDMI/DP,pcm=6 Jack", |
| "Mic Jack", |
| }; |
| struct pollfd poll_fds[] = { |
| {5, 0, 0}, |
| }; |
| struct cras_alsa_jack_list *jack_list; |
| |
| ResetStubData(); |
| snd_hctl_poll_descriptors_fds = poll_fds; |
| snd_hctl_poll_descriptors_num_fds = ARRAY_SIZE(poll_fds); |
| ucm_get_dev_for_jack_return = true; |
| jack_list = run_test_with_elem_list( |
| CRAS_STREAM_OUTPUT, |
| elem_names, |
| 5, |
| reinterpret_cast<snd_use_case_mgr_t*>(0x55), |
| ARRAY_SIZE(elem_names), |
| 1, |
| 1); |
| ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list); |
| EXPECT_EQ(ARRAY_SIZE(poll_fds), cras_system_add_select_fd_called); |
| EXPECT_EQ(5, cras_system_add_select_fd_values[0]); |
| |
| 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); |
| EXPECT_EQ(ARRAY_SIZE(poll_fds), cras_system_rm_select_fd_called); |
| EXPECT_EQ(5, cras_system_rm_select_fd_values[0]); |
| EXPECT_EQ(1, snd_hctl_close_called); |
| } |
| |
| /* 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 |
| int snd_hctl_open(snd_hctl_t **hctlp, const char *name, int mode) { |
| *hctlp = snd_hctl_open_pointer_val; |
| snd_hctl_open_called++; |
| return snd_hctl_open_return_value; |
| } |
| int snd_hctl_load(snd_hctl_t *hctl) { |
| snd_hctl_load_called++; |
| return snd_hctl_load_return_value; |
| } |
| int snd_hctl_close(snd_hctl_t *hctl) { |
| snd_hctl_close_called++; |
| return 0; |
| } |
| 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; |
| } |
| int snd_hctl_poll_descriptors_count(snd_hctl_t *hctl) { |
| return snd_hctl_poll_descriptors_num_fds; |
| } |
| int snd_hctl_poll_descriptors(snd_hctl_t *hctl, |
| struct pollfd *pfds, |
| unsigned int space) { |
| unsigned int num = MIN(space, snd_hctl_poll_descriptors_num_fds); |
| memcpy(pfds, snd_hctl_poll_descriptors_fds, num * sizeof(*pfds)); |
| snd_hctl_poll_descriptors_called++; |
| return num; |
| } |
| int snd_hctl_handle_events(snd_hctl_t *hctl) { |
| snd_hctl_handle_events_called++; |
| return 0; |
| } |
| 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; |
| } |
| int snd_hctl_nonblock(snd_hctl_t *hctl, int nonblock) { |
| snd_hctl_nonblock_called++; |
| return 0; |
| } |
| // 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_output_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_volume_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; |
| } |
| |
| char *sys_input_get_device_name(const char *path) |
| { |
| sys_input_get_device_name_called++; |
| return strdup("c1 Headphone Jack"); |
| } |
| |
| 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); |
| } |
| |
| 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; |
| } |
| |
| unsigned gpio_get_switch_names(enum CRAS_STREAM_DIRECTION direction, |
| char **names, size_t n_names) |
| { |
| unsigned i, ub; |
| static const char *dummy[] = { |
| "/dev/input/event3", |
| "/dev/input/event2", |
| }; |
| |
| ++gpio_get_switch_names_called; |
| |
| ub = gpio_get_switch_names_count; |
| if (ub > ARRAY_SIZE(dummy)) |
| ub = ARRAY_SIZE(dummy); |
| |
| for (i = 0; i < ub; ++i) { |
| names[i] = strdup(dummy[i]); |
| } |
| return ub; |
| } |
| |
| int ucm_set_enabled(snd_use_case_mgr_t *mgr, const char *dev, int enable) { |
| ucm_set_enabled_value = enable; |
| return 0; |
| } |
| |
| char *ucm_get_cap_control(snd_use_case_mgr_t *mgr, const char *ucm_dev) { |
| ++ucm_get_cap_control_called; |
| return ucm_get_cap_control_value; |
| } |
| |
| char *ucm_get_dev_for_jack(snd_use_case_mgr_t *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_dsp_name(snd_use_case_mgr_t *mgr, const char *ucm_dev, |
| int direction) { |
| ++ucm_get_dsp_name_called; |
| return NULL; |
| } |
| |
| const char *ucm_get_edid_file_for_dev(snd_use_case_mgr_t *mgr, |
| const char *dev) { |
| return edid_file_ret; |
| } |
| |
| const char *ucm_get_override_type_name(snd_use_case_mgr_t *mgr, |
| const char *ucm_dev) |
| { |
| ++ucm_get_override_type_name_called; |
| return NULL; |
| } |
| |
| const char *ucm_get_device_name_for_dev(snd_use_case_mgr_t *mgr, |
| const char *dev, |
| enum CRAS_STREAM_DIRECTION direction) |
| { |
| return ucm_get_device_name_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; |
| } |
| |
| } /* extern "C" */ |
| |
| } // namespace |
| |
| int main(int argc, char **argv) { |
| ::testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |