| // 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 <sys/stat.h> |
| #include <unistd.h> |
| #include <zircon/hw/usb.h> |
| #include <zircon/syscalls.h> |
| |
| #include <ctime> |
| |
| #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() {} |
| |
| // Initialize an FTDI device. Asserts on failure. |
| void InitFtdi(fbl::String* devpath); |
| }; |
| |
| // Initialize an FTDI USB device. Asserts on failure. |
| void USBVirtualBus::InitFtdi(fbl::String* devpath) { |
| namespace usb_peripheral = ::llcpp::fuchsia::hardware::usb::peripheral; |
| |
| usb_peripheral::DeviceDescriptor device_desc = {}; |
| device_desc.bcdUSB = htole16(0x0200); |
| device_desc.bMaxPacketSize0 = 64; |
| device_desc.bcdDevice = htole16(0x0100); |
| device_desc.bNumConfigurations = 1; |
| |
| // Setting FTDI Vendor |
| device_desc.idVendor = htole16(0x403); |
| // Setting 232H product |
| device_desc.idProduct = htole16(0x6014); |
| |
| usb_peripheral::FunctionDescriptor ftdi_function_desc = { |
| .interface_class = USB_CLASS_VENDOR, |
| .interface_subclass = USB_SUBCLASS_VENDOR, |
| .interface_protocol = USB_PROTOCOL_TEST_FTDI, |
| }; |
| |
| std::vector<usb_peripheral::FunctionDescriptor> function_descs; |
| function_descs.push_back(ftdi_function_desc); |
| |
| ASSERT_NO_FATAL_FAILURES(SetupPeripheralDevice(device_desc, std::move(function_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 FtdiTest : public zxtest::Test { |
| public: |
| void SetUp() override { ASSERT_NO_FATAL_FAILURES(bus_.InitFtdi(&devpath_)); } |
| |
| void TearDown() override { |
| auto result = bus_.peripheral().ClearFunctions(); |
| ASSERT_OK(result.status()); |
| ASSERT_FALSE(result->result.is_err()); |
| |
| 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 50 milliseconds. |
| constexpr int timeout_length = 50000; |
| auto timeout = std::time(0) + timeout_length; |
| while (std::time(0) < 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(FtdiTest, 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]); |
| } |
| } |
| |
| } // namespace |
| } // namespace usb_virtual_bus |