blob: 3ee4888f4a4c13920d37aa37acbe7b0aa0bfadbf [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 "src/ui/input/drivers/hid/hid.h"
#include <fidl/fuchsia.hardware.hidbus/cpp/wire_test_base.h>
#include <lib/driver/testing/cpp/driver_test.h>
#include <lib/hid/ambient-light.h>
#include <lib/hid/boot.h>
#include <lib/hid/paradise.h>
#include <gtest/gtest.h>
#include "src/lib/testing/predicates/status.h"
namespace hid_driver {
namespace fhidbus = fuchsia_hardware_hidbus;
namespace finput = fuchsia_hardware_input;
class FakeHidbus : public fidl::testing::WireTestBase<fhidbus::Hidbus> {
public:
fhidbus::Service::InstanceHandler GetInstanceHandler() {
return fhidbus::Service::InstanceHandler({
.device =
[this](fidl::ServerEnd<fhidbus::Hidbus> server) {
binding_.AddBinding(fdf::Dispatcher::GetCurrent()->async_dispatcher(),
std::move(server), this, fidl::kIgnoreBindingClosure);
},
});
}
void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override {
ASSERT_TRUE(false);
}
const fhidbus::HidInfo& Query() { return info_; }
void Query(QueryCompleter::Sync& completer) override {
fidl::Arena<> arena;
completer.ReplySuccess(fidl::ToWire(arena, info_));
}
void Start(StartCompleter::Sync& completer) override {
if (start_status_ != ZX_OK) {
completer.ReplyError(start_status_);
return;
}
completer.ReplySuccess();
}
void Stop(StopCompleter::Sync& completer) override {}
void SetDescriptor(fhidbus::wire::HidbusSetDescriptorRequest* request,
SetDescriptorCompleter::Sync& completer) override {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void GetDescriptor(fhidbus::wire::HidbusGetDescriptorRequest* request,
GetDescriptorCompleter::Sync& completer) override {
completer.ReplySuccess(
fidl::VectorView<uint8_t>::FromExternal(report_desc_.data(), report_desc_.size()));
}
void GetReport(fhidbus::wire::HidbusGetReportRequest* request,
GetReportCompleter::Sync& completer) override {
if (request->rpt_id != last_set_report_id_) {
completer.ReplyError(ZX_ERR_INTERNAL);
return;
}
if (request->len < last_set_report_.size()) {
completer.ReplyError(ZX_ERR_BUFFER_TOO_SMALL);
}
completer.ReplySuccess(
fidl::VectorView<uint8_t>::FromExternal(last_set_report_.data(), last_set_report_.size()));
}
void SetReport(fhidbus::wire::HidbusSetReportRequest* request,
SetReportCompleter::Sync& completer) override {
last_set_report_id_ = request->rpt_id;
last_set_report_ =
std::vector<uint8_t>(request->data.data(), request->data.data() + request->data.count());
completer.ReplySuccess();
}
void GetIdle(fhidbus::wire::HidbusGetIdleRequest* request,
GetIdleCompleter::Sync& completer) override {
completer.ReplySuccess(0);
}
void SetIdle(fhidbus::wire::HidbusSetIdleRequest* request,
SetIdleCompleter::Sync& completer) override {
completer.ReplySuccess();
}
void GetProtocol(GetProtocolCompleter::Sync& completer) override {
completer.ReplySuccess(hid_protocol_);
}
void SetProtocol(fhidbus::wire::HidbusSetProtocolRequest* request,
SetProtocolCompleter::Sync& completer) override {
hid_protocol_ = request->protocol;
completer.ReplySuccess();
}
void SetProtocol(fhidbus::wire::HidProtocol proto) { hid_protocol_ = proto; }
void SetHidInfo(fhidbus::wire::HidInfo info) { info_ = fidl::ToNatural(info); }
void SetStartStatus(zx_status_t status) { start_status_ = status; }
void SendReport(uint8_t* report_data, size_t report_size) {
SendReportWithTime(report_data, report_size, zx_clock_get_monotonic());
}
void SendReportWithTime(uint8_t* report_data, size_t report_size, zx_time_t time) {
binding_.ForEachBinding([&](const auto& binding) {
fidl::Arena arena;
auto result = fidl::WireSendEvent(binding)->OnReportReceived(
fhidbus::wire::Report::Builder(arena)
.buf(fidl::VectorView<uint8_t>::FromExternal(report_data, report_size))
.timestamp(time)
.Build());
ASSERT_TRUE(result.ok());
});
}
void SetDescriptor(const uint8_t* desc, size_t desc_len) {
report_desc_ = std::vector<uint8_t>(desc, desc + desc_len);
}
protected:
std::vector<uint8_t> report_desc_;
std::vector<uint8_t> last_set_report_;
uint8_t last_set_report_id_;
fhidbus::wire::HidProtocol hid_protocol_ = fhidbus::wire::HidProtocol::kReport;
fhidbus::HidInfo info_;
zx_status_t start_status_ = ZX_OK;
private:
fidl::ServerBindingGroup<fhidbus::Hidbus> binding_;
};
class HidDriverTestEnvironment : fdf_testing::Environment {
public:
zx::result<> Serve(fdf::OutgoingDirectory& to_driver_vfs) override {
return to_driver_vfs.AddService<fhidbus::Service>(fake_hidbus_.GetInstanceHandler());
}
FakeHidbus& fake_hidbus() { return fake_hidbus_; }
private:
FakeHidbus fake_hidbus_;
};
class TestConfig final {
public:
using DriverType = hid_driver::HidDriver;
using EnvironmentType = HidDriverTestEnvironment;
};
class HidDeviceTest : public ::testing::Test {
public:
void TearDown() override {
zx::result<> result = driver_test().StopDriver();
ASSERT_EQ(ZX_OK, result.status_value());
}
void SetupBootMouseDevice() {
driver_test().RunInEnvironmentTypeContext([](HidDriverTestEnvironment& env) {
size_t desc_size;
const uint8_t* boot_mouse_desc = get_boot_mouse_report_desc(&desc_size);
env.fake_hidbus().SetDescriptor(boot_mouse_desc, desc_size);
fidl::Arena<> arena;
env.fake_hidbus().SetHidInfo(fhidbus::wire::HidInfo::Builder(arena)
.boot_protocol(fhidbus::wire::HidBootProtocol::kPointer)
.vendor_id(0xabc)
.product_id(123)
.version(5)
.dev_num(0)
.polling_rate(0)
.Build());
});
}
fidl::ClientEnd<finput::Device> GetClient() {
auto endpoints = fidl::Endpoints<finput::Device>::Create();
auto result = driver_test().driver()->hiddev().CreateInstance(std::move(endpoints.server));
EXPECT_OK(result);
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = fidl::WireCall(endpoints.client)->GetReportsEvent();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
report_event_ = std::move(result.value()->event);
})
.is_ok());
return std::move(endpoints.client);
}
void ReadOneReport(fidl::WireSyncClient<finput::Device>& client, uint8_t* report_data,
size_t report_size, size_t* returned_size) {
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
ASSERT_OK(
report_event_.wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), nullptr));
auto result = client->ReadReport();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_TRUE(result.value()->report.has_buf());
ASSERT_TRUE(result.value()->report.buf().count() <= report_size);
for (size_t i = 0; i < result.value()->report.buf().count(); i++) {
report_data[i] = result.value()->report.buf()[i];
}
*returned_size = result.value()->report.buf().count();
})
.is_ok());
}
fdf_testing::ForegroundDriverTest<TestConfig>& driver_test() { return driver_test_; }
protected:
fdf_testing::ForegroundDriverTest<TestConfig> driver_test_;
zx::event report_event_;
};
TEST_F(HidDeviceTest, LifeTimeTest) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
}
TEST_F(HidDeviceTest, TestQuery) {
// Ids were chosen arbitrarily.
constexpr uint16_t kVendorId = 0xacbd;
constexpr uint16_t kProductId = 0xdcba;
constexpr uint16_t kVersion = 0x1234;
SetupBootMouseDevice();
driver_test().RunInEnvironmentTypeContext([](HidDriverTestEnvironment& env) {
fidl::Arena<> arena;
env.fake_hidbus().SetHidInfo(fhidbus::wire::HidInfo::Builder(arena)
.boot_protocol(fhidbus::wire::HidBootProtocol::kPointer)
.vendor_id(kVendorId)
.product_id(kProductId)
.version(kVersion)
.dev_num(0)
.polling_rate(0)
.Build());
});
ASSERT_TRUE(driver_test().StartDriver().is_ok());
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->Query();
ASSERT_OK(result.status());
ASSERT_EQ(kVendorId, result.value()->info.vendor_id());
ASSERT_EQ(kProductId, result.value()->info.product_id());
ASSERT_EQ(kVersion, result.value()->info.version());
})
.is_ok());
}
TEST_F(HidDeviceTest, BootMouseSendReport) {
SetupBootMouseDevice();
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
ASSERT_TRUE(driver_test().StartDriver().is_ok());
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(mouse_report, sizeof(mouse_report));
});
uint8_t returned_report[3] = {};
size_t actual;
ReadOneReport(client, returned_report, sizeof(returned_report), &actual);
ASSERT_EQ(actual, sizeof(returned_report));
for (size_t i = 0; i < actual; i++) {
ASSERT_EQ(returned_report[i], mouse_report[i]);
}
}
TEST_F(HidDeviceTest, BootMouseSendReportWithTime) {
SetupBootMouseDevice();
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
ASSERT_TRUE(driver_test().StartDriver().is_ok());
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
const zx_time_t kTimestamp = 0xabcd;
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReportWithTime(mouse_report, sizeof(mouse_report), kTimestamp);
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
ASSERT_OK(
report_event_.wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), nullptr));
auto result = client->ReadReport();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_TRUE(result.value()->report.has_timestamp());
ASSERT_EQ(result.value()->report.timestamp(), kTimestamp);
})
.is_ok());
ASSERT_NO_FATAL_FAILURE();
}
TEST_F(HidDeviceTest, BootMouseSendReportInPieces) {
SetupBootMouseDevice();
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
ASSERT_TRUE(driver_test().StartDriver().is_ok());
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(&mouse_report[0], sizeof(uint8_t));
env.fake_hidbus().SendReport(&mouse_report[1], sizeof(uint8_t));
env.fake_hidbus().SendReport(&mouse_report[2], sizeof(uint8_t));
});
uint8_t returned_report[3] = {};
size_t actual;
ReadOneReport(client, returned_report, sizeof(returned_report), &actual);
ASSERT_EQ(actual, sizeof(returned_report));
for (size_t i = 0; i < actual; i++) {
ASSERT_EQ(returned_report[i], mouse_report[i]);
}
}
TEST_F(HidDeviceTest, BootMouseSendMultipleReports) {
SetupBootMouseDevice();
uint8_t double_mouse_report[] = {0xDE, 0xAD, 0xBE, 0x12, 0x34, 0x56};
ASSERT_TRUE(driver_test().StartDriver().is_ok());
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
driver_test().RunInEnvironmentTypeContext([&double_mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(double_mouse_report, sizeof(double_mouse_report));
});
uint8_t returned_report[3] = {};
size_t actual;
// Read the first report.
ReadOneReport(client, returned_report, sizeof(returned_report), &actual);
ASSERT_EQ(actual, sizeof(returned_report));
for (size_t i = 0; i < actual; i++) {
ASSERT_EQ(returned_report[i], double_mouse_report[i]);
}
// Read the second report.
ReadOneReport(client, returned_report, sizeof(returned_report), &actual);
ASSERT_EQ(actual, sizeof(returned_report));
for (size_t i = 0; i < actual; i++) {
ASSERT_EQ(returned_report[i], double_mouse_report[i + 3]);
}
}
TEST_F(HidDeviceTest, FailToRegister) {
driver_test().RunInEnvironmentTypeContext([](HidDriverTestEnvironment& env) {
fidl::Arena<> arena;
env.fake_hidbus().SetHidInfo(fhidbus::wire::HidInfo::Builder(arena)
.boot_protocol(fhidbus::wire::HidBootProtocol::kOther)
.vendor_id(0)
.product_id(0)
.version(0)
.dev_num(0)
.polling_rate(0)
.Build());
env.fake_hidbus().SetStartStatus(ZX_ERR_INTERNAL);
});
ASSERT_EQ(driver_test().StartDriver().status_value(), ZX_ERR_INTERNAL);
}
TEST_F(HidDeviceTest, ReadReportSingleReport) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
// Send the reports.
const zx_time_t time = 0xabcd;
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReportWithTime(mouse_report, sizeof(mouse_report), time);
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->ReadReport();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_EQ(time, result.value()->report.timestamp());
ASSERT_EQ(sizeof(mouse_report), result.value()->report.buf().count());
for (size_t i = 0; i < result.value()->report.buf().count(); i++) {
EXPECT_EQ(mouse_report[i], result.value()->report.buf()[i]);
}
})
.is_ok());
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->ReadReport();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
ASSERT_EQ(result->error_value(), ZX_ERR_SHOULD_WAIT);
})
.is_ok());
}
TEST_F(HidDeviceTest, ReadReportDoubleReport) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
uint8_t double_mouse_report[] = {0xDE, 0xAD, 0xBE, 0x12, 0x34, 0x56};
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
// Send the reports.
const zx_time_t time = 0xabcd;
driver_test().RunInEnvironmentTypeContext([&double_mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReportWithTime(double_mouse_report, sizeof(double_mouse_report), time);
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->ReadReport();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_EQ(time, result.value()->report.timestamp());
ASSERT_EQ(sizeof(hid_boot_mouse_report_t),
result.value()->report.buf().count());
for (size_t i = 0; i < result.value()->report.buf().count(); i++) {
EXPECT_EQ(double_mouse_report[i], result.value()->report.buf()[i]);
}
})
.is_ok());
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->ReadReport();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_EQ(time, result.value()->report.timestamp());
ASSERT_EQ(sizeof(hid_boot_mouse_report_t),
result.value()->report.buf().count());
for (size_t i = 0; i < result.value()->report.buf().count(); i++) {
EXPECT_EQ(double_mouse_report[i + sizeof(hid_boot_mouse_report_t)],
result.value()->report.buf()[i]);
}
})
.is_ok());
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->ReadReport();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
ASSERT_EQ(result->error_value(), ZX_ERR_SHOULD_WAIT);
})
.is_ok());
}
TEST_F(HidDeviceTest, ReadReportsSingleReport) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
// Send the reports.
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(mouse_report, sizeof(mouse_report));
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->ReadReports();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_EQ(sizeof(mouse_report), result.value()->data.count());
for (size_t i = 0; i < result.value()->data.count(); i++) {
EXPECT_EQ(mouse_report[i], result.value()->data[i]);
}
})
.is_ok());
}
TEST_F(HidDeviceTest, ReadReportsDoubleReport) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
uint8_t double_mouse_report[] = {0xDE, 0xAD, 0xBE, 0x12, 0x34, 0x56};
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
// Send the reports.
driver_test().RunInEnvironmentTypeContext([&double_mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(double_mouse_report, sizeof(double_mouse_report));
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->ReadReports();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_EQ(sizeof(double_mouse_report), result.value()->data.count());
for (size_t i = 0; i < result.value()->data.count(); i++) {
EXPECT_EQ(double_mouse_report[i], result.value()->data[i]);
}
})
.is_ok());
}
TEST_F(HidDeviceTest, ReadReportsBlockingWait) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
auto dispatcher = driver_test().runtime().StartBackgroundDispatcher();
ASSERT_OK(async::PostTask(dispatcher->async_dispatcher(), [&]() {
sleep(1);
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(mouse_report, sizeof(mouse_report));
});
}));
uint8_t returned_report[3] = {};
size_t actual;
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
ReadOneReport(client, returned_report, sizeof(returned_report), &actual);
ASSERT_EQ(sizeof(mouse_report), actual);
for (size_t i = 0; i < actual; i++) {
EXPECT_EQ(mouse_report[i], returned_report[i]);
}
}
// Test that only whole reports get sent through.
TEST_F(HidDeviceTest, ReadReportsOneAndAHalfReports) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
// Send the report.
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(mouse_report, sizeof(mouse_report));
});
// Send a half of a report.
driver_test().RunInEnvironmentTypeContext([](HidDriverTestEnvironment& env) {
uint8_t half_report[] = {0xDE, 0xAD};
env.fake_hidbus().SendReport(half_report, sizeof(half_report));
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->ReadReports();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_EQ(sizeof(mouse_report), result.value()->data.count());
for (size_t i = 0; i < result.value()->data.count(); i++) {
EXPECT_EQ(mouse_report[i], result.value()->data[i]);
}
})
.is_ok());
}
// This tests that we can set the boot mode for a non-boot device, and that the device will
// have it's report descriptor set to the boot mode descriptor. For this, we will take an
// arbitrary descriptor and claim that it can be set to a boot-mode keyboard. We then
// test that the report descriptor we get back is for the boot keyboard.
// (The descriptor doesn't matter, as long as a device claims its a boot device it should
// support this transformation in hardware).
TEST_F(HidDeviceTest, SettingBootModeMouse) {
driver_test().RunInEnvironmentTypeContext([](HidDriverTestEnvironment& env) {
size_t desc_size;
const uint8_t* desc = get_paradise_touchpad_v1_report_desc(&desc_size);
env.fake_hidbus().SetDescriptor(desc, desc_size);
fidl::Arena<> arena;
env.fake_hidbus().SetHidInfo(fhidbus::wire::HidInfo::Builder(arena)
.boot_protocol(fhidbus::wire::HidBootProtocol::kPointer)
.vendor_id(0)
.product_id(0)
.version(0)
.dev_num(0)
.polling_rate(0)
.Build());
// Set the device to boot protocol.
env.fake_hidbus().SetProtocol(fhidbus::wire::HidProtocol::kBoot);
});
ASSERT_TRUE(driver_test().StartDriver().is_ok());
size_t boot_mouse_desc_size;
const uint8_t* boot_mouse_desc = get_boot_mouse_report_desc(&boot_mouse_desc_size);
ASSERT_EQ(boot_mouse_desc_size, driver_test().driver()->hiddev().GetReportDescLen());
const uint8_t* received_desc = driver_test().driver()->hiddev().GetReportDesc();
for (size_t i = 0; i < boot_mouse_desc_size; i++) {
ASSERT_EQ(boot_mouse_desc[i], received_desc[i]);
}
}
// This tests that we can set the boot mode for a non-boot device, and that the device will
// have it's report descriptor set to the boot mode descriptor. For this, we will take an
// arbitrary descriptor and claim that it can be set to a boot-mode keyboard. We then
// test that the report descriptor we get back is for the boot keyboard.
// (The descriptor doesn't matter, as long as a device claims its a boot device it should
// support this transformation in hardware).
TEST_F(HidDeviceTest, SettingBootModeKbd) {
driver_test().RunInEnvironmentTypeContext([](HidDriverTestEnvironment& env) {
size_t desc_size;
const uint8_t* desc = get_paradise_touchpad_v1_report_desc(&desc_size);
env.fake_hidbus().SetDescriptor(desc, desc_size);
fidl::Arena<> arena;
env.fake_hidbus().SetHidInfo(fhidbus::wire::HidInfo::Builder(arena)
.boot_protocol(fhidbus::wire::HidBootProtocol::kKbd)
.vendor_id(0)
.product_id(0)
.version(0)
.dev_num(0)
.polling_rate(0)
.Build());
// Set the device to boot protocol.
env.fake_hidbus().SetProtocol(fhidbus::wire::HidProtocol::kBoot);
});
ASSERT_TRUE(driver_test().StartDriver().is_ok());
size_t boot_kbd_desc_size;
const uint8_t* boot_kbd_desc = get_boot_kbd_report_desc(&boot_kbd_desc_size);
ASSERT_EQ(boot_kbd_desc_size, driver_test().driver()->hiddev().GetReportDescLen());
const uint8_t* received_desc = driver_test().driver()->hiddev().GetReportDesc();
for (size_t i = 0; i < boot_kbd_desc_size; i++) {
ASSERT_EQ(boot_kbd_desc[i], received_desc[i]);
}
}
TEST_F(HidDeviceTest, GetDescriptor) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
EXPECT_TRUE(
driver_test()
.RunOnBackgroundDispatcherSync([&]() {
size_t known_size;
const uint8_t* known_descriptor = get_boot_mouse_report_desc(&known_size);
auto result = client->GetReportDesc();
ASSERT_TRUE(result.ok());
ASSERT_EQ(known_size, result->desc.count());
ASSERT_EQ(std::vector(known_descriptor, known_descriptor + known_size),
std::vector(result->desc.data(), result->desc.data() + result->desc.count()));
})
.is_ok());
}
TEST_F(HidDeviceTest, GetSetReport) {
driver_test().RunInEnvironmentTypeContext([](HidDriverTestEnvironment& env) {
const uint8_t* desc;
size_t desc_size = get_ambient_light_report_desc(&desc);
env.fake_hidbus().SetDescriptor(desc, desc_size);
fidl::Arena<> arena;
env.fake_hidbus().SetHidInfo(fhidbus::wire::HidInfo::Builder(arena)
.boot_protocol(fhidbus::wire::HidBootProtocol::kNone)
.vendor_id(0)
.product_id(0)
.version(0)
.dev_num(0)
.polling_rate(0)
.Build());
});
ASSERT_TRUE(driver_test().StartDriver().is_ok());
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
ambient_light_feature_rpt_t feature_report = {};
feature_report.rpt_id = AMBIENT_LIGHT_RPT_ID_FEATURE;
// Below value are chosen arbitrarily.
feature_report.state = 100;
feature_report.interval_ms = 50;
feature_report.threshold_high = 40;
feature_report.threshold_low = 10;
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->SetReport(
fhidbus::wire::ReportType::kFeature, AMBIENT_LIGHT_RPT_ID_FEATURE,
fidl::VectorView<uint8_t>::FromExternal(
reinterpret_cast<uint8_t*>(&feature_report), sizeof(feature_report)));
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
})
.is_ok());
EXPECT_TRUE(
driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->GetReport(fhidbus::wire::ReportType::kFeature,
AMBIENT_LIGHT_RPT_ID_FEATURE);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_EQ(sizeof(feature_report), result->value()->report.count());
ASSERT_EQ(
std::vector(reinterpret_cast<uint8_t*>(&feature_report),
reinterpret_cast<uint8_t*>(&feature_report) + sizeof(feature_report)),
std::vector(result->value()->report.data(),
result->value()->report.data() + result->value()->report.count()));
})
.is_ok());
}
// Tests that a device with too large reports don't cause buffer overruns.
TEST_F(HidDeviceTest, GetReportBufferOverrun) {
driver_test().RunInEnvironmentTypeContext([](HidDriverTestEnvironment& env) {
const uint8_t desc[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
0x05, 0x09, // Usage Page (Button)
0x09, 0x30, // Usage (0x30)
0x97, 0x00, 0xF0, 0x00, 0x00, // Report Count (65279)
0x75, 0x08, // Report Size (8)
0x25, 0x01, // Logical Maximum (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
// 22 bytes
};
size_t desc_size = sizeof(desc);
env.fake_hidbus().SetDescriptor(desc, desc_size);
fidl::Arena<> arena;
env.fake_hidbus().SetHidInfo(fhidbus::wire::HidInfo::Builder(arena)
.boot_protocol(fhidbus::wire::HidBootProtocol::kNone)
.vendor_id(0)
.product_id(0)
.version(0)
.dev_num(0)
.polling_rate(0)
.Build());
});
ASSERT_TRUE(driver_test().StartDriver().is_ok());
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->GetReport(fhidbus::wire::ReportType::kInput, 0);
ASSERT_TRUE(result.ok());
ASSERT_FALSE(result->is_ok());
EXPECT_EQ(result->error_value(), ZX_ERR_INTERNAL);
})
.is_ok());
}
TEST_F(HidDeviceTest, DeviceReportReaderSingleReport) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
fidl::WireSyncClient<finput::DeviceReportsReader> reader;
{
auto endpoints = fidl::Endpoints<finput::DeviceReportsReader>::Create();
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->GetDeviceReportsReader(std::move(endpoints.server));
ASSERT_OK(result.status());
reader = fidl::WireSyncClient<finput::DeviceReportsReader>(
std::move(endpoints.client));
})
.is_ok());
}
// Send the reports.
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(mouse_report, sizeof(mouse_report));
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto response = reader->ReadReports();
ASSERT_OK(response.status());
ASSERT_FALSE(response->is_error());
auto result = response->value();
ASSERT_EQ(result->reports.count(), 1UL);
ASSERT_EQ(result->reports[0].buf().count(), sizeof(mouse_report));
for (size_t i = 0; i < result->reports[0].buf().count(); i++) {
EXPECT_EQ(mouse_report[i], result->reports[0].buf()[i]);
}
})
.is_ok());
}
TEST_F(HidDeviceTest, DeviceReportReaderDoubleReport) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
uint8_t mouse_report_two[] = {0xDE, 0xAD, 0xBE};
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
fidl::WireSyncClient<finput::DeviceReportsReader> reader;
{
auto endpoints = fidl::Endpoints<finput::DeviceReportsReader>::Create();
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->GetDeviceReportsReader(std::move(endpoints.server));
ASSERT_OK(result.status());
reader = fidl::WireSyncClient<finput::DeviceReportsReader>(
std::move(endpoints.client));
})
.is_ok());
}
// Send the reports.
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(mouse_report, sizeof(mouse_report));
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
report_event_.wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), nullptr);
report_event_.signal(ZX_USER_SIGNAL_0, 0);
})
.is_ok());
driver_test().RunInEnvironmentTypeContext([&mouse_report_two](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(mouse_report_two, sizeof(mouse_report_two));
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
report_event_.wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), nullptr);
auto response = reader->ReadReports();
ASSERT_OK(response.status());
ASSERT_FALSE(response->is_error());
auto result = response->value();
ASSERT_EQ(result->reports.count(), 2UL);
ASSERT_EQ(result->reports[0].buf().count(), sizeof(mouse_report));
for (size_t i = 0; i < result->reports[0].buf().count(); i++) {
EXPECT_EQ(mouse_report[i], result->reports[0].buf()[i]);
}
ASSERT_EQ(result->reports[1].buf().count(), sizeof(mouse_report));
for (size_t i = 0; i < result->reports[1].buf().count(); i++) {
EXPECT_EQ(mouse_report[i], result->reports[1].buf()[i]);
}
})
.is_ok());
}
TEST_F(HidDeviceTest, DeviceReportReaderTwoClients) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
fidl::WireSyncClient<finput::DeviceReportsReader> reader1;
fidl::WireSyncClient<finput::DeviceReportsReader> reader2;
{
auto endpoints1 = fidl::Endpoints<finput::DeviceReportsReader>::Create();
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result1 = client->GetDeviceReportsReader(std::move(endpoints1.server));
ASSERT_OK(result1.status());
reader1 = fidl::WireSyncClient<finput::DeviceReportsReader>(
std::move(endpoints1.client));
})
.is_ok());
auto endpoints2 = fidl::Endpoints<finput::DeviceReportsReader>::Create();
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result2 = client->GetDeviceReportsReader(std::move(endpoints2.server));
ASSERT_OK(result2.status());
reader2 = fidl::WireSyncClient<finput::DeviceReportsReader>(
std::move(endpoints2.client));
})
.is_ok());
}
// Send the report.
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(mouse_report, sizeof(mouse_report));
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto response = reader1->ReadReports();
ASSERT_OK(response.status());
ASSERT_FALSE(response->is_error());
auto result = response->value();
ASSERT_EQ(result->reports.count(), 1UL);
ASSERT_EQ(result->reports[0].buf().count(), sizeof(mouse_report));
for (size_t i = 0; i < result->reports[0].buf().count(); i++) {
EXPECT_EQ(mouse_report[i], result->reports[0].buf()[i]);
}
})
.is_ok());
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto response = reader2->ReadReports();
ASSERT_OK(response.status());
ASSERT_FALSE(response->is_error());
auto result = response->value();
ASSERT_EQ(result->reports.count(), 1UL);
ASSERT_EQ(result->reports[0].buf().count(), sizeof(mouse_report));
for (size_t i = 0; i < result->reports[0].buf().count(); i++) {
EXPECT_EQ(mouse_report[i], result->reports[0].buf()[i]);
}
})
.is_ok());
}
// Test that only whole reports get sent through.
TEST_F(HidDeviceTest, DeviceReportReaderOneAndAHalfReports) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
fidl::WireSyncClient<finput::DeviceReportsReader> reader;
{
auto endpoints = fidl::Endpoints<finput::DeviceReportsReader>::Create();
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->GetDeviceReportsReader(std::move(endpoints.server));
ASSERT_OK(result.status());
reader = fidl::WireSyncClient<finput::DeviceReportsReader>(
std::move(endpoints.client));
})
.is_ok());
}
// Send the report.
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(mouse_report, sizeof(mouse_report));
});
// Send a half of a report.
uint8_t half_report[] = {0xDE, 0xAD};
driver_test().RunInEnvironmentTypeContext([&half_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(half_report, sizeof(half_report));
});
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto response = reader->ReadReports();
ASSERT_OK(response.status());
ASSERT_FALSE(response->is_error());
auto result = response->value();
ASSERT_EQ(result->reports.count(), 1UL);
ASSERT_EQ(sizeof(mouse_report), result->reports[0].buf().count());
for (size_t i = 0; i < result->reports[0].buf().count(); i++) {
EXPECT_EQ(mouse_report[i], result->reports[0].buf()[i]);
}
})
.is_ok());
}
TEST_F(HidDeviceTest, DeviceReportReaderHangingGet) {
SetupBootMouseDevice();
ASSERT_TRUE(driver_test().StartDriver().is_ok());
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
auto client = fidl::WireSyncClient<finput::Device>(GetClient());
fidl::WireSyncClient<finput::DeviceReportsReader> reader;
{
auto endpoints = fidl::Endpoints<finput::DeviceReportsReader>::Create();
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto result = client->GetDeviceReportsReader(std::move(endpoints.server));
ASSERT_OK(result.status());
reader = fidl::WireSyncClient<finput::DeviceReportsReader>(
std::move(endpoints.client));
})
.is_ok());
}
auto dispatcher = driver_test().runtime().StartBackgroundDispatcher();
ASSERT_OK(async::PostTask(dispatcher->async_dispatcher(), [&]() {
sleep(1);
driver_test().RunInEnvironmentTypeContext([&mouse_report](HidDriverTestEnvironment& env) {
env.fake_hidbus().SendReport(mouse_report, sizeof(mouse_report));
});
}));
EXPECT_TRUE(driver_test()
.RunOnBackgroundDispatcherSync([&]() {
auto response = reader->ReadReports();
ASSERT_OK(response.status());
ASSERT_FALSE(response->is_error());
auto result = response->value();
ASSERT_EQ(result->reports.count(), 1UL);
ASSERT_EQ(sizeof(mouse_report), result->reports[0].buf().count());
for (size_t i = 0; i < result->reports[0].buf().count(); i++) {
EXPECT_EQ(mouse_report[i], result->reports[0].buf()[i]);
}
})
.is_ok());
}
} // namespace hid_driver