blob: cab1406ba5a20fe9931dedda22befb1ab2356f52 [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 <dirent.h>
#include <endian.h>
#include <fcntl.h>
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fidl/fuchsia.hardware.input/cpp/wire.h>
#include <fidl/fuchsia.hardware.usb.peripheral/cpp/wire.h>
#include <fidl/fuchsia.hardware.usb.virtual.bus/cpp/wire.h>
#include <lib/component/incoming/cpp/directory.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/ddk/platform-defs.h>
#include <lib/device-watcher/cpp/device-watcher.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <lib/fit/function.h>
#include <lib/hid/boot.h>
#include <lib/usb-virtual-bus-launcher/usb-virtual-bus-launcher.h>
#include <sys/stat.h>
#include <unistd.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <fbl/string.h>
#include <gtest/gtest.h>
#include <usb/usb.h>
#include "src/lib/testing/predicates/status.h"
namespace usb_virtual_bus {
namespace {
namespace fhidbus = fuchsia_hardware_hidbus;
using usb_virtual::BusLauncher;
class UsbHidTest : public ::testing::Test {
public:
void SetUp() override {
zx::result bus = BusLauncher::Create();
ASSERT_OK(bus);
bus_ = std::move(bus.value());
auto usb_hid_function_desc = GetConfigDescriptor();
ASSERT_NO_FATAL_FAILURE(InitUsbHid(usb_hid_function_desc));
fdio_cpp::UnownedFdioCaller caller(bus_->GetRootFd());
zx::result controller =
component::ConnectAt<fuchsia_hardware_input::Controller>(caller.directory(), devpath_);
ASSERT_OK(controller);
auto [device, server] = fidl::Endpoints<fuchsia_hardware_input::Device>::Create();
fidl::OneWayStatus status = fidl::WireCall(controller.value())->OpenSession(std::move(server));
ASSERT_OK(status.status());
sync_client_ = fidl::WireSyncClient<fuchsia_hardware_input::Device>(std::move(device));
}
void TearDown() override {
if (bus_.has_value()) {
ASSERT_OK(bus_->ClearPeripheralDeviceFunctions());
ASSERT_OK(bus_->Disable());
}
}
protected:
virtual fuchsia_hardware_usb_peripheral::wire::FunctionDescriptor GetConfigDescriptor() = 0;
// Initialize a Usb HID device. Asserts on failure.
void InitUsbHid(fuchsia_hardware_usb_peripheral::wire::FunctionDescriptor desc) {
namespace usb_peripheral = fuchsia_hardware_usb_peripheral;
std::vector<usb_peripheral::wire::FunctionDescriptor> function_descs = {desc};
ASSERT_OK(bus_->SetupPeripheralDevice(
{
.bcd_usb = htole16(0x0200),
.b_max_packet_size0 = 64,
.id_vendor = htole16(0x18d1),
.id_product = htole16(0xaf10),
.bcd_device = htole16(0x0100),
.b_num_configurations = 1,
},
{fidl::VectorView<usb_peripheral::wire::FunctionDescriptor>::FromExternal(
function_descs)}));
fdio_cpp::UnownedFdioCaller caller(bus_->GetRootFd());
{
zx::result directory = component::OpenDirectoryAt(caller.directory(), "class/input");
ASSERT_OK(directory);
zx::result watch_result = device_watcher::WatchDirectoryForItems(
directory.value(), [this](std::string_view devpath) {
devpath_ = fbl::String::Concat({"class/input/", devpath});
return std::monostate{};
});
ASSERT_OK(watch_result);
}
}
std::optional<BusLauncher> bus_;
fbl::String devpath_;
fidl::WireSyncClient<fuchsia_hardware_input::Device> sync_client_;
};
class UsbOneEndpointTest : public UsbHidTest {
protected:
fuchsia_hardware_usb_peripheral::wire::FunctionDescriptor GetConfigDescriptor() override {
return fuchsia_hardware_usb_peripheral::wire::FunctionDescriptor{
.interface_class = USB_CLASS_HID,
.interface_subclass = 0,
.interface_protocol = USB_PROTOCOL_TEST_HID_ONE_ENDPOINT,
};
}
};
TEST_F(UsbOneEndpointTest, GetDeviceIdsVidPid) {
// Check USB device descriptor VID/PID plumbing.
auto result = sync_client_->Query();
ASSERT_OK(result.status());
ASSERT_TRUE(result->is_ok());
EXPECT_EQ(0x18d1u, result.value()->info.vendor_id());
EXPECT_EQ(0xaf10u, result.value()->info.product_id());
}
TEST_F(UsbOneEndpointTest, SetAndGetReport) {
uint8_t buf[sizeof(hid_boot_mouse_report_t)] = {0xab, 0xbc, 0xde};
auto set_result = sync_client_->SetReport(fhidbus::wire::ReportType::kInput, 0,
fidl::VectorView<uint8_t>::FromExternal(buf));
auto get_result = sync_client_->GetReport(fhidbus::wire::ReportType::kInput, 0);
ASSERT_TRUE(set_result.ok());
ASSERT_TRUE(set_result->is_ok());
ASSERT_TRUE(get_result.ok());
ASSERT_TRUE(get_result->is_ok());
ASSERT_EQ(get_result.value()->report.size(), sizeof(hid_boot_mouse_report_t));
ASSERT_EQ(0xab, get_result.value()->report[0]);
ASSERT_EQ(0xbc, get_result.value()->report[1]);
ASSERT_EQ(0xde, get_result.value()->report[2]);
}
class UsbTwoEndpointTest : public UsbHidTest {
protected:
fuchsia_hardware_usb_peripheral::wire::FunctionDescriptor GetConfigDescriptor() override {
return fuchsia_hardware_usb_peripheral::wire::FunctionDescriptor{
.interface_class = USB_CLASS_HID,
.interface_subclass = 0,
.interface_protocol = USB_PROTOCOL_TEST_HID_TWO_ENDPOINT,
};
}
};
TEST_F(UsbTwoEndpointTest, SetAndGetReport) {
uint8_t buf[sizeof(hid_boot_mouse_report_t)] = {0xab, 0xbc, 0xde};
auto set_result = sync_client_->SetReport(fhidbus::wire::ReportType::kInput, 0,
fidl::VectorView<uint8_t>::FromExternal(buf));
auto get_result = sync_client_->GetReport(fhidbus::wire::ReportType::kInput, 0);
ASSERT_TRUE(set_result.ok());
ASSERT_TRUE(set_result->is_ok());
ASSERT_TRUE(get_result.ok());
ASSERT_TRUE(get_result->is_ok());
ASSERT_EQ(get_result.value()->report.size(), sizeof(hid_boot_mouse_report_t));
ASSERT_EQ(0xab, get_result.value()->report[0]);
ASSERT_EQ(0xbc, get_result.value()->report[1]);
ASSERT_EQ(0xde, get_result.value()->report[2]);
}
} // namespace
} // namespace usb_virtual_bus