blob: 7da71a8891feef46426e06179c26e8d34af98d9c [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.h"
#include <lib/fake_ddk/fake_ddk.h>
#include <unistd.h>
#include <thread>
#include <vector>
#include <ddktl/protocol/hidbus.h>
#include <hid/ambient-light.h>
#include <hid/boot.h>
#include <hid/paradise.h>
#include <zxtest/zxtest.h>
namespace hid_driver {
struct ProtocolDeviceOps {
const zx_protocol_device_t* ops;
void* ctx;
};
// Create our own Fake Ddk Bind class. We want to save the last device arguments that
// have been seen, so the test can get ahold of the instance device and test
// Reads and Writes on it.
class Binder : public fake_ddk::Bind {
public:
zx_status_t DeviceAdd(zx_driver_t* drv, zx_device_t* parent, device_add_args_t* args,
zx_device_t** out) override {
zx_status_t status;
if (args && args->ops) {
if (args->ops->message) {
if ((status = fidl_.SetMessageOp(args->ctx, args->ops->message)) < 0) {
return status;
}
}
}
*out = fake_ddk::kFakeDevice;
add_called_ = true;
last_ops_.ctx = args->ctx;
last_ops_.ops = args->ops;
return ZX_OK;
}
ProtocolDeviceOps GetLastDeviceOps() { return last_ops_; }
private:
ProtocolDeviceOps last_ops_;
};
class FakeHidbus : public ddk::HidbusProtocol<FakeHidbus> {
public:
FakeHidbus() : proto_({&hidbus_protocol_ops_, this}) {}
zx_status_t HidbusQuery(uint32_t options, hid_info_t* out_info) {
*out_info = info_;
return ZX_OK;
}
void SetHidInfo(hid_info_t info) { info_ = info; }
void SetStartStatus(zx_status_t status) { start_status_ = status; }
zx_status_t HidbusStart(const hidbus_ifc_protocol_t* ifc) {
if (start_status_ != ZX_OK) {
return start_status_;
}
ifc_ = *ifc;
return ZX_OK;
}
void SendReport(const uint8_t* report_data, size_t report_size) {
ASSERT_NE(ifc_.ops, nullptr);
ifc_.ops->io_queue(ifc_.ctx, report_data, report_size, zx_clock_get_monotonic());
}
void SendReportWithTime(const uint8_t* report_data, size_t report_size, zx_time_t time) {
ASSERT_NE(ifc_.ops, nullptr);
ifc_.ops->io_queue(ifc_.ctx, report_data, report_size, time);
}
void HidbusStop() { ifc_.ops = nullptr; }
zx_status_t HidbusGetDescriptor(hid_description_type_t desc_type, void* out_data_buffer,
size_t data_size, size_t* out_data_actual) {
if (data_size < report_desc_.size()) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
memcpy(out_data_buffer, report_desc_.data(), report_desc_.size());
*out_data_actual = report_desc_.size();
return ZX_OK;
}
void SetDescriptor(const uint8_t* desc, size_t desc_len) {
report_desc_ = std::vector<uint8_t>(desc, desc + desc_len);
}
zx_status_t HidbusGetReport(hid_report_type_t rpt_type, uint8_t rpt_id, void* out_data_buffer,
size_t data_size, size_t* out_data_actual) {
if (rpt_id != last_set_report_id_) {
return ZX_ERR_INTERNAL;
}
if (data_size < last_set_report_.size()) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
memcpy(out_data_buffer, last_set_report_.data(), last_set_report_.size());
*out_data_actual = last_set_report_.size();
return ZX_OK;
}
zx_status_t HidbusSetReport(hid_report_type_t rpt_type, uint8_t rpt_id, const void* data_buffer,
size_t data_size) {
last_set_report_id_ = rpt_id;
auto data_bytes = reinterpret_cast<const uint8_t*>(data_buffer);
last_set_report_ = std::vector<uint8_t>(data_bytes, data_bytes + data_size);
return ZX_OK;
}
zx_status_t HidbusGetIdle(uint8_t rpt_id, uint8_t* out_duration) {
*out_duration = 0;
return ZX_OK;
}
zx_status_t HidbusSetIdle(uint8_t rpt_id, uint8_t duration) { return ZX_OK; }
zx_status_t HidbusGetProtocol(hid_protocol_t* out_protocol) {
*out_protocol = hid_protocol_;
return ZX_OK;
}
zx_status_t HidbusSetProtocol(hid_protocol_t protocol) {
hid_protocol_ = protocol;
return ZX_OK;
}
hidbus_protocol_t* GetProto() { return &proto_; }
protected:
std::vector<uint8_t> report_desc_;
std::vector<uint8_t> last_set_report_;
uint8_t last_set_report_id_;
hid_protocol_t hid_protocol_ = HID_PROTOCOL_REPORT;
hidbus_protocol_t proto_ = {};
hid_info_t info_ = {};
hidbus_ifc_protocol_t ifc_;
zx_status_t start_status_ = ZX_OK;
};
class HidDeviceTest : public zxtest::Test {
public:
void SetUp() override {
client_ = ddk::HidbusProtocolClient(fake_hidbus_.GetProto());
device_ = new HidDevice(fake_ddk::kFakeParent);
// Each test is responsible for calling Bind().
}
void TearDown() override {
TeardownInstanceDriver();
device_->DdkAsyncRemove();
EXPECT_TRUE(ddk_.Ok());
// This should delete the object, which means this test should not leak.
device_->DdkRelease();
}
void SetupBootMouseDevice() {
size_t desc_size;
const uint8_t* boot_mouse_desc = get_boot_mouse_report_desc(&desc_size);
fake_hidbus_.SetDescriptor(boot_mouse_desc, desc_size);
hid_info_t info = {};
info.device_class = HID_DEVICE_CLASS_POINTER;
info.boot_device = true;
fake_hidbus_.SetHidInfo(info);
}
void SetupInstanceDriver() {
ASSERT_OK(device_->DdkOpen(&instance_driver_, 0));
instance_ops_ = ddk_.GetLastDeviceOps();
sync_client_ =
llcpp::fuchsia::hardware::input::Device::SyncClient(std::move(ddk_.FidlClient()));
auto result = sync_client_->GetReportsEvent();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
report_event_ = std::move(result->event);
}
void TeardownInstanceDriver() {
if (instance_driver_ == nullptr) {
return;
}
instance_ops_.ops->close(instance_ops_.ctx, 0);
instance_ops_.ops->release(instance_ops_.ctx);
instance_driver_ = nullptr;
}
zx_status_t ReadOneReport(uint8_t* report_data, size_t report_size, size_t* returned_size) {
zx_status_t status = report_event_.wait_one(DEV_STATE_READABLE, zx::time::infinite(), nullptr);
if (status != ZX_OK) {
return status;
}
auto result = sync_client_->ReadReport();
status = result.status();
if (status != ZX_OK) {
return status;
}
status = result->status;
if (status != ZX_OK) {
return status;
}
if (result->data.count() > report_size) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
for (size_t i = 0; i < result->data.count(); i++) {
report_data[i] = result->data[i];
}
*returned_size = result->data.count();
return ZX_OK;
}
protected:
zx_device_t* instance_driver_ = nullptr;
ProtocolDeviceOps instance_ops_;
std::optional<llcpp::fuchsia::hardware::input::Device::SyncClient> sync_client_;
zx::event report_event_;
HidDevice* device_;
Binder ddk_;
FakeHidbus fake_hidbus_;
ddk::HidbusProtocolClient client_;
};
TEST_F(HidDeviceTest, LifeTimeTest) {
SetupBootMouseDevice();
ASSERT_OK(device_->Bind(client_));
}
TEST_F(HidDeviceTest, TestQuery) {
// Ids were chosen arbitrarily.
constexpr uint16_t kVendorId = 0xacbd;
constexpr uint16_t kProductId = 0xdcba;
constexpr uint16_t kVersion = 0x1234;
SetupBootMouseDevice();
hid_info_t info = {};
info.device_class = HID_DEVICE_CLASS_POINTER;
info.boot_device = true;
info.vendor_id = kVendorId;
info.product_id = kProductId;
info.version = kVersion;
fake_hidbus_.SetHidInfo(info);
ASSERT_OK(device_->Bind(client_));
zx_device_t* open_dev;
ASSERT_OK(device_->DdkOpen(&open_dev, 0));
// Opening the device created an instance device to be created, and we can
// get its arguments here.
ProtocolDeviceOps dev_ops = ddk_.GetLastDeviceOps();
auto sync_client =
llcpp::fuchsia::hardware::input::Device::SyncClient(std::move(ddk_.FidlClient()));
auto result = sync_client.GetDeviceIds();
ASSERT_OK(result.status());
llcpp::fuchsia::hardware::input::DeviceIds ids = result->ids;
ASSERT_EQ(kVendorId, ids.vendor_id);
ASSERT_EQ(kProductId, ids.product_id);
ASSERT_EQ(kVersion, ids.version);
// Close the instance device.
dev_ops.ops->close(dev_ops.ctx, 0);
dev_ops.ops->release(dev_ops.ctx);
}
TEST_F(HidDeviceTest, BootMouseSendReport) {
SetupBootMouseDevice();
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
ASSERT_OK(device_->Bind(client_));
SetupInstanceDriver();
fake_hidbus_.SendReport(mouse_report, sizeof(mouse_report));
uint8_t returned_report[3] = {};
size_t actual;
ASSERT_OK(ReadOneReport(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_OK(device_->Bind(client_));
// Regsiter a device listener
std::pair<sync_completion_t, zx_time_t> callback_data;
callback_data.second = 0xabcd;
hid_report_listener_protocol_ops_t listener_ops;
listener_ops.receive_report = [](void* ctx, const uint8_t* report_list, size_t report_count,
zx_time_t report_time) {
auto callback_data = static_cast<std::pair<sync_completion_t, zx_time_t>*>(ctx);
ASSERT_EQ(callback_data->second, report_time);
sync_completion_signal(&callback_data->first);
};
hid_report_listener_protocol_t listener = {&listener_ops, &callback_data};
device_->HidDeviceRegisterListener(&listener);
fake_hidbus_.SendReportWithTime(mouse_report, sizeof(mouse_report), callback_data.second);
sync_completion_wait_deadline(&callback_data.first, zx::time::infinite().get());
ASSERT_NO_FATAL_FAILURES();
}
TEST_F(HidDeviceTest, BootMouseSendReportInPieces) {
SetupBootMouseDevice();
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
ASSERT_OK(device_->Bind(client_));
SetupInstanceDriver();
fake_hidbus_.SendReport(&mouse_report[0], sizeof(uint8_t));
fake_hidbus_.SendReport(&mouse_report[1], sizeof(uint8_t));
fake_hidbus_.SendReport(&mouse_report[2], sizeof(uint8_t));
uint8_t returned_report[3] = {};
size_t actual;
ASSERT_OK(ReadOneReport(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_OK(device_->Bind(client_));
SetupInstanceDriver();
fake_hidbus_.SendReport(double_mouse_report, sizeof(double_mouse_report));
uint8_t returned_report[3] = {};
size_t actual;
// Read the first report.
ASSERT_OK(ReadOneReport(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.
ASSERT_OK(ReadOneReport(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(HidDeviceTest, FailToRegister) {
FakeHidbus fake_hidbus;
HidDevice device(fake_ddk::kFakeParent);
fake_hidbus.SetStartStatus(ZX_ERR_INTERNAL);
auto client = ddk::HidbusProtocolClient(fake_hidbus.GetProto());
ASSERT_EQ(device.Bind(client), ZX_ERR_INTERNAL);
}
TEST_F(HidDeviceTest, ReadReportSingleReport) {
SetupBootMouseDevice();
ASSERT_OK(device_->Bind(client_));
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
SetupInstanceDriver();
// Send the reports.
zx_time_t time = 0xabcd;
fake_hidbus_.SendReportWithTime(mouse_report, sizeof(mouse_report), time);
auto result = sync_client_->ReadReport();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
ASSERT_EQ(time, result->time);
ASSERT_EQ(sizeof(mouse_report), result->data.count());
for (size_t i = 0; i < result->data.count(); i++) {
EXPECT_EQ(mouse_report[i], result->data[i]);
}
result = sync_client_->ReadReport();
ASSERT_OK(result.status());
ASSERT_EQ(result->status, ZX_ERR_SHOULD_WAIT);
}
TEST_F(HidDeviceTest, ReadReportDoubleReport) {
SetupBootMouseDevice();
ASSERT_OK(device_->Bind(client_));
uint8_t double_mouse_report[] = {0xDE, 0xAD, 0xBE, 0x12, 0x34, 0x56};
SetupInstanceDriver();
// Send the reports.
zx_time_t time = 0xabcd;
fake_hidbus_.SendReportWithTime(double_mouse_report, sizeof(double_mouse_report), time);
auto result = sync_client_->ReadReport();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
ASSERT_EQ(time, result->time);
ASSERT_EQ(sizeof(hid_boot_mouse_report_t), result->data.count());
for (size_t i = 0; i < result->data.count(); i++) {
EXPECT_EQ(double_mouse_report[i], result->data[i]);
}
result = sync_client_->ReadReport();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
ASSERT_EQ(time, result->time);
ASSERT_EQ(sizeof(hid_boot_mouse_report_t), result->data.count());
for (size_t i = 0; i < result->data.count(); i++) {
EXPECT_EQ(double_mouse_report[i + sizeof(hid_boot_mouse_report_t)], result->data[i]);
}
result = sync_client_->ReadReport();
ASSERT_OK(result.status());
ASSERT_EQ(result->status, ZX_ERR_SHOULD_WAIT);
}
TEST_F(HidDeviceTest, ReadReportsSingleReport) {
SetupBootMouseDevice();
ASSERT_OK(device_->Bind(client_));
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
SetupInstanceDriver();
// Send the reports.
fake_hidbus_.SendReport(mouse_report, sizeof(mouse_report));
auto sync_client =
llcpp::fuchsia::hardware::input::Device::SyncClient(std::move(ddk_.FidlClient()));
auto result = sync_client_->ReadReports();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
ASSERT_EQ(sizeof(mouse_report), result->data.count());
for (size_t i = 0; i < result->data.count(); i++) {
EXPECT_EQ(mouse_report[i], result->data[i]);
}
}
TEST_F(HidDeviceTest, ReadReportsDoubleReport) {
SetupBootMouseDevice();
ASSERT_OK(device_->Bind(client_));
uint8_t double_mouse_report[] = {0xDE, 0xAD, 0xBE, 0x12, 0x34, 0x56};
SetupInstanceDriver();
// Send the reports.
fake_hidbus_.SendReport(double_mouse_report, sizeof(double_mouse_report));
auto result = sync_client_->ReadReports();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
ASSERT_EQ(sizeof(double_mouse_report), result->data.count());
for (size_t i = 0; i < result->data.count(); i++) {
EXPECT_EQ(double_mouse_report[i], result->data[i]);
}
}
TEST_F(HidDeviceTest, ReadReportsBlockingWait) {
SetupBootMouseDevice();
ASSERT_OK(device_->Bind(client_));
SetupInstanceDriver();
// Send the reports, but delayed.
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
std::thread report_thread([&]() {
sleep(1);
fake_hidbus_.SendReport(mouse_report, sizeof(mouse_report));
});
ASSERT_OK(report_event_.wait_one(DEV_STATE_READABLE, zx::time::infinite(), nullptr));
// Get the report.
auto result = sync_client_->ReadReports();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
ASSERT_EQ(sizeof(mouse_report), result->data.count());
for (size_t i = 0; i < result->data.count(); i++) {
EXPECT_EQ(mouse_report[i], result->data[i]);
}
report_thread.join();
}
// Test that only whole reports get sent through.
TEST_F(HidDeviceTest, ReadReportsOneAndAHalfReports) {
SetupBootMouseDevice();
ASSERT_OK(device_->Bind(client_));
SetupInstanceDriver();
// Send the report.
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
fake_hidbus_.SendReport(mouse_report, sizeof(mouse_report));
// Send a half of a report.
uint8_t half_report[] = {0xDE, 0xAD};
fake_hidbus_.SendReport(half_report, sizeof(half_report));
auto result = sync_client_->ReadReports();
ASSERT_OK(result.status());
ASSERT_OK(result->status);
ASSERT_EQ(sizeof(mouse_report), result->data.count());
for (size_t i = 0; i < result->data.count(); i++) {
EXPECT_EQ(mouse_report[i], result->data[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, SettingBootModeMouse) {
size_t desc_size;
const uint8_t* desc = get_paradise_touchpad_v1_report_desc(&desc_size);
fake_hidbus_.SetDescriptor(desc, desc_size);
hid_info_t info = {};
info.device_class = HID_DEVICE_CLASS_POINTER;
info.boot_device = true;
fake_hidbus_.SetHidInfo(info);
// Set the device to boot protocol.
fake_hidbus_.HidbusSetProtocol(HID_PROTOCOL_BOOT);
ASSERT_OK(device_->Bind(client_));
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, device_->GetReportDescLen());
const uint8_t* received_desc = device_->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) {
size_t desc_size;
const uint8_t* desc = get_paradise_touchpad_v1_report_desc(&desc_size);
fake_hidbus_.SetDescriptor(desc, desc_size);
hid_info_t info = {};
info.device_class = HID_DEVICE_CLASS_KBD;
info.boot_device = true;
fake_hidbus_.SetHidInfo(info);
// Set the device to boot protocol.
fake_hidbus_.HidbusSetProtocol(HID_PROTOCOL_BOOT);
ASSERT_OK(device_->Bind(client_));
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, device_->GetReportDescLen());
const uint8_t* received_desc = device_->GetReportDesc();
for (size_t i = 0; i < boot_kbd_desc_size; i++) {
ASSERT_EQ(boot_kbd_desc[i], received_desc[i]);
}
}
TEST_F(HidDeviceTest, BanjoGetDescriptor) {
SetupBootMouseDevice();
ASSERT_OK(device_->Bind(fake_hidbus_.GetProto()));
size_t known_size;
const uint8_t* known_descriptor = get_boot_mouse_report_desc(&known_size);
uint8_t report_descriptor[HID_MAX_DESC_LEN];
size_t actual;
ASSERT_OK(device_->HidDeviceGetDescriptor(report_descriptor, sizeof(report_descriptor), &actual));
ASSERT_EQ(known_size, actual);
ASSERT_BYTES_EQ(known_descriptor, report_descriptor, known_size);
}
TEST_F(HidDeviceTest, BanjoRegisterListenerSendReport) {
SetupBootMouseDevice();
ASSERT_OK(device_->Bind(fake_hidbus_.GetProto()));
uint8_t mouse_report[] = {0xDE, 0xAD, 0xBE};
struct ReportCtx {
sync_completion_t* completion;
uint8_t* known_report;
};
sync_completion_t seen_report;
ReportCtx ctx;
ctx.completion = &seen_report;
ctx.known_report = mouse_report;
hid_report_listener_protocol_ops_t ops;
ops.receive_report = [](void* ctx, const uint8_t* report_list, size_t report_count,
zx_time_t time) {
ASSERT_EQ(sizeof(mouse_report), report_count);
auto report_ctx = reinterpret_cast<ReportCtx*>(ctx);
ASSERT_BYTES_EQ(report_ctx->known_report, report_list, report_count);
sync_completion_signal(report_ctx->completion);
};
hid_report_listener_protocol_t listener;
listener.ctx = &ctx;
listener.ops = &ops;
ASSERT_OK(device_->HidDeviceRegisterListener(&listener));
fake_hidbus_.SendReport(mouse_report, sizeof(mouse_report));
ASSERT_OK(sync_completion_wait(&seen_report, zx::time::infinite().get()));
device_->HidDeviceUnregisterListener();
}
TEST_F(HidDeviceTest, BanjoGetSetReport) {
const uint8_t* desc;
size_t desc_size = get_ambient_light_report_desc(&desc);
fake_hidbus_.SetDescriptor(desc, desc_size);
hid_info_t info = {};
info.device_class = HID_DEVICE_CLASS_OTHER;
info.boot_device = false;
fake_hidbus_.SetHidInfo(info);
ASSERT_OK(device_->Bind(fake_hidbus_.GetProto()));
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;
ASSERT_OK(device_->HidDeviceSetReport(HID_REPORT_TYPE_FEATURE, AMBIENT_LIGHT_RPT_ID_FEATURE,
reinterpret_cast<uint8_t*>(&feature_report),
sizeof(feature_report)));
ambient_light_feature_rpt_t received_report = {};
size_t actual;
ASSERT_OK(device_->HidDeviceGetReport(HID_REPORT_TYPE_FEATURE, AMBIENT_LIGHT_RPT_ID_FEATURE,
reinterpret_cast<uint8_t*>(&received_report),
sizeof(received_report), &actual));
ASSERT_EQ(sizeof(received_report), actual);
ASSERT_BYTES_EQ(&feature_report, &received_report, actual);
}
// Tests that a device with too large reports don't cause buffer overruns.
TEST_F(HidDeviceTest, GetReportBufferOverrun) {
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);
fake_hidbus_.SetDescriptor(desc, desc_size);
hid_info_t info = {};
info.device_class = HID_DEVICE_CLASS_OTHER;
info.boot_device = false;
fake_hidbus_.SetHidInfo(info);
ASSERT_OK(device_->Bind(fake_hidbus_.GetProto()));
std::vector<uint8_t> report(0xFF0000);
size_t actual;
ASSERT_EQ(
device_->HidDeviceGetReport(HID_REPORT_TYPE_INPUT, 0, report.data(), report.size(), &actual),
ZX_ERR_INTERNAL);
}
} // namespace hid_driver