| // Copyright 2019 The Fuchsia 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 "hid-buttons.h" |
| |
| #include <lib/fake_ddk/fake_ddk.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <zircon/assert.h> |
| |
| #include <cstddef> |
| |
| #include <ddk/metadata.h> |
| #include <ddk/metadata/buttons.h> |
| #include <ddk/protocol/buttons.h> |
| #include <ddktl/protocol/gpio.h> |
| #include <mock/ddktl/protocol/gpio.h> |
| #include <zxtest/zxtest.h> |
| |
| #include "zircon/errors.h" |
| |
| namespace { |
| static const buttons_button_config_t buttons_direct[] = { |
| {BUTTONS_TYPE_DIRECT, BUTTONS_ID_VOLUME_UP, 0, 0, 0}, |
| }; |
| |
| static const buttons_gpio_config_t gpios_direct[] = { |
| {BUTTONS_GPIO_TYPE_INTERRUPT, 0, {GPIO_NO_PULL}}}; |
| |
| static const buttons_button_config_t buttons_multiple[] = { |
| {BUTTONS_TYPE_DIRECT, BUTTONS_ID_VOLUME_UP, 0, 0, 0}, |
| {BUTTONS_TYPE_DIRECT, BUTTONS_ID_MIC_MUTE, 1, 0, 0}, |
| {BUTTONS_TYPE_DIRECT, BUTTONS_ID_CAM_MUTE, 2, 0, 0}, |
| }; |
| |
| static const buttons_gpio_config_t gpios_multiple[] = { |
| {BUTTONS_GPIO_TYPE_INTERRUPT, 0, {GPIO_NO_PULL}}, |
| {BUTTONS_GPIO_TYPE_INTERRUPT, 0, {GPIO_NO_PULL}}, |
| {BUTTONS_GPIO_TYPE_INTERRUPT, 0, {GPIO_NO_PULL}}, |
| }; |
| |
| static const buttons_button_config_t buttons_matrix[] = { |
| {BUTTONS_TYPE_MATRIX, BUTTONS_ID_VOLUME_UP, 0, 2, 0}, |
| {BUTTONS_TYPE_MATRIX, BUTTONS_ID_KEY_A, 1, 2, 0}, |
| {BUTTONS_TYPE_MATRIX, BUTTONS_ID_KEY_M, 0, 3, 0}, |
| {BUTTONS_TYPE_MATRIX, BUTTONS_ID_PLAY_PAUSE, 1, 3, 0}, |
| }; |
| |
| static const buttons_gpio_config_t gpios_matrix[] = { |
| {BUTTONS_GPIO_TYPE_INTERRUPT, 0, {GPIO_PULL_UP}}, |
| {BUTTONS_GPIO_TYPE_INTERRUPT, 0, {GPIO_PULL_UP}}, |
| {BUTTONS_GPIO_TYPE_MATRIX_OUTPUT, 0, {0}}, |
| {BUTTONS_GPIO_TYPE_MATRIX_OUTPUT, 0, {0}}, |
| }; |
| |
| static const buttons_button_config_t buttons_duplicate[] = { |
| {BUTTONS_TYPE_DIRECT, BUTTONS_ID_VOLUME_UP, 0, 0, 0}, |
| {BUTTONS_TYPE_DIRECT, BUTTONS_ID_VOLUME_DOWN, 1, 0, 0}, |
| {BUTTONS_TYPE_DIRECT, BUTTONS_ID_FDR, 2, 0, 0}, |
| }; |
| |
| static const buttons_gpio_config_t gpios_duplicate[] = { |
| {BUTTONS_GPIO_TYPE_INTERRUPT, 0, {GPIO_NO_PULL}}, |
| {BUTTONS_GPIO_TYPE_INTERRUPT, 0, {GPIO_NO_PULL}}, |
| {BUTTONS_GPIO_TYPE_INTERRUPT, 0, {GPIO_NO_PULL}}, |
| }; |
| } // namespace |
| |
| namespace buttons { |
| |
| class HidButtonsDeviceTest : public HidButtonsDevice { |
| public: |
| HidButtonsDeviceTest() : HidButtonsDevice(fake_ddk::kFakeParent) {} |
| |
| void DdkUnbind(ddk::UnbindTxn txn) { |
| // ShutDown() assigns nullptr to the function_ pointers. Normally, the structures being pointed |
| // at would be freed by the real DDK as a consequence of unbinding them. However, in the test, |
| // they need to be freed manually (necessitating a copy of the pointer). |
| HidButtonsHidBusFunction* hidbus_function_copy_ = hidbus_function_; |
| HidButtonsButtonsFunction* buttons_function_copy_ = buttons_function_; |
| |
| HidButtonsDevice::ShutDown(); |
| txn.Reply(); |
| |
| delete hidbus_function_copy_; |
| delete buttons_function_copy_; |
| } |
| void DdkRelease() { delete this; } |
| |
| void ShutDownTest() { DdkUnbind(ddk::UnbindTxn(fake_ddk::kFakeDevice)); } |
| |
| ddk::MockGpio& GetGpio(size_t index) { return gpio_mocks_[index]; } |
| |
| void VerifyAndClearGpios() { |
| for (auto& gpio : gpio_mocks_) { |
| ASSERT_NO_FATAL_FAILURES(gpio.VerifyAndClear()); |
| } |
| } |
| |
| void SetupGpio(ddk::MockGpio* mock, const buttons_gpio_config_t& gpio_config, zx::interrupt irq) { |
| mock->ExpectSetAltFunction(ZX_OK, 0); |
| |
| if (gpio_config.type == BUTTONS_GPIO_TYPE_INTERRUPT) { |
| mock->ExpectConfigIn(ZX_OK, gpio_config.internal_pull) |
| .ExpectRead(ZX_OK, 0) // Not pushed, low. |
| .ExpectReleaseInterrupt(ZX_OK) |
| .ExpectGetInterrupt(ZX_OK, ZX_INTERRUPT_MODE_EDGE_HIGH, std::move(irq)); |
| |
| // Make sure polarity is correct in case it changed during configuration. |
| mock->ExpectRead(ZX_OK, 0) // Not pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Set correct polarity. |
| .ExpectRead(ZX_OK, 0); // Still not pushed. |
| } else if (gpio_config.type == BUTTONS_GPIO_TYPE_MATRIX_OUTPUT) { |
| mock->ExpectConfigOut(ZX_OK, gpio_config.output_value); |
| } |
| } |
| |
| zx_status_t BindTest(const buttons_gpio_config_t* gpios_config, size_t gpios_config_size, |
| const buttons_button_config_t* buttons_config, size_t buttons_config_size) { |
| gpio_mocks_ = std::vector<ddk::MockGpio>(gpios_config_size); |
| for (size_t i = 0; i < gpio_mocks_.size(); i++) { |
| zx::interrupt irq; |
| zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &irq); |
| SetupGpio(&gpio_mocks_[i], gpios_config[i], std::move(irq)); |
| } |
| |
| const size_t n_gpios = gpios_config_size; |
| auto gpios = fbl::Array(new HidButtonsDevice::Gpio[n_gpios], n_gpios); |
| const size_t n_buttons = buttons_config_size; |
| auto buttons = fbl::Array(new buttons_button_config_t[n_buttons], n_buttons); |
| for (size_t i = 0; i < n_gpios; ++i) { |
| gpios[i].gpio = *gpio_mocks_[i].GetProto(); |
| gpios[i].config = gpios_config[i]; |
| } |
| |
| for (size_t i = 0; i < n_buttons; ++i) { |
| buttons[i] = buttons_config[i]; |
| switch (buttons_config[i].type) { |
| case BUTTONS_TYPE_DIRECT: { |
| gpio_mocks_[buttons[i].gpioA_idx].ExpectRead(ZX_OK, 0); |
| break; |
| } |
| case BUTTONS_TYPE_MATRIX: { |
| gpio_mocks_[buttons[i].gpioB_idx].ExpectConfigIn(ZX_OK, 0x2); |
| gpio_mocks_[buttons[i].gpioA_idx].ExpectRead(ZX_OK, 0); |
| gpio_mocks_[buttons[i].gpioB_idx].ExpectConfigOut( |
| ZX_OK, gpios[buttons[i].gpioB_idx].config.output_value); |
| break; |
| } |
| default: |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| |
| return HidButtonsDevice::Bind(std::move(gpios), std::move(buttons)); |
| } |
| |
| void FakeInterrupt() { |
| // Issue the first interrupt. |
| zx_port_packet packet = {kPortKeyInterruptStart + 0, ZX_PKT_TYPE_USER, ZX_OK, {}}; |
| zx_status_t status = port_.queue(&packet); |
| ZX_ASSERT(status == ZX_OK); |
| } |
| |
| void FakeInterrupt(ButtonType type) { |
| // Issue the first interrupt. |
| zx_port_packet packet = { |
| kPortKeyInterruptStart + button_map_[type], ZX_PKT_TYPE_USER, ZX_OK, {}}; |
| zx_status_t status = port_.queue(&packet); |
| ZX_ASSERT(status == ZX_OK); |
| } |
| |
| void DebounceWait() { |
| sync_completion_wait(&debounce_threshold_passed_, ZX_TIME_INFINITE); |
| sync_completion_reset(&debounce_threshold_passed_); |
| } |
| |
| void ClosingChannel(ButtonsNotifyInterface* interface) override { |
| HidButtonsDevice::ClosingChannel(interface); |
| sync_completion_signal(&test_channels_cleared_); |
| } |
| |
| void Notify(uint32_t type) override { |
| HidButtonsDevice::Notify(type); |
| sync_completion_signal(&debounce_threshold_passed_); |
| } |
| |
| void Wait() { |
| sync_completion_wait(&test_channels_cleared_, ZX_TIME_INFINITE); |
| sync_completion_reset(&test_channels_cleared_); |
| } |
| |
| HidButtonsButtonsFunction* GetButtonsFn() { return GetButtonsFunction(); } |
| |
| private: |
| sync_completion_t test_channels_cleared_; |
| sync_completion_t debounce_threshold_passed_; |
| |
| std::vector<ddk::MockGpio> gpio_mocks_; |
| }; |
| |
| TEST(HidButtonsTest, DirectButtonBind) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_direct, countof(gpios_direct), buttons_direct, |
| countof(buttons_direct))); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, DirectButtonPush) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_direct, countof(gpios_direct), buttons_direct, |
| countof(buttons_direct))); |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1) // Still pushed, ok to continue. |
| .ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.FakeInterrupt(); |
| device.DebounceWait(); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, DirectButtonUnpushedReport) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_direct, countof(gpios_direct), buttons_direct, |
| countof(buttons_direct))); |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 0) // Not Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Keep the correct polarity. |
| .ExpectRead(ZX_OK, 0) // Still not pushed, ok to continue. |
| .ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.FakeInterrupt(); |
| device.DebounceWait(); |
| |
| hidbus_ifc_protocol_ops_t ops = {}; |
| ops.io_queue = [](void* ctx, const void* buffer, size_t size, zx_time_t time) { |
| buttons_input_rpt_t report_volume_up = {}; |
| report_volume_up.rpt_id = 1; |
| report_volume_up.volume_up = 0; // Unpushed. |
| ASSERT_BYTES_EQ(buffer, &report_volume_up, size); |
| EXPECT_EQ(size, sizeof(report_volume_up)); |
| }; |
| hidbus_ifc_protocol_t protocol = {}; |
| protocol.ops = &ops; |
| device.HidbusStart(&protocol); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, DirectButtonPushedReport) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_direct, countof(gpios_direct), buttons_direct, |
| countof(buttons_direct))); |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1) // Still pushed, ok to continue. |
| .ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.FakeInterrupt(); |
| device.DebounceWait(); |
| |
| hidbus_ifc_protocol_ops_t ops = {}; |
| ops.io_queue = [](void* ctx, const void* buffer, size_t size, zx_time_t time) { |
| buttons_input_rpt_t report_volume_up = {}; |
| report_volume_up.rpt_id = 1; |
| report_volume_up.volume_up = 1; // Pushed |
| ASSERT_BYTES_EQ(buffer, &report_volume_up, size); |
| EXPECT_EQ(size, sizeof(report_volume_up)); |
| }; |
| hidbus_ifc_protocol_t protocol = {}; |
| protocol.ops = &ops; |
| device.HidbusStart(&protocol); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, DirectButtonPushUnpushPush) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_direct, countof(gpios_direct), buttons_direct, |
| countof(buttons_direct))); |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1) // Still pushed, ok to continue. |
| .ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.FakeInterrupt(); |
| device.DebounceWait(); |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 0) // Not pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Turn the polarity. |
| .ExpectRead(ZX_OK, 0) // Still not pushed, ok to continue. |
| .ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.FakeInterrupt(); |
| device.DebounceWait(); |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1) // Still pushed, ok to continue. |
| .ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.FakeInterrupt(); |
| device.DebounceWait(); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, DirectButtonFlaky) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_direct, countof(gpios_direct), buttons_direct, |
| countof(buttons_direct))); |
| |
| // Reconfigure Polarity due to interrupt and keep checking until correct. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 0) // Oops now not pushed! not ok, retry. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1) // Oops pushed! not ok, retry. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 0) // Oops now not pushed! not ok, retry. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1) // Oops pushed again! not ok, retry. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1); // Now pushed and polarity set low, ok. |
| // Read value to generate report. |
| device.GetGpio(0).ExpectRead(ZX_OK, 1); // Pushed. |
| device.FakeInterrupt(); |
| device.DebounceWait(); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, MatrixButtonBind) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_matrix, countof(gpios_matrix), buttons_matrix, |
| countof(buttons_matrix))); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, MatrixButtonPush) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_matrix, countof(gpios_matrix), buttons_matrix, |
| countof(buttons_matrix))); |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1); // Still pushed, ok to continue. |
| |
| // Matrix Scan for 0. |
| device.GetGpio(2).ExpectConfigIn(ZX_OK, GPIO_NO_PULL); // Float column. |
| device.GetGpio(0).ExpectRead(ZX_OK, 1); // Read row. |
| device.GetGpio(2).ExpectConfigOut(ZX_OK, gpios_matrix[2].output_value); // Restore column. |
| |
| // Matrix Scan for 1. |
| device.GetGpio(2).ExpectConfigIn(ZX_OK, GPIO_NO_PULL); // Float column. |
| device.GetGpio(1).ExpectRead(ZX_OK, 0); // Read row. |
| device.GetGpio(2).ExpectConfigOut(ZX_OK, gpios_matrix[2].output_value); // Restore column. |
| |
| // Matrix Scan for 2. |
| device.GetGpio(3).ExpectConfigIn(ZX_OK, GPIO_NO_PULL); // Float column. |
| device.GetGpio(0).ExpectRead(ZX_OK, 0); // Read row. |
| device.GetGpio(3).ExpectConfigOut(ZX_OK, gpios_matrix[3].output_value); // Restore column. |
| |
| // Matrix Scan for 3. |
| device.GetGpio(3).ExpectConfigIn(ZX_OK, GPIO_NO_PULL); // Float column. |
| device.GetGpio(1).ExpectRead(ZX_OK, 0); // Read row. |
| device.GetGpio(3).ExpectConfigOut(ZX_OK, gpios_matrix[3].output_value); // Restore colument. |
| |
| device.FakeInterrupt(); |
| device.DebounceWait(); |
| |
| hidbus_ifc_protocol_ops_t ops = {}; |
| ops.io_queue = [](void* ctx, const void* buffer, size_t size, zx_time_t time) { |
| buttons_input_rpt_t report_volume_up = {}; |
| report_volume_up.rpt_id = 1; |
| report_volume_up.volume_up = 1; |
| ASSERT_BYTES_EQ(buffer, &report_volume_up, size); |
| EXPECT_EQ(size, sizeof(report_volume_up)); |
| }; |
| hidbus_ifc_protocol_t protocol = {}; |
| protocol.ops = &ops; |
| device.HidbusStart(&protocol); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, ButtonsProtocolTest) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_multiple, countof(gpios_multiple), buttons_multiple, |
| countof(buttons_multiple))); |
| |
| zx::channel client_end, server_end; |
| zx::channel::create(0, &client_end, &server_end); |
| device.GetButtonsFn()->ButtonsGetChannel(std::move(server_end)); |
| client_end.reset(); |
| device.Wait(); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, GetStateTest) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_multiple, countof(gpios_multiple), buttons_multiple, |
| countof(buttons_multiple))); |
| |
| { // Scoping for Client |
| zx::channel client_end, server_end; |
| EXPECT_OK(zx::channel::create(0, &client_end, &server_end)); |
| device.GetButtonsFn()->ButtonsGetChannel(std::move(server_end)); |
| Buttons::SyncClient client(std::move(client_end)); |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(1).ExpectRead(ZX_OK, 1); // Read value. |
| auto result = client.GetState(ButtonType::MUTE); |
| EXPECT_EQ(result->pressed, true); |
| } // Close Client |
| |
| device.Wait(); |
| device.ShutDownTest(); |
| |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, Notify1) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_multiple, countof(gpios_multiple), buttons_multiple, |
| countof(buttons_multiple))); |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(1) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1); // Still pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(1) |
| .ExpectRead(ZX_OK, 0) // Not pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Turn the polarity. |
| .ExpectRead(ZX_OK, 0); // Still not pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1); // Still pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| |
| { // Scoping for Client |
| zx::channel client_end, server_end; |
| EXPECT_OK(zx::channel::create(0, &client_end, &server_end)); |
| device.GetButtonsFn()->ButtonsGetChannel(std::move(server_end)); |
| Buttons::SyncClient client(std::move(client_end)); |
| auto result1 = client.RegisterNotify(1 << static_cast<uint8_t>(ButtonType::MUTE)); |
| EXPECT_OK(result1.status()); |
| |
| // Interrupts |
| device.FakeInterrupt(ButtonType::MUTE); |
| device.DebounceWait(); |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::MUTE && message->pressed == true) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client.HandleEvents(event_handlers).ok()); |
| } |
| device.FakeInterrupt(ButtonType::MUTE); |
| device.DebounceWait(); |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::MUTE && message->pressed == false) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client.HandleEvents(event_handlers).ok()); |
| } |
| auto result2 = client.RegisterNotify(1 << static_cast<uint8_t>(ButtonType::VOLUME_UP)); |
| EXPECT_OK(result2.status()); |
| device.FakeInterrupt(ButtonType::VOLUME_UP); |
| device.DebounceWait(); |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::VOLUME_UP && message->pressed == true) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client.HandleEvents(event_handlers).ok()); |
| } |
| } // Close Client |
| |
| device.Wait(); |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, Notify2) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_multiple, countof(gpios_multiple), buttons_multiple, |
| countof(buttons_multiple))); |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(1) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1); // Still pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(1) |
| .ExpectRead(ZX_OK, 0) // Not pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Turn the polarity. |
| .ExpectRead(ZX_OK, 0); // Still not pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(1) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1); // Still pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 0) // Not pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Turn the polarity. |
| .ExpectRead(ZX_OK, 0); // Still not pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1); // Still pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| |
| { // Scoping for Client 2 |
| // Client 2 |
| zx::channel client_end2, server_end2; |
| EXPECT_OK(zx::channel::create(0, &client_end2, &server_end2)); |
| device.GetButtonsFn()->ButtonsGetChannel(std::move(server_end2)); |
| Buttons::SyncClient client2(std::move(client_end2)); |
| auto result2_1 = client2.RegisterNotify(1 << static_cast<uint8_t>(ButtonType::MUTE)); |
| EXPECT_OK(result2_1.status()); |
| |
| { // Scoping for Client 1 |
| // Client 1 |
| zx::channel client_end1, server_end1; |
| EXPECT_OK(zx::channel::create(0, &client_end1, &server_end1)); |
| device.GetButtonsFn()->ButtonsGetChannel(std::move(server_end1)); |
| Buttons::SyncClient client1(std::move(client_end1)); |
| auto result1_1 = client1.RegisterNotify(1 << static_cast<uint8_t>(ButtonType::MUTE)); |
| EXPECT_OK(result1_1.status()); |
| |
| // Interrupts |
| device.FakeInterrupt(ButtonType::MUTE); |
| device.DebounceWait(); |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::MUTE && message->pressed == true) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client1.HandleEvents(event_handlers).ok()); |
| } |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::MUTE && message->pressed == true) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client2.HandleEvents(event_handlers).ok()); |
| } |
| device.FakeInterrupt(ButtonType::MUTE); |
| device.DebounceWait(); |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::MUTE && message->pressed == false) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client1.HandleEvents(event_handlers).ok()); |
| } |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::MUTE && message->pressed == false) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client2.HandleEvents(event_handlers).ok()); |
| } |
| auto result1_2 = client1.RegisterNotify((1 << static_cast<uint8_t>(ButtonType::VOLUME_UP)) | |
| (1 << static_cast<uint8_t>(ButtonType::MUTE))); |
| EXPECT_OK(result1_2.status()); |
| auto result2_2 = client2.RegisterNotify(1 << static_cast<uint8_t>(ButtonType::VOLUME_UP)); |
| EXPECT_OK(result2_2.status()); |
| device.FakeInterrupt(ButtonType::MUTE); |
| device.DebounceWait(); |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::MUTE && message->pressed == true) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client1.HandleEvents(event_handlers).ok()); |
| } |
| device.FakeInterrupt(ButtonType::VOLUME_UP); |
| device.DebounceWait(); |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::VOLUME_UP && message->pressed == false) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client1.HandleEvents(event_handlers).ok()); |
| } |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::VOLUME_UP && message->pressed == false) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client2.HandleEvents(event_handlers).ok()); |
| } |
| } // Close Client 1 |
| |
| device.Wait(); |
| device.FakeInterrupt(ButtonType::VOLUME_UP); |
| device.DebounceWait(); |
| { |
| Buttons::EventHandlers event_handlers{ |
| .on_notify = |
| [](Buttons::OnNotifyResponse* message) { |
| if (message->type == ButtonType::VOLUME_UP && message->pressed == true) { |
| return ZX_OK; |
| } |
| return ZX_ERR_INTERNAL; |
| }, |
| .unknown = [] { return ZX_ERR_INVALID_ARGS; }, |
| }; |
| EXPECT_TRUE(client2.HandleEvents(event_handlers).ok()); |
| } |
| } // Close Client 2 |
| |
| device.Wait(); |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, DuplicateReports) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_duplicate, countof(gpios_duplicate), buttons_duplicate, |
| countof(buttons_duplicate))); |
| |
| // Holding FDR (VOL_UP and VOL_DOWN), then release VOL_UP, should only get one report. |
| // Reconfigure Polarity due to interrupt. |
| device.GetGpio(2) |
| .ExpectRead(ZX_OK, 1) // Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1); // Still pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.FakeInterrupt(ButtonType::RESET); |
| device.DebounceWait(); |
| |
| device.GetGpio(0) |
| .ExpectRead(ZX_OK, 0) // Not Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Keep the correct polarity. |
| .ExpectRead(ZX_OK, 0); // Still not pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.FakeInterrupt(ButtonType::VOLUME_UP); |
| device.DebounceWait(); |
| |
| device.GetGpio(2) |
| .ExpectRead(ZX_OK, 0) // Not Pushed. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Keep the correct polarity. |
| .ExpectRead(ZX_OK, 0); // Still not pushed, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.FakeInterrupt(ButtonType::RESET); |
| device.DebounceWait(); |
| |
| hidbus_ifc_protocol_ops_t ops = {}; |
| ops.io_queue = [](void* ctx, const void* buffer, size_t size, zx_time_t time) { |
| buttons_input_rpt_t reports[2]; |
| reports[0] = {}; |
| reports[0].rpt_id = 1; |
| reports[0].volume_up = 1; // Pushed. |
| reports[0].volume_down = 1; // Pushed. |
| reports[0].reset = 1; // Pushed. |
| reports[1] = {}; |
| reports[1].rpt_id = 1; |
| reports[1].volume_up = 0; // Unpushed. |
| reports[1].volume_down = 1; // Pushed. |
| reports[1].reset = 0; // Unpushed. |
| ASSERT_BYTES_EQ(buffer, reports, size); |
| EXPECT_EQ(size, sizeof(reports)); |
| }; |
| hidbus_ifc_protocol_t protocol = {}; |
| protocol.ops = &ops; |
| device.HidbusStart(&protocol); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| TEST(HidButtonsTest, CamMute) { |
| HidButtonsDeviceTest device; |
| EXPECT_OK(device.BindTest(gpios_multiple, countof(gpios_multiple), buttons_multiple, |
| countof(buttons_multiple))); |
| |
| hidbus_ifc_protocol_ops_t ops = {}; |
| ops.io_queue = [](void* ctx, const void* buffer, size_t size, zx_time_t time) { |
| buttons_input_rpt_t report_volume_up = {}; |
| report_volume_up.rpt_id = 1; |
| report_volume_up.camera_access_disabled = 1; |
| ASSERT_BYTES_EQ(buffer, &report_volume_up, size); |
| EXPECT_EQ(size, sizeof(report_volume_up)); |
| }; |
| hidbus_ifc_protocol_t protocol = {}; |
| protocol.ops = &ops; |
| EXPECT_OK(device.HidbusStart(&protocol)); |
| |
| device.GetGpio(2) |
| .ExpectRead(ZX_OK, 1) // On. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_LOW) // Turn the polarity. |
| .ExpectRead(ZX_OK, 1); // Still on, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 1); // Read value to prepare report. |
| device.FakeInterrupt(ButtonType::CAM_MUTE); |
| device.DebounceWait(); |
| |
| device.HidbusStop(); |
| |
| ops.io_queue = [](void* ctx, const void* buffer, size_t size, zx_time_t time) { |
| buttons_input_rpt_t report_volume_up = {}; |
| report_volume_up.rpt_id = 1; |
| report_volume_up.camera_access_disabled = 0; |
| ASSERT_BYTES_EQ(buffer, &report_volume_up, size); |
| EXPECT_EQ(size, sizeof(report_volume_up)); |
| }; |
| protocol.ops = &ops; |
| EXPECT_OK(device.HidbusStart(&protocol)); |
| |
| device.GetGpio(2) |
| .ExpectRead(ZX_OK, 0) // Off. |
| .ExpectSetPolarity(ZX_OK, GPIO_POLARITY_HIGH) // Turn the polarity. |
| .ExpectRead(ZX_OK, 0); // Still off, ok to continue. |
| device.GetGpio(0).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(1).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.GetGpio(2).ExpectRead(ZX_OK, 0); // Read value to prepare report. |
| device.FakeInterrupt(ButtonType::CAM_MUTE); |
| device.DebounceWait(); |
| |
| device.ShutDownTest(); |
| ASSERT_NO_FATAL_FAILURES(device.VerifyAndClearGpios()); |
| } |
| |
| } // namespace buttons |