blob: 85063138622ec64d653a6e36c9763b564ceba8ae [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 "i2c-hid.h"
#include <endian.h>
#include <fidl/fuchsia.hardware.acpi/cpp/wire.h>
#include <fidl/fuchsia.hardware.interrupt/cpp/wire.h>
#include <fuchsia/hardware/hidbus/c/banjo.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/default.h>
#include <lib/async_patterns/cpp/dispatcher_bound.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/device.h>
#include <lib/ddk/driver.h>
#include <lib/ddk/trace/event.h>
#include <lib/device-protocol/i2c-channel.h>
#include <lib/fake-hidbus-ifc/fake-hidbus-ifc.h>
#include <lib/fake-i2c/fake-i2c.h>
#include <lib/sync/completion.h>
#include <lib/zx/clock.h>
#include <lib/zx/interrupt.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include <unistd.h>
#include <zircon/assert.h>
#include <zircon/rights.h>
#include <zircon/types.h>
#include <vector>
#include <fbl/auto_lock.h>
#include <zxtest/zxtest.h>
#include "src/devices/lib/acpi/mock/mock-acpi.h"
#include "src/devices/testing/mock-ddk/mock-device.h"
namespace i2c_hid {
// Ids were chosen arbitrarily.
static constexpr uint16_t kHidVendorId = 0xabcd;
static constexpr uint16_t kHidProductId = 0xdcba;
static constexpr uint16_t kHidVersion = 0x0123;
class FakeI2cHid : public fake_i2c::FakeI2c {
public:
// Sets the report descriptor. Must be called before binding the driver because
// the driver reads |hiddesc_| on bind.
void SetReportDescriptor(std::vector<uint8_t> report_desc) {
fbl::AutoLock lock(&report_read_lock_);
hiddesc_.wReportDescLength = htole16(report_desc.size());
report_desc_ = std::move(report_desc);
}
// Calling this function will make the FakeI2cHid driver return an error when the I2cHidbus
// tries to read the HidDescriptor. This so we can test that the I2cHidbus driver shuts
// down correctly when it fails to read the HidDescriptor.
void SetHidDescriptorFailure(zx_status_t status) { hiddesc_status_ = status; }
void SendReport(std::vector<uint8_t> report) {
SendReportWithLength(report, report.size() + sizeof(uint16_t));
}
// This lets us send a report with an incorrect length.
void SendReportWithLength(std::vector<uint8_t> report, size_t len) {
{
fbl::AutoLock lock(&report_read_lock_);
report_ = std::move(report);
report_len_ = len;
irq_.trigger(0, zx::clock::get_monotonic());
}
sync_completion_wait_deadline(&report_read_, zx::time::infinite().get());
sync_completion_reset(&report_read_);
}
zx_status_t WaitUntilReset() {
return sync_completion_wait_deadline(&is_reset_, zx::time::infinite().get());
}
private:
zx_status_t TransactCommands(const uint8_t* write_buffer, size_t write_buffer_size,
uint8_t* read_buffer, size_t* read_buffer_size)
__TA_REQUIRES(report_read_lock_) {
if (write_buffer_size < 4) {
return ZX_ERR_INTERNAL;
}
// Reset Command.
if (write_buffer[3] == kResetCommand) {
*read_buffer_size = 0;
pending_reset_ = true;
irq_.trigger(0, zx::clock::get_monotonic());
return ZX_OK;
}
// Set Command. At the moment this fake doesn't test for the types of reports, we only ever
// get/set |report_|.
if (write_buffer[3] == kSetReportCommand) {
if (write_buffer_size < 6) {
return ZX_ERR_INTERNAL;
}
// Get the report size.
uint16_t report_size = static_cast<uint16_t>(write_buffer[6] + (write_buffer[7] << 8));
if (write_buffer_size < (6 + report_size)) {
return ZX_ERR_INTERNAL;
}
// The report size includes the 2 bytes for the size, which we don't want when setting
// the report.
report_.resize(report_size - 2);
memcpy(report_.data(), write_buffer + 8, report_.size());
return ZX_OK;
}
// Get Command.
if (write_buffer[3] == kGetReportCommand) {
// Set the report size as the first two bytes.
read_buffer[0] = static_cast<uint8_t>((report_.size() + 2) & 0xFF);
read_buffer[1] = static_cast<uint8_t>(((report_.size() + 2) >> 8) & 0xFF);
memcpy(read_buffer + 2, report_.data(), report_.size());
*read_buffer_size = report_.size() + 2;
return ZX_OK;
}
return ZX_ERR_INTERNAL;
}
zx_status_t Transact(const uint8_t* write_buffer, size_t write_buffer_size, uint8_t* read_buffer,
size_t* read_buffer_size) override {
fbl::AutoLock lock(&report_read_lock_);
// General Read.
if (write_buffer_size == 0) {
// Reading the Reset status.
if (pending_reset_) {
SetRead(kResetReport, sizeof(kResetReport), read_buffer, read_buffer_size);
pending_reset_ = false;
sync_completion_signal(&is_reset_);
return ZX_OK;
}
// First two bytes are the report length.
*reinterpret_cast<uint16_t*>(read_buffer) = htole16(report_len_);
memcpy(read_buffer + sizeof(uint16_t), report_.data(), report_.size());
*read_buffer_size = report_.size() + sizeof(uint16_t);
sync_completion_signal(&report_read_);
return ZX_OK;
}
// Reading the Hid descriptor.
if (CompareWrite(write_buffer, write_buffer_size, kHidDescCommand, sizeof(kHidDescCommand))) {
if (hiddesc_status_ != ZX_OK) {
return hiddesc_status_;
}
SetRead(&hiddesc_, sizeof(hiddesc_), read_buffer, read_buffer_size);
return ZX_OK;
}
// Reading the Hid Report descriptor.
if (CompareWrite(write_buffer, write_buffer_size,
reinterpret_cast<const uint8_t*>(&kReportDescRegister),
sizeof(kReportDescRegister))) {
SetRead(report_desc_.data(), report_desc_.size(), read_buffer, read_buffer_size);
return ZX_OK;
}
// General commands.
bool is_general_command = (write_buffer_size >= 2) && (write_buffer[0] == kHidCommand[0]) &&
(write_buffer[1] == kHidCommand[1]);
if (is_general_command) {
return TransactCommands(write_buffer, write_buffer_size, read_buffer, read_buffer_size);
}
return ZX_ERR_INTERNAL;
}
// Register values were just picked arbitrarily.
static constexpr uint16_t kInputRegister = htole16(0x5);
static constexpr uint16_t kOutputRegister = htole16(0x6);
static constexpr uint16_t kCommandRegister = htole16(0x7);
static constexpr uint16_t kDataRegister = htole16(0x8);
static constexpr uint16_t kReportDescRegister = htole16(0x9);
static constexpr uint16_t kMaxInputLength = 0x1000;
static constexpr uint8_t kResetReport[2] = {0, 0};
static constexpr uint8_t kHidDescCommand[2] = {0x01, 0x00};
static constexpr uint8_t kHidCommand[2] = {static_cast<uint8_t>(kCommandRegister & 0xff),
static_cast<uint8_t>(kCommandRegister >> 8)};
I2cHidDesc hiddesc_ = []() {
I2cHidDesc hiddesc = {};
hiddesc.wHIDDescLength = htole16(sizeof(I2cHidDesc));
hiddesc.wInputRegister = kInputRegister;
hiddesc.wOutputRegister = kOutputRegister;
hiddesc.wCommandRegister = kCommandRegister;
hiddesc.wDataRegister = kDataRegister;
hiddesc.wMaxInputLength = kMaxInputLength;
hiddesc.wReportDescRegister = kReportDescRegister;
hiddesc.wVendorID = htole16(kHidVendorId);
hiddesc.wProductID = htole16(kHidProductId);
hiddesc.wVersionID = htole16(kHidVersion);
return hiddesc;
}();
zx_status_t hiddesc_status_ = ZX_OK;
std::atomic<bool> pending_reset_ = false;
sync_completion_t is_reset_;
fbl::Mutex report_read_lock_;
sync_completion_t report_read_;
std::vector<uint8_t> report_desc_ __TA_GUARDED(report_read_lock_);
std::vector<uint8_t> report_ __TA_GUARDED(report_read_lock_);
size_t report_len_ __TA_GUARDED(report_read_lock_) = 0;
};
class I2cHidTest : public zxtest::Test {
public:
class InterruptServer : public fidl::WireServer<fuchsia_hardware_interrupt::Provider> {
public:
explicit InterruptServer(zx::interrupt irq) : irq_(std::move(irq)) {}
void ResetIrq() { irq_.reset(); }
// Creates a handler function that can be used to connect to this server.
fidl::ProtocolHandler<::fuchsia_hardware_interrupt::Provider> Publish() {
return bindings_.CreateHandler(this, async_get_default_dispatcher(),
fidl::kIgnoreBindingClosure);
}
private:
void Get(GetCompleter::Sync& completer) override {
zx::interrupt clone;
ASSERT_OK(irq_.duplicate(ZX_RIGHT_SAME_RIGHTS, &clone));
completer.ReplySuccess(std::move(clone));
}
fidl::ServerBindingGroup<fuchsia_hardware_interrupt::Provider> bindings_;
zx::interrupt irq_;
};
// A mock component that exposes `fuchsia.hardware.interrupt/Provider`.
class MockComponent {
public:
MockComponent(zx::interrupt irq, fidl::ServerEnd<fuchsia_io::Directory> outgoing)
: outgoing_(async_get_default_dispatcher()), server_(std::move(irq)) {
ASSERT_OK(outgoing_
.AddService<fuchsia_hardware_interrupt::Service>(
fuchsia_hardware_interrupt::Service::InstanceHandler{{
.provider = server_.Publish(),
}})
.status_value());
ASSERT_OK(outgoing_.Serve(std::move(outgoing)));
}
void ResetIrq() { server_.ResetIrq(); }
private:
component::OutgoingDirectory outgoing_;
InterruptServer server_;
};
I2cHidTest() : loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {}
void SetUp() override {
parent_ = MockDevice::FakeRootParent();
ASSERT_OK(loop_.StartThread("i2c-hid-test-thread"));
acpi_device_.SetEvaluateObject(
[](acpi::mock::Device::EvaluateObjectRequestView view,
acpi::mock::Device::EvaluateObjectCompleter::Sync& completer) {
fidl::Arena<> alloc;
auto encoded = fuchsia_hardware_acpi::wire::EncodedObject::WithObject(
alloc, fuchsia_hardware_acpi::wire::Object::WithIntegerVal(alloc, 0x01));
completer.ReplySuccess(std::move(encoded));
ASSERT_TRUE(completer.result_of_reply().ok());
});
zx::interrupt irq;
ASSERT_OK(zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &irq));
zx::interrupt interrupt;
ASSERT_OK(irq.duplicate(ZX_RIGHT_SAME_RIGHTS, &interrupt));
fake_i2c_hid_.SetInterrupt(std::move(interrupt));
auto io_endpoints = fidl::Endpoints<fuchsia_io::Directory>::Create();
mock_component_.emplace(std::move(irq), std::move(io_endpoints.server));
parent_->AddFidlService(fuchsia_hardware_interrupt::Service::Name,
std::move(io_endpoints.client), "irq001");
auto client = acpi_device_.CreateClient(loop_.dispatcher());
ASSERT_OK(client.status_value());
device_ = new I2cHidbus(parent_.get(), std::move(client.value()));
auto endpoints = fidl::Endpoints<fuchsia_hardware_i2c::Device>::Create();
fidl::BindServer(loop_.dispatcher(), std::move(endpoints.server), &fake_i2c_hid_);
i2c_ = std::move(endpoints.client);
// Each test is responsible for calling Bind().
}
void TearDown() override {
device_->DdkAsyncRemove();
EXPECT_OK(mock_ddk::ReleaseFlaggedDevices(device_->zxdev()));
}
void StartHidBus() { device_->HidbusStart(fake_hid_bus_.GetProto()); }
protected:
acpi::mock::Device acpi_device_;
I2cHidbus* device_;
std::shared_ptr<MockDevice> parent_;
FakeI2cHid fake_i2c_hid_;
fake_hidbus_ifc::FakeHidbusIfc fake_hid_bus_;
fidl::ClientEnd<fuchsia_hardware_i2c::Device> i2c_;
async::Loop loop_;
async_patterns::DispatcherBound<MockComponent> mock_component_{loop_.dispatcher()};
};
TEST_F(I2cHidTest, HidTestBind) {
ASSERT_OK(device_->Bind(std::move(i2c_)));
device_->zxdev()->InitOp();
ASSERT_OK(device_->zxdev()->WaitUntilInitReplyCalled(zx::time::infinite()));
EXPECT_OK(device_->zxdev()->InitReplyCallStatus());
}
TEST_F(I2cHidTest, HidTestQuery) {
ASSERT_OK(device_->Bind(std::move(i2c_)));
device_->zxdev()->InitOp();
ASSERT_OK(fake_i2c_hid_.WaitUntilReset());
StartHidBus();
hid_info_t info = {};
ASSERT_OK(device_->HidbusQuery(0, &info));
ASSERT_EQ(kHidVendorId, info.vendor_id);
ASSERT_EQ(kHidProductId, info.product_id);
ASSERT_EQ(kHidVersion, info.version);
}
TEST_F(I2cHidTest, HidTestReadReportDesc) {
uint8_t returned_report_desc[HID_MAX_DESC_LEN];
size_t returned_report_desc_len;
std::vector<uint8_t> report_desc(4);
report_desc[0] = 1;
report_desc[1] = 100;
report_desc[2] = 255;
report_desc[3] = 5;
fake_i2c_hid_.SetReportDescriptor(report_desc);
ASSERT_OK(device_->Bind(std::move(i2c_)));
device_->zxdev()->InitOp();
ASSERT_OK(device_->HidbusGetDescriptor(HID_DESCRIPTION_TYPE_REPORT, returned_report_desc,
sizeof(returned_report_desc), &returned_report_desc_len));
ASSERT_EQ(returned_report_desc_len, report_desc.size());
for (size_t i = 0; i < returned_report_desc_len; i++) {
ASSERT_EQ(returned_report_desc[i], report_desc[i]);
}
}
TEST(I2cHidTest, HidTestReportDescFailureLifetimeTest) {
I2cHidbus* device_;
std::shared_ptr<MockDevice> parent = MockDevice::FakeRootParent();
FakeI2cHid fake_i2c_hid_;
fake_hidbus_ifc::FakeHidbusIfc fake_hid_bus_;
ddk::I2cChannel channel_;
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
EXPECT_OK(loop.StartThread());
auto i2c_endpoints = fidl::Endpoints<fuchsia_hardware_i2c::Device>::Create();
fidl::BindServer(loop.dispatcher(), std::move(i2c_endpoints.server), &fake_i2c_hid_);
auto endpoints = fidl::Endpoints<fuchsia_hardware_acpi::Device>::Create();
endpoints.server.reset();
device_ = new I2cHidbus(parent.get(),
acpi::Client::Create(fidl::WireSyncClient(std::move(endpoints.client))));
channel_ = ddk::I2cChannel(std::move(i2c_endpoints.client));
fake_i2c_hid_.SetHidDescriptorFailure(ZX_ERR_TIMED_OUT);
ASSERT_OK(device_->Bind(std::move(channel_)));
device_->zxdev()->InitOp();
EXPECT_OK(device_->zxdev()->WaitUntilInitReplyCalled(zx::time::infinite()));
EXPECT_NOT_OK(device_->zxdev()->InitReplyCallStatus());
loop.Shutdown();
device_async_remove(parent.get());
mock_ddk::ReleaseFlaggedDevices(parent.get());
}
TEST_F(I2cHidTest, HidTestReadReport) {
ASSERT_OK(device_->Bind(std::move(i2c_)));
device_->zxdev()->InitOp();
ASSERT_OK(fake_i2c_hid_.WaitUntilReset());
StartHidBus();
// Any arbitrary values or vector length could be used here.
std::vector<uint8_t> rpt(4);
rpt[0] = 1;
rpt[1] = 100;
rpt[2] = 255;
rpt[3] = 5;
fake_i2c_hid_.SendReport(rpt);
std::vector<uint8_t> returned_rpt;
ASSERT_OK(fake_hid_bus_.WaitUntilNextReport(&returned_rpt));
ASSERT_EQ(returned_rpt.size(), rpt.size());
for (size_t i = 0; i < returned_rpt.size(); i++) {
EXPECT_EQ(returned_rpt[i], rpt[i]);
}
}
TEST_F(I2cHidTest, HidTestBadReportLen) {
ASSERT_OK(device_->Bind(std::move(i2c_)));
device_->zxdev()->InitOp();
ASSERT_OK(fake_i2c_hid_.WaitUntilReset());
StartHidBus();
// Send a report with a length that's too long.
std::vector<uint8_t> too_long_rpt{0xAA};
fake_i2c_hid_.SendReportWithLength(too_long_rpt, UINT16_MAX);
// Send a normal report.
std::vector<uint8_t> normal_rpt{0xBB};
fake_i2c_hid_.SendReport(normal_rpt);
// Wait until the reports are in.
std::vector<uint8_t> returned_rpt;
ASSERT_OK(fake_hid_bus_.WaitUntilNextReport(&returned_rpt));
// We should've only seen one report since the too long report will cause an error.
ASSERT_EQ(fake_hid_bus_.NumReportsSeen(), 1);
// Double check that the returned report is the normal one.
ASSERT_EQ(returned_rpt.size(), normal_rpt.size());
ASSERT_EQ(returned_rpt[0], normal_rpt[0]);
}
TEST_F(I2cHidTest, HidTestReadReportNoIrq) {
// Replace the device's interrupt with an invalid one.
fake_i2c_hid_.SetInterrupt(zx::interrupt());
mock_component_.AsyncCall(&MockComponent::ResetIrq);
ASSERT_OK(device_->Bind(std::move(i2c_)));
device_->zxdev()->InitOp();
ASSERT_OK(fake_i2c_hid_.WaitUntilReset());
StartHidBus();
// Any arbitrary values or vector length could be used here.
std::vector<uint8_t> rpt(4);
rpt[0] = 1;
rpt[1] = 100;
rpt[2] = 255;
rpt[3] = 5;
fake_i2c_hid_.SendReport(rpt);
std::vector<uint8_t> returned_rpt;
ASSERT_OK(fake_hid_bus_.WaitUntilNextReport(&returned_rpt));
ASSERT_EQ(returned_rpt.size(), rpt.size());
for (size_t i = 0; i < returned_rpt.size(); i++) {
EXPECT_EQ(returned_rpt[i], rpt[i]);
}
}
TEST_F(I2cHidTest, HidTestDedupeReportsNoIrq) {
// Replace the device's interrupt with an invalid one.
fake_i2c_hid_.SetInterrupt(zx::interrupt());
mock_component_.AsyncCall(&MockComponent::ResetIrq);
ASSERT_OK(device_->Bind(std::move(i2c_)));
device_->zxdev()->InitOp();
ASSERT_OK(fake_i2c_hid_.WaitUntilReset());
StartHidBus();
// Send three reports.
std::vector<uint8_t> rpt1(4);
rpt1[0] = 1;
rpt1[1] = 100;
rpt1[2] = 255;
rpt1[3] = 5;
fake_i2c_hid_.SendReport(rpt1);
fake_i2c_hid_.SendReport(rpt1);
fake_i2c_hid_.SendReport(rpt1);
std::vector<uint8_t> returned_rpt1;
ASSERT_OK(fake_hid_bus_.WaitUntilNextReport(&returned_rpt1));
// We should've only seen one report since the repeats should have been deduped.
ASSERT_EQ(fake_hid_bus_.NumReportsSeen(), 1);
ASSERT_EQ(returned_rpt1.size(), rpt1.size());
for (size_t i = 0; i < returned_rpt1.size(); i++) {
EXPECT_EQ(returned_rpt1[i], rpt1[i]);
}
// Send three different reports.
std::vector<uint8_t> rpt2(4);
rpt2[0] = 1;
rpt2[1] = 200;
rpt2[2] = 100;
rpt2[3] = 6;
fake_i2c_hid_.SendReport(rpt2);
fake_i2c_hid_.SendReport(rpt2);
fake_i2c_hid_.SendReport(rpt2);
std::vector<uint8_t> returned_rpt2;
ASSERT_OK(fake_hid_bus_.WaitUntilNextReport(&returned_rpt2));
// We should've only seen two report since the repeats should have been deduped.
ASSERT_EQ(fake_hid_bus_.NumReportsSeen(), 2);
ASSERT_EQ(returned_rpt2.size(), rpt2.size());
for (size_t i = 0; i < returned_rpt2.size(); i++) {
EXPECT_EQ(returned_rpt2[i], rpt2[i]);
}
// Send a report with different length.
std::vector<uint8_t> rpt3(5);
rpt3[0] = 1;
rpt3[1] = 200;
rpt3[2] = 100;
rpt3[3] = 6;
rpt3[4] = 10;
fake_i2c_hid_.SendReport(rpt3);
std::vector<uint8_t> returned_rpt3;
ASSERT_OK(fake_hid_bus_.WaitUntilNextReport(&returned_rpt3));
ASSERT_EQ(fake_hid_bus_.NumReportsSeen(), 3);
ASSERT_EQ(returned_rpt3.size(), rpt3.size());
for (size_t i = 0; i < returned_rpt3.size(); i++) {
EXPECT_EQ(returned_rpt3[i], rpt3[i]);
}
}
TEST_F(I2cHidTest, HidTestSetReport) {
ASSERT_OK(device_->Bind(std::move(i2c_)));
device_->zxdev()->InitOp();
ASSERT_OK(fake_i2c_hid_.WaitUntilReset());
// Any arbitrary values or vector length could be used here.
uint8_t report_data[4] = {1, 100, 255, 5};
ASSERT_OK(
device_->HidbusSetReport(HID_REPORT_TYPE_FEATURE, 0x1, report_data, sizeof(report_data)));
uint8_t received_data[4] = {};
size_t out_len;
ASSERT_OK(device_->HidbusGetReport(HID_REPORT_TYPE_FEATURE, 0x1, received_data,
sizeof(received_data), &out_len));
ASSERT_EQ(out_len, 4);
for (size_t i = 0; i < out_len; i++) {
EXPECT_EQ(received_data[i], report_data[i]);
}
}
} // namespace i2c_hid