blob: 92abed8950e332a0fa6a0b604ff5d55d64d6b769 [file] [log] [blame]
// Copyright 2022 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 <lib/driver/compat/cpp/connect.h>
#include <string_view>
namespace compat {
namespace {
const std::string kCompatServicePath = std::string("/svc/") + fuchsia_driver_compat::Service::Name;
}
fdf::async_helpers::AsyncTask FindDirectoryEntries(fidl::ClientEnd<fuchsia_io::Directory> dir,
async_dispatcher_t* dispatcher,
EntriesCallback cb) {
auto client = fidl::WireClient<fuchsia_io::Directory>(std::move(dir), dispatcher);
fdf::async_helpers::AsyncTask task;
// NOTE: It would be nicer to call Watch, but that is not supported in the component's
// VFS implementation.
client->ReadDirents(fuchsia_io::wire::kMaxBuf)
.Then([cb = std::move(cb), completer = task.CreateCompleter()](
fidl::WireUnownedResult<::fuchsia_io::Directory::ReadDirents>& result) mutable {
// The format of the packed dirent structure, taken from io.fidl.
struct dirent {
// Describes the inode of the entry.
uint64_t ino;
// Describes the length of the dirent name in bytes.
uint8_t size;
// Describes the type of the entry. Aligned with the
// POSIX d_type values. Use `DIRENT_TYPE_*` constants.
uint8_t type;
// Unterminated name of entry.
char name[0];
} __PACKED;
if (!result.ok()) {
cb(zx::error(result.status()));
return;
}
size_t index = 0;
auto& dirents = result->dirents;
std::vector<std::string> names;
while (index + sizeof(dirent) < dirents.size()) {
auto packed_entry = reinterpret_cast<const dirent*>(&result->dirents[index]);
size_t packed_entry_size = sizeof(dirent) + packed_entry->size;
if (index + packed_entry_size > dirents.size()) {
break;
}
names.emplace_back(packed_entry->name, packed_entry->size);
index += packed_entry_size;
}
cb(zx::ok(std::move(names)));
});
task.SetItem(std::move(client));
return task;
}
zx::result<std::vector<std::string>> FindDirectoryEntries(
fidl::ClientEnd<fuchsia_io::Directory> dir) {
auto client = fidl::WireSyncClient<fuchsia_io::Directory>(std::move(dir));
// NOTE: It would be nicer to call Watch, but that is not supported in the component's
// VFS implementation.
fidl::WireResult result = client->ReadDirents(fuchsia_io::wire::kMaxBuf);
if (!result.ok()) {
return zx::error(result.status());
}
// The format of the packed dirent structure, taken from io.fidl.
struct dirent {
// Describes the inode of the entry.
uint64_t ino;
// Describes the length of the dirent name in bytes.
uint8_t size;
// Describes the type of the entry. Aligned with the
// POSIX d_type values. Use `DIRENT_TYPE_*` constants.
uint8_t type;
// Unterminated name of entry.
char name[0];
} __PACKED;
size_t index = 0;
auto& dirents = result->dirents;
std::vector<std::string> names;
while (index + sizeof(dirent) < dirents.size()) {
auto packed_entry = reinterpret_cast<const dirent*>(&result->dirents[index]);
size_t packed_entry_size = sizeof(dirent) + packed_entry->size;
if (index + packed_entry_size > dirents.size()) {
break;
}
names.emplace_back(packed_entry->name, packed_entry->size);
index += packed_entry_size;
}
return zx::ok(std::move(names));
}
fdf::async_helpers::AsyncTask ConnectToParentDevices(async_dispatcher_t* dispatcher,
const fdf::Namespace* ns, ConnectCallback cb) {
auto result =
ns->Open<fuchsia_io::Directory>(kCompatServicePath.c_str(), fuchsia_io::wire::kPermReadable);
if (result.is_error()) {
cb(result.take_error());
return fdf::async_helpers::AsyncTask(true);
}
return FindDirectoryEntries(
std::move(result.value()), dispatcher,
[ns, cb = std::move(cb)](zx::result<std::vector<std::string>> entries) mutable {
if (entries.is_error()) {
cb(entries.take_error());
return;
}
std::vector<ParentDevice> devices;
for (auto& name : entries.value()) {
if (name == ".") {
continue;
}
auto result = ns->Connect<fuchsia_driver_compat::Device>(
std::string(fuchsia_driver_compat::Service::Name)
.append("/")
.append(name)
.append("/device")
.c_str());
if (result.is_error()) {
cb(result.take_error());
return;
}
devices.push_back(ParentDevice{
.name = std::move(name),
.client = std::move(result.value()),
});
}
cb(zx::ok(std::move(devices)));
});
}
zx::result<std::vector<ParentDevice>> ConnectToParentDevices(const fdf::Namespace* ns) {
auto result =
ns->Open<fuchsia_io::Directory>(kCompatServicePath.c_str(), fuchsia_io::wire::kPermReadable);
if (result.is_error()) {
return result.take_error();
}
zx::result<std::vector<std::string>> entries = FindDirectoryEntries(std::move(result.value()));
if (entries.is_error()) {
return entries.take_error();
}
std::vector<ParentDevice> devices;
for (auto& name : entries.value()) {
if (name == ".") {
continue;
}
auto result =
ns->Connect<fuchsia_driver_compat::Device>(std::string(fuchsia_driver_compat::Service::Name)
.append("/")
.append(name)
.append("/device")
.c_str());
if (result.is_error()) {
return result.take_error();
}
devices.push_back(ParentDevice{
.name = std::move(name),
.client = std::move(result.value()),
});
}
return zx::ok(std::move(devices));
}
} // namespace compat