[garnet][lib] Introduce usb-virtual-bus
Library for setting up usb-virtual-bus, usb-peripheral as well as providing methods for usb device integration tests
Code has been tested in https://fuchsia-review.googlesource.com/c/fuchsia/+/296016
Change-Id: I8bc6c8b61c3511952d5b5c9734312b3182895997
diff --git a/src/lib/isolated_devmgr/BUILD.gn b/src/lib/isolated_devmgr/BUILD.gn
index 18ccabb..2faeee7 100644
--- a/src/lib/isolated_devmgr/BUILD.gn
+++ b/src/lib/isolated_devmgr/BUILD.gn
@@ -53,6 +53,36 @@
]
}
+static_library("usb-virtual-bus") {
+ testonly = true
+ sources = [
+ "usb-virtual-bus-helper.cc",
+ "usb-virtual-bus-helper.h",
+ "usb-virtual-bus.cc",
+ "usb-virtual-bus.h",
+ ]
+ public_deps = [
+ "//garnet/public/lib/gtest",
+ "//zircon/public/fidl/fuchsia-hardware-usb-peripheral:fuchsia-hardware-usb-peripheral_llcpp",
+ "//zircon/public/fidl/fuchsia-hardware-usb-virtual-bus:fuchsia-hardware-usb-virtual-bus_llcpp",
+ "//zircon/public/lib/devmgr-integration-test",
+ ]
+ deps = [
+ "//sdk/fidl/fuchsia.sys",
+ "//sdk/lib/sys/cpp",
+ "//zircon/public/lib/async-loop-cpp",
+ "//zircon/public/lib/ddk",
+ "//zircon/public/lib/devmgr-launcher",
+ "//zircon/public/lib/fbl",
+ "//zircon/public/lib/fdio",
+ "//zircon/public/lib/fidl-async-cpp",
+ "//zircon/public/lib/fit",
+ "//zircon/public/lib/zx",
+ ]
+ configs += [ "//build/config/fuchsia:enable_zircon_asserts" ]
+ configs += [ "//build/config/fuchsia:static_cpp_standard_library" ]
+}
+
executable("test_bin") {
testonly = true
diff --git a/src/lib/isolated_devmgr/usb-virtual-bus-helper.cc b/src/lib/isolated_devmgr/usb-virtual-bus-helper.cc
new file mode 100644
index 0000000..32bdad3
--- /dev/null
+++ b/src/lib/isolated_devmgr/usb-virtual-bus-helper.cc
@@ -0,0 +1,33 @@
+// 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 "usb-virtual-bus-helper.h"
+
+#include <lib/fdio/watcher.h>
+
+#include <fbl/function.h>
+#include <fbl/string.h>
+
+namespace usb_virtual_bus_helper {
+
+zx_status_t WaitForAnyFile(int dirfd, int event, const char* name, void* cookie) {
+ if (event != WATCH_EVENT_ADD_FILE) {
+ return ZX_OK;
+ }
+ if (*name) {
+ *reinterpret_cast<fbl::String*>(cookie) = fbl::String(name);
+ return ZX_ERR_STOP;
+ } else {
+ return ZX_OK;
+ }
+}
+
+zx_status_t WaitForFile(int dirfd, int event, const char* fn, void* name) {
+ if (event != WATCH_EVENT_ADD_FILE) {
+ return ZX_OK;
+ }
+ return strcmp(static_cast<const char*>(name), fn) ? ZX_OK : ZX_ERR_STOP;
+}
+
+} // namespace usb_virtual_bus_helper
diff --git a/src/lib/isolated_devmgr/usb-virtual-bus-helper.h b/src/lib/isolated_devmgr/usb-virtual-bus-helper.h
new file mode 100644
index 0000000..799a8fd
--- /dev/null
+++ b/src/lib/isolated_devmgr/usb-virtual-bus-helper.h
@@ -0,0 +1,14 @@
+// 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.
+
+#ifndef SRC_LIB_ISOLATED_DEVMGR_USB_VIRTUAL_BUS_HELPER_H_
+#define SRC_LIB_ISOLATED_DEVMGR_USB_VIRTUAL_BUS_HELPER_H_
+#include <zircon/types.h>
+namespace usb_virtual_bus_helper {
+
+zx_status_t WaitForAnyFile(int dirfd, int event, const char* name, void* cookie);
+
+zx_status_t WaitForFile(int dirfd, int event, const char* fn, void* name);
+} // namespace usb_virtual_bus_helper
+#endif // SRC_LIB_ISOLATED_DEVMGR_USB_VIRTUAL_BUS_HELPER_H_
diff --git a/src/lib/isolated_devmgr/usb-virtual-bus.cc b/src/lib/isolated_devmgr/usb-virtual-bus.cc
new file mode 100644
index 0000000..c323e68
--- /dev/null
+++ b/src/lib/isolated_devmgr/usb-virtual-bus.cc
@@ -0,0 +1,143 @@
+// 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 "usb-virtual-bus.h"
+
+#include <fcntl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/devmgr-integration-test/fixture.h>
+#include <lib/fdio/directory.h>
+#include <lib/fdio/fd.h>
+#include <lib/fdio/fdio.h>
+#include <lib/fdio/namespace.h>
+#include <lib/fdio/spawn.h>
+#include <lib/fdio/unsafe.h>
+#include <lib/fdio/watcher.h>
+#include <lib/fidl-async/cpp/bind.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <zircon/device/vfs.h>
+#include <zircon/hw/usb.h>
+#include <zircon/processargs.h>
+#include <zircon/syscalls.h>
+
+#include <gtest/gtest.h>
+
+namespace usb_virtual_bus {
+
+USBVirtualBusBase::USBVirtualBusBase(std::string pkg_url, std::string svc_name) {
+ // create a IsolatedDevmgr through test component
+ auto ctx = sys::ComponentContext::Create();
+ fidl::InterfacePtr<fuchsia::sys::Launcher> launcher;
+ ctx->svc()->Connect(launcher.NewRequest());
+
+ zx::channel req;
+ services_ = sys::ServiceDirectory::CreateWithRequest(&req);
+
+ fuchsia::sys::LaunchInfo info;
+ info.directory_request = std::move(req);
+ info.url = pkg_url;
+ launcher->CreateComponent(std::move(info), ctlr_.NewRequest());
+ ctlr_.set_error_handler([](zx_status_t err) { FAIL() << "Controller shouldn't exit"; });
+
+ zx::channel devfs_server_end, devfs_client_end;
+ zx::channel::create(0, &devfs_server_end, &devfs_client_end);
+ services_->Connect(svc_name, std::move(devfs_server_end));
+ int fd_devfs;
+ fdio_fd_create(devfs_client_end.release(), &fd_devfs);
+ devfs_.reset(fd_devfs);
+}
+
+void USBVirtualBusBase::InitPeripheral() {
+ fbl::unique_fd fd;
+ devmgr_integration_test::RecursiveWaitForFile(devfs_root(),
+ "sys/platform/11:03:0/usb-virtual-bus", &fd);
+ ASSERT_EQ(fd.is_valid(), true);
+ zx::channel virtual_bus;
+ ASSERT_EQ(fdio_get_service_handle(fd.release(), virtual_bus.reset_and_get_address()), ZX_OK);
+ virtual_bus_.emplace(std::move(virtual_bus));
+
+ auto enable_result = virtual_bus_->Enable();
+ ASSERT_EQ(enable_result.status(), ZX_OK);
+ ASSERT_EQ(enable_result.value().status, ZX_OK);
+
+ char peripheral_str[] = "usb-peripheral";
+ fd.reset(openat(devfs_root().get(), "class", O_RDONLY));
+ while (fdio_watch_directory(fd.get(), usb_virtual_bus_helper::WaitForFile, ZX_TIME_INFINITE,
+ &peripheral_str) != ZX_ERR_STOP)
+ continue;
+
+ fd.reset(openat(devfs_root().get(), "class/usb-peripheral", O_RDONLY));
+ fbl::String devpath;
+ while (fdio_watch_directory(fd.get(), usb_virtual_bus_helper::WaitForAnyFile, ZX_TIME_INFINITE,
+ &devpath) != ZX_ERR_STOP)
+ continue;
+
+ devpath = fbl::String::Concat({fbl::String("class/usb-peripheral/"), fbl::String(devpath)});
+ fd.reset(openat(devfs_root().get(), devpath.c_str(), O_RDWR));
+
+ zx::channel peripheral;
+ ASSERT_EQ(fdio_get_service_handle(fd.release(), peripheral.reset_and_get_address()), ZX_OK);
+ peripheral_.emplace(std::move(peripheral));
+
+ auto clear_result = peripheral_->ClearFunctions();
+ ASSERT_EQ(clear_result.status(), ZX_OK);
+ ASSERT_EQ(clear_result.value().result.is_err(), false);
+}
+
+int USBVirtualBusBase::GetRootFd() { return devfs_root().get(); }
+
+class EventWatcher : public ::llcpp::fuchsia::hardware::usb::peripheral::Events::Interface {
+ public:
+ explicit EventWatcher(async::Loop* loop, zx::channel svc, size_t functions)
+ : loop_(loop), functions_(functions) {
+ fidl::Bind(loop->dispatcher(), std::move(svc), this);
+ }
+
+ void FunctionRegistered(FunctionRegisteredCompleter::Sync completer);
+
+ bool all_functions_registered() { return functions_registered_ == functions_; }
+
+ private:
+ async::Loop* loop_;
+ const size_t functions_;
+ size_t functions_registered_ = 0;
+
+ bool state_changed_ = false;
+};
+
+void EventWatcher::FunctionRegistered(FunctionRegisteredCompleter::Sync completer) {
+ functions_registered_++;
+ if (all_functions_registered()) {
+ state_changed_ = true;
+ loop_->Quit();
+ completer.Close(ZX_ERR_CANCELED);
+ } else {
+ completer.Reply();
+ }
+}
+
+void USBVirtualBusBase::SetupPeripheralDevice(const DeviceDescriptor& device_desc,
+ std::vector<FunctionDescriptor> function_descs) {
+ zx::channel state_change_sender, state_change_receiver;
+ ASSERT_EQ(zx::channel::create(0, &state_change_sender, &state_change_receiver), ZX_OK);
+ auto set_result = peripheral_->SetStateChangeListener(std::move(state_change_receiver));
+ ASSERT_EQ(set_result.status(), ZX_OK);
+
+ auto set_config = peripheral_->SetConfiguration(
+ device_desc, ::fidl::VectorView(function_descs.size(), function_descs.data()));
+ ASSERT_EQ(set_config.status(), ZX_OK);
+ ASSERT_FALSE(set_config->result.is_err());
+
+ async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
+ EventWatcher watcher(&loop, std::move(state_change_sender), function_descs.size());
+ loop.Run();
+ ASSERT_TRUE(watcher.all_functions_registered());
+
+ auto connect_result = virtual_bus_->Connect();
+ ASSERT_EQ(connect_result.status(), ZX_OK);
+ ASSERT_EQ(connect_result.value().status, ZX_OK);
+}
+
+} // namespace usb_virtual_bus
diff --git a/src/lib/isolated_devmgr/usb-virtual-bus.h b/src/lib/isolated_devmgr/usb-virtual-bus.h
new file mode 100644
index 0000000..ed9dd43
--- /dev/null
+++ b/src/lib/isolated_devmgr/usb-virtual-bus.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef SRC_LIB_ISOLATED_DEVMGR_USB_VIRTUAL_BUS_H_
+#define SRC_LIB_ISOLATED_DEVMGR_USB_VIRTUAL_BUS_H_
+
+#include <fuchsia/hardware/usb/peripheral/llcpp/fidl.h>
+#include <fuchsia/hardware/usb/virtual/bus/llcpp/fidl.h>
+#include <fuchsia/sys/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+#include <zircon/types.h>
+
+#include <optional>
+#include <vector>
+
+#include <fbl/unique_fd.h>
+
+#include "usb-virtual-bus-helper.h"
+
+namespace usb_virtual_bus {
+using llcpp::fuchsia::hardware::usb::peripheral::DeviceDescriptor;
+using llcpp::fuchsia::hardware::usb::peripheral::FunctionDescriptor;
+class USBVirtualBusBase {
+ public:
+ USBVirtualBusBase(std::string pkg_url, std::string svc_name);
+ void InitPeripheral();
+ void SetupPeripheralDevice(const DeviceDescriptor& device_desc,
+ std::vector<FunctionDescriptor> function_descs);
+ int GetRootFd();
+ fbl::unique_fd& devfs_root() { return devfs_; };
+ llcpp::fuchsia::hardware::usb::peripheral::Device::SyncClient& peripheral() {
+ return peripheral_.value();
+ }
+ llcpp::fuchsia::hardware::usb::virtual_::bus::Bus::SyncClient& virtual_bus() {
+ return virtual_bus_.value();
+ }
+
+ private:
+ fbl::unique_fd devfs_;
+ std::shared_ptr<sys::ServiceDirectory> services_;
+ fidl::InterfacePtr<fuchsia::sys::ComponentController> ctlr_;
+ std::optional<llcpp::fuchsia::hardware::usb::peripheral::Device::SyncClient> peripheral_;
+ std::optional<llcpp::fuchsia::hardware::usb::virtual_::bus::Bus::SyncClient> virtual_bus_;
+ DISALLOW_COPY_ASSIGN_AND_MOVE(USBVirtualBusBase);
+};
+
+} // namespace usb_virtual_bus
+
+#endif // SRC_LIB_ISOLATED_DEVMGR_USB_VIRTUAL_BUS_H_