blob: 25f070e01c89304d3f69de5128c0edabe47adbd0 [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 <fuchsia/device/manager/llcpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl-async/cpp/bind.h>
#include <ddk/driver.h>
#include <zxtest/zxtest.h>
#include "devhost.h"
#include "zx-device.h"
namespace {
class FakeCoordinator : public ::llcpp::fuchsia::device::manager::Coordinator::Interface {
public:
zx_status_t Connect(async_dispatcher_t* dispatcher, zx::channel request) {
return fidl::Bind(dispatcher, std::move(request), this);
}
void AddDevice(::zx::channel coordinator, ::zx::channel device_controller,
::fidl::VectorView<uint64_t> props, ::fidl::StringView name, uint32_t protocol_id,
::fidl::StringView driver_path, ::fidl::StringView args,
llcpp::fuchsia::device::manager::AddDeviceConfig device_add_config, bool has_init,
::zx::channel client_remote, AddDeviceCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_AddDevice_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void ScheduleRemove(bool unbind_self, ScheduleRemoveCompleter::Sync completer) override {}
void AddCompositeDevice(::fidl::StringView name,
llcpp::fuchsia::device::manager::CompositeDeviceDescriptor comp_desc,
AddCompositeDeviceCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_AddCompositeDevice_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void PublishMetadata(::fidl::StringView device_path, uint32_t key,
::fidl::VectorView<uint8_t> data,
PublishMetadataCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_PublishMetadata_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void AddDeviceInvisible(::zx::channel coordinator, ::zx::channel device_controller,
::fidl::VectorView<uint64_t> props, ::fidl::StringView name,
uint32_t protocol_id, ::fidl::StringView driver_path,
::fidl::StringView args, bool has_init, ::zx::channel client_remote,
AddDeviceInvisibleCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_AddDeviceInvisible_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void MakeVisible(MakeVisibleCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_MakeVisible_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void BindDevice(::fidl::StringView driver_path, BindDeviceCompleter::Sync completer) override {
bind_count_++;
llcpp::fuchsia::device::manager::Coordinator_BindDevice_Result response;
zx_status_t status = ZX_OK;
response.set_err(&status);
completer.Reply(std::move(response));
}
void GetTopologicalPath(GetTopologicalPathCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_GetTopologicalPath_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void LoadFirmware(::fidl::StringView fw_path, LoadFirmwareCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_LoadFirmware_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void GetMetadata(uint32_t key, GetMetadataCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_GetMetadata_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void GetMetadataSize(uint32_t key, GetMetadataSizeCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_GetMetadataSize_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void AddMetadata(uint32_t key, ::fidl::VectorView<uint8_t> data,
AddMetadataCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_AddMetadata_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void ScheduleUnbindChildren(ScheduleUnbindChildrenCompleter::Sync completer) override {}
void RunCompatibilityTests(int64_t hook_wait_time,
RunCompatibilityTestsCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_RunCompatibilityTests_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
void DirectoryWatch(uint32_t mask, uint32_t options, ::zx::channel watcher,
DirectoryWatchCompleter::Sync completer) override {
llcpp::fuchsia::device::manager::Coordinator_DirectoryWatch_Result response;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
response.set_err(&status);
completer.Reply(std::move(response));
}
uint32_t bind_count_ = 0;
uint32_t unbind_reply_count_ = 0;
};
class CoreTest : public zxtest::Test {
protected:
CoreTest() : loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {
loop_.StartThread("devhost-test-loop");
}
void Connect(zx::channel* out) {
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
ASSERT_OK(coordinator_.Connect(loop_.dispatcher(), std::move(remote)));
*out = std::move(local);
}
// This simulates receiving an unbind and remove request from the devcoordinator.
void UnbindDevice(fbl::RefPtr<zx_device> dev) {
devmgr::ApiAutoLock lock;
devmgr::devhost_device_unbind(dev);
// devhost_device_complete_removal() will drop the device reference added by device_add().
// Since we never called device_add(), we should increment the reference count here.
fbl::RefPtr<zx_device_t> dev_add_ref(dev.get());
__UNUSED auto ptr = fbl::ExportToRawPtr(&dev_add_ref);
devmgr::devhost_device_complete_removal(dev);
}
async::Loop loop_;
FakeCoordinator coordinator_;
};
TEST_F(CoreTest, RebindNoChildren) {
fbl::RefPtr<zx_device> dev;
ASSERT_OK(zx_device::Create(&dev));
zx_protocol_device_t ops = {};
dev->ops = &ops;
zx::channel rpc;
ASSERT_NO_FATAL_FAILURES(Connect(&rpc));
dev->coordinator_rpc = zx::unowned(rpc);
EXPECT_EQ(device_rebind(dev.get()), ZX_OK);
EXPECT_EQ(coordinator_.bind_count_, 1);
dev->flags |= DEV_FLAG_DEAD;
}
TEST_F(CoreTest, RebindHasOneChild) {
{
uint32_t unbind_count = 0;
fbl::RefPtr<zx_device> parent;
zx::channel rpc;
ASSERT_NO_FATAL_FAILURES(Connect(&rpc));
zx_protocol_device_t ops = {};
ops.unbind = [](void* ctx) { *static_cast<uint32_t*>(ctx) += 1; };
ASSERT_OK(zx_device::Create(&parent));
parent->ops = &ops;
parent->ctx = &unbind_count;
parent->coordinator_rpc = zx::unowned(rpc);
{
fbl::RefPtr<zx_device> child;
ASSERT_OK(zx_device::Create(&child));
child->ops = &ops;
child->ctx = &unbind_count;
child->rpc = zx::unowned(rpc);
parent->children.push_back(child.get());
child->parent = parent;
EXPECT_EQ(device_rebind(parent.get()), ZX_OK);
EXPECT_EQ(coordinator_.bind_count_, 0);
ASSERT_NO_FATAL_FAILURES(UnbindDevice(child));
EXPECT_EQ(unbind_count, 1);
child->flags |= DEV_FLAG_DEAD;
}
parent->flags |= DEV_FLAG_DEAD;
}
EXPECT_EQ(coordinator_.bind_count_, 1);
}
TEST_F(CoreTest, RebindHasMultipleChildren) {
{
uint32_t unbind_count = 0;
fbl::RefPtr<zx_device> parent;
zx::channel rpc;
ASSERT_NO_FATAL_FAILURES(Connect(&rpc));
zx_protocol_device_t ops = {};
ops.unbind = [](void* ctx) { *static_cast<uint32_t*>(ctx) += 1; };
ASSERT_OK(zx_device::Create(&parent));
parent->ops = &ops;
parent->ctx = &unbind_count;
parent->coordinator_rpc = zx::unowned(rpc);
{
std::array<fbl::RefPtr<zx_device>, 5> children;
for (auto& child : children) {
ASSERT_OK(zx_device::Create(&child));
child->ops = &ops;
child->ctx = &unbind_count;
child->rpc = zx::unowned(rpc);
parent->children.push_back(child.get());
child->parent = parent;
}
EXPECT_EQ(device_rebind(parent.get()), ZX_OK);
for (auto& child : children) {
EXPECT_EQ(coordinator_.bind_count_, 0);
ASSERT_NO_FATAL_FAILURES(UnbindDevice(child));
}
EXPECT_EQ(unbind_count, children.size());
for (auto& child : children) {
child->flags |= DEV_FLAG_DEAD;
}
}
parent->flags |= DEV_FLAG_DEAD;
}
EXPECT_EQ(coordinator_.bind_count_, 1);
}
} // namespace