blob: 414f75f0857f5e04353413f948d1ceb3a7c21a16 [file] [log] [blame]
// Copyright 2018 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 <fidl/fuchsia.hardware.usb.tester/cpp/wire.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <unistd.h>
#include <filesystem>
#include <zxtest/zxtest.h>
namespace {
constexpr std::string_view kUsbTesterDir = "/dev/class/usb-tester";
constexpr std::string_view kUsbDeviceDir = "/dev/class/usb-device";
constexpr double ISOCH_MIN_PASS_PERCENT = 80;
constexpr uint64_t ISOCH_MIN_PACKETS = 10;
zx_status_t check_xhci_root_hubs() {
uint8_t count = 0;
for (auto const& dir_entry : std::filesystem::directory_iterator{kUsbDeviceDir}) {
std::ignore = dir_entry;
count++;
}
// TODO(ravoorir): Use FIDL apis to read the descriptors
// of the devices and ensure that both 2.0 root hub and
// 3.0 root hub showed up.
if (count < 2) {
return ZX_ERR_BAD_STATE;
}
return ZX_OK;
}
zx::result<fidl::ClientEnd<fuchsia_hardware_usb_tester::Device>> open_test_device() {
for (auto const& dir_entry : std::filesystem::directory_iterator{kUsbTesterDir}) {
return component::Connect<fuchsia_hardware_usb_tester::Device>(dir_entry.path().c_str());
}
return zx::error(ZX_ERR_NOT_FOUND);
}
TEST(UsbTests, RootHubsTest) {
// TODO(https://fxbug.dev/42072584): There should be a matrix of hardware that should
// be accessible from here. Depending on whether the hardware has
// xhci/ehci, we should check the root hubs.
if (zx_status_t status = check_xhci_root_hubs(); status != ZX_OK) {
// TODO(https://fxbug.dev/42072584): At the moment we cannot restrict a test
// to only run on hardware(https://fxbug.dev/42175425) and not the qemu instances.
// We should fail here when running on hardware.
printf("[SKIPPING] Root hub creation failed.\n");
return;
}
}
TEST(UsbTests, BulkLoopbackTest) {
zx::result device_result = open_test_device();
if (device_result.is_error()) {
printf("[SKIPPING]\n");
return;
}
{
fuchsia_hardware_usb_tester::wire::BulkTestParams params = {
.data_pattern = fuchsia_hardware_usb_tester::wire::DataPatternType::kConstant,
.len = 64 * 1024,
};
fidl::WireResult result =
fidl::WireCall(device_result.value())->BulkLoopback(params, nullptr, nullptr);
ASSERT_OK(result.status());
ASSERT_OK(result.value().s);
}
{
fuchsia_hardware_usb_tester::wire::BulkTestParams params = {
.data_pattern = fuchsia_hardware_usb_tester::wire::DataPatternType::kRandom,
.len = 64 * 1024,
};
fidl::WireResult result =
fidl::WireCall(device_result.value())->BulkLoopback(params, nullptr, nullptr);
ASSERT_OK(result.status());
ASSERT_OK(result.value().s);
}
}
TEST(UsbTests, BulkScatterGatherTest) {
zx::result device_result = open_test_device();
if (device_result.is_error()) {
printf("[SKIPPING]\n");
return;
}
fuchsia_hardware_usb_tester::wire::BulkTestParams params = {
.data_pattern = fuchsia_hardware_usb_tester::wire::DataPatternType::kRandom,
.len = 230,
};
fuchsia_hardware_usb_tester::wire::SgList sg_list = {.len = 5};
sg_list.entries[0] = {.length = 10, .offset = 100};
sg_list.entries[1] = {.length = 30, .offset = 1000};
sg_list.entries[2] = {.length = 100, .offset = 4000};
sg_list.entries[3] = {.length = 40, .offset = 5000};
sg_list.entries[4] = {.length = 50, .offset = 10000};
auto sg_list_view =
fidl::ObjectView<fuchsia_hardware_usb_tester::wire::SgList>::FromExternal(&sg_list);
{
fidl::WireResult result =
fidl::WireCall(device_result.value())->BulkLoopback(params, sg_list_view, nullptr);
ASSERT_OK(result.status());
ASSERT_OK(result.value().s);
}
{
fidl::WireResult result =
fidl::WireCall(device_result.value())->BulkLoopback(params, nullptr, sg_list_view);
ASSERT_OK(result.status());
ASSERT_OK(result.value().s);
}
}
void usb_isoch_verify_result(fuchsia_hardware_usb_tester::wire::IsochResult& result) {
ASSERT_GT(result.num_packets, 0lu, "didn't transfer any isochronous packets");
// Isochronous transfers aren't guaranteed, so just require a high enough percentage to pass.
ASSERT_GE(result.num_packets, ISOCH_MIN_PACKETS,
"num_packets is too low for a reliable result, should request more bytes");
double percent_passed =
(static_cast<double>(result.num_passed) / static_cast<double>(result.num_packets)) * 100;
ASSERT_GE(percent_passed, ISOCH_MIN_PASS_PERCENT, "not enough isoch transfers succeeded");
}
TEST(UsbTests, IsochLoopbackTest) {
zx::result device_result = open_test_device();
if (device_result.is_error()) {
printf("[SKIPPING]\n");
return;
}
{
fuchsia_hardware_usb_tester::wire::IsochTestParams params = {
.data_pattern = fuchsia_hardware_usb_tester::wire::DataPatternType::kConstant,
.num_packets = 64,
.packet_size = 1024,
};
fidl::WireResult result = fidl::WireCall(device_result.value())->IsochLoopback(params);
ASSERT_OK(result.status());
ASSERT_OK(result.value().s);
ASSERT_NO_FATAL_FAILURE(usb_isoch_verify_result(result.value().result));
}
{
fuchsia_hardware_usb_tester::wire::IsochTestParams params = {
.data_pattern = fuchsia_hardware_usb_tester::wire::DataPatternType::kRandom,
.num_packets = 64,
.packet_size = 1024,
};
fidl::WireResult result = fidl::WireCall(device_result.value())->IsochLoopback(params);
ASSERT_OK(result.status());
ASSERT_OK(result.value().s);
ASSERT_NO_FATAL_FAILURE(usb_isoch_verify_result(result.value().result));
}
}
TEST(UsbTests, CallbacksOptOutTest) {
zx::result device_result = open_test_device();
if (device_result.is_error()) {
printf("[SKIPPING]\n");
return;
}
fuchsia_hardware_usb_tester::wire::IsochTestParams params = {
.data_pattern = fuchsia_hardware_usb_tester::wire::DataPatternType::kConstant,
.num_packets = 64,
.packet_size = 1024,
.packet_opts_len = params.num_packets,
};
size_t reqs_per_callback = 10;
for (size_t i = 0; i < params.num_packets; ++i) {
// Set a callback on every 10 requests, and also on the last request.
bool set_cb = ((i + 1) % reqs_per_callback == 0) || (i == params.num_packets - 1);
params.packet_opts[i].set_cb = set_cb;
params.packet_opts[i].expect_cb = set_cb;
}
fidl::WireResult result = fidl::WireCall(device_result.value())->IsochLoopback(params);
ASSERT_OK(result.status());
ASSERT_OK(result.value().s);
ASSERT_NO_FATAL_FAILURE(usb_isoch_verify_result(result.value().result));
}
TEST(UsbTests, SingleCallbackErrorTest) {
zx::result device_result = open_test_device();
if (device_result.is_error()) {
printf("[SKIPPING]\n");
return;
}
// We should always get a callback on error.
fuchsia_hardware_usb_tester::wire::IsochTestParams params = {
.data_pattern = fuchsia_hardware_usb_tester::wire::DataPatternType::kConstant,
.num_packets = 1,
.packet_size = 1024,
.packet_opts_len = 1,
};
params.packet_opts[0] = {
.set_cb = false,
.set_error = true,
.expect_cb = true,
};
fidl::WireResult result = fidl::WireCall(device_result.value())->IsochLoopback(params);
ASSERT_OK(result.status());
ASSERT_OK(result.value().s);
// Don't need to verify the transfer results since we only care about callbacks for this test.
}
TEST(UsbTests, CallbacksOnErrorTest) {
zx::result device_result = open_test_device();
if (device_result.is_error()) {
printf("[SKIPPING]\n");
return;
}
// Error on the last packet receiving a callback.
fuchsia_hardware_usb_tester::wire::IsochTestParams params = {
.data_pattern = fuchsia_hardware_usb_tester::wire::DataPatternType::kConstant,
.num_packets = 4,
.packet_size = 1024,
.packet_opts_len = 4,
};
params.packet_opts[0] = {.set_cb = false, .set_error = false, .expect_cb = false};
params.packet_opts[1] = {.set_cb = false, .set_error = true, .expect_cb = true};
params.packet_opts[2] = {.set_cb = false, .set_error = false, .expect_cb = true};
params.packet_opts[3] = {.set_cb = true, .set_error = true, .expect_cb = true};
fidl::WireResult result = fidl::WireCall(device_result.value())->IsochLoopback(params);
ASSERT_OK(result.status());
ASSERT_OK(result.value().s);
// Don't need to verify the transfer results since we only care about callbacks for this test.
}
TEST(UsbTests, CallbacksOnMultipleErrorsTests) {
zx::result device_result = open_test_device();
if (device_result.is_error()) {
printf("[SKIPPING]\n");
return;
}
fuchsia_hardware_usb_tester::wire::IsochTestParams params = {
.data_pattern = fuchsia_hardware_usb_tester::wire::DataPatternType::kConstant,
.num_packets = 10,
.packet_size = 1024,
.packet_opts_len = 10,
};
params.packet_opts[0] = {.set_cb = false, .set_error = false, .expect_cb = false};
params.packet_opts[1] = {.set_cb = false, .set_error = false, .expect_cb = true};
params.packet_opts[2] = {.set_cb = false, .set_error = true, .expect_cb = true};
params.packet_opts[3] = {.set_cb = true, .set_error = true, .expect_cb = true};
params.packet_opts[4] = {.set_cb = false, .set_error = false, .expect_cb = false};
params.packet_opts[5] = {.set_cb = false, .set_error = true, .expect_cb = true};
params.packet_opts[6] = {.set_cb = false, .set_error = false, .expect_cb = false};
params.packet_opts[7] = {.set_cb = true, .set_error = false, .expect_cb = true};
params.packet_opts[8] = {.set_cb = false, .set_error = true, .expect_cb = true};
params.packet_opts[9] = {.set_cb = true, .set_error = false, .expect_cb = true};
fidl::WireResult result = fidl::WireCall(device_result.value())->IsochLoopback(params);
ASSERT_OK(result.status());
ASSERT_OK(result.value().s);
// Don't need to verify the transfer results since we only care about callbacks for this test.
}
} // namespace