blob: cb4be4cbfc244d944475ea453c0414922eef35f2 [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 <fuchsia/hardware/usb/peripheral/llcpp/fidl.h>
#include <fuchsia/hardware/usb/virtual/bus/llcpp/fidl.h>
#include <lib/fdio/watcher.h>
#include <lib/usb-virtual-bus-launcher/usb-virtual-bus-launcher.h>
#include <lib/zx/clock.h>
#include <sys/stat.h>
#include <unistd.h>
#include <zircon/hw/usb.h>
#include <zircon/hw/usb/cdc.h>
#include <zircon/syscalls.h>
#include <ddk/platform-defs.h>
#include <fbl/string.h>
#include <hid/boot.h>
#include <zxtest/zxtest.h>
namespace usb_virtual_bus {
namespace {
class USBVirtualBus : public usb_virtual_bus_base::USBVirtualBusBase {
public:
USBVirtualBus() = default;
// Initialize a USB CDC ACM device. Asserts on failure.
void InitUsbCdcAcm(fbl::String* devpath);
};
// Initialize a USB CDC ACM device. Asserts on failure.
void USBVirtualBus::InitUsbCdcAcm(fbl::String* devpath) {
namespace usb_peripheral = ::llcpp::fuchsia::hardware::usb::peripheral;
using ConfigurationDescriptor =
::fidl::VectorView<::llcpp::fuchsia::hardware::usb::peripheral::FunctionDescriptor>;
usb_peripheral::DeviceDescriptor device_desc = {};
device_desc.bcd_usb = htole16(0x0200);
device_desc.b_max_packet_size0 = 64;
device_desc.bcd_device = htole16(0x0100);
device_desc.b_num_configurations = 1;
usb_peripheral::FunctionDescriptor usb_cdc_acm_function_desc = {
.interface_class = USB_CLASS_COMM,
.interface_subclass = USB_CDC_SUBCLASS_ABSTRACT,
.interface_protocol = 0,
};
std::vector<usb_peripheral::FunctionDescriptor> function_descs;
function_descs.push_back(usb_cdc_acm_function_desc);
std::vector<ConfigurationDescriptor> config_descs;
config_descs.emplace_back(fidl::unowned_vec(function_descs));
ASSERT_NO_FATAL_FAILURES(SetupPeripheralDevice(std::move(device_desc), std::move(config_descs)));
fbl::unique_fd fd(openat(devmgr_.devfs_root().get(), "class/serial", O_RDONLY));
while (fdio_watch_directory(fd.get(), WaitForAnyFile, ZX_TIME_INFINITE, devpath) != ZX_ERR_STOP) {
}
*devpath = fbl::String::Concat({fbl::String("class/serial/"), *devpath});
}
class UsbCdcAcmTest : public zxtest::Test {
public:
void SetUp() override { ASSERT_NO_FATAL_FAILURES(bus_.InitUsbCdcAcm(&devpath_)); }
void TearDown() override {
ASSERT_NO_FATAL_FAILURES(bus_.ClearPeripheralDeviceFunctions());
auto result2 = bus_.virtual_bus().Disable();
ASSERT_NO_FATAL_FAILURES(ValidateResult(result2));
}
zx_status_t ReadWithTimeout(int fd, void* data, size_t size, size_t* actual) {
// Time out in 5 seconds.
const auto timeout = zx::deadline_after(zx::sec(5));
while (zx::clock::get_monotonic() < timeout) {
*actual = read(fd, data, size);
if (*actual != 0) {
return ZX_OK;
}
}
return ZX_ERR_SHOULD_WAIT;
}
protected:
USBVirtualBus bus_;
fbl::String devpath_;
};
TEST_F(UsbCdcAcmTest, ReadAndWriteTest) {
fbl::unique_fd fd(openat(bus_.GetRootFd(), devpath_.c_str(), O_RDWR));
ASSERT_GT(fd.get(), 0);
uint8_t write_data[] = {1, 2, 3};
size_t bytes_sent = write(fd.get(), write_data, sizeof(write_data));
ASSERT_EQ(bytes_sent, sizeof(write_data));
uint8_t read_data[3] = {};
zx_status_t status = ReadWithTimeout(fd.get(), read_data, sizeof(read_data), &bytes_sent);
ASSERT_OK(status);
ASSERT_EQ(bytes_sent, sizeof(read_data));
for (size_t i = 0; i < sizeof(write_data); i++) {
ASSERT_EQ(read_data[i], write_data[i]);
}
uint8_t write_data2[] = {5, 4, 3, 2, 1};
bytes_sent = write(fd.get(), write_data2, sizeof(write_data2));
ASSERT_EQ(bytes_sent, sizeof(write_data2));
uint8_t read_data2[5] = {};
status = ReadWithTimeout(fd.get(), read_data2, sizeof(read_data2), &bytes_sent);
ASSERT_OK(status);
ASSERT_EQ(bytes_sent, sizeof(read_data2));
for (size_t i = 0; i < sizeof(write_data2); i++) {
ASSERT_EQ(read_data2[i], write_data2[i]);
}
// Writing just "0" to the fake USB driver will cause an empty response to be queued.
uint8_t write_data3[1] = {'0'};
bytes_sent = write(fd.get(), write_data3, sizeof(write_data3));
ASSERT_EQ(bytes_sent, sizeof(write_data3));
uint8_t read_data3[1] = {};
status = ReadWithTimeout(fd.get(), read_data3, sizeof(read_data3), &bytes_sent);
ASSERT_EQ(status, ZX_ERR_SHOULD_WAIT);
ASSERT_EQ(bytes_sent, 0);
}
} // namespace
} // namespace usb_virtual_bus