blob: 04c694ff68f6731d459a436a87609f4eb396f67e [file] [log] [blame]
// 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