blob: b7737fa68fbea23460bad58377c5b132aed9f217 [file] [log] [blame] [edit]
// Copyright 2020 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 LIB_DRIVER_INCOMING_CPP_NAMESPACE_H_
#define LIB_DRIVER_INCOMING_CPP_NAMESPACE_H_
#include <fidl/fuchsia.io/cpp/markers.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/component/incoming/cpp/service.h>
#include <lib/driver/incoming/cpp/service_validator.h>
#include <lib/fdf/cpp/protocol.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/namespace.h>
#include <lib/fidl_driver/cpp/transport.h>
// Forward declare to avoid pulling in unneeded generated bindings
namespace fuchsia_component_runner {
class ComponentNamespaceEntry;
namespace wire {
class ComponentNamespaceEntry;
}
} // namespace fuchsia_component_runner
namespace fdf {
namespace internal {
template <typename T>
static constexpr std::false_type always_false{};
// Returns a client_end to the connection.
template <typename ServiceMember>
zx::result<fdf::ClientEnd<typename ServiceMember::ProtocolType>> DriverTransportConnect(
fidl::UnownedClientEnd<fuchsia_io::Directory> svc_dir, std::string_view instance) {
static_assert((std::is_same_v<typename ServiceMember::ProtocolType::Transport,
fidl::internal::DriverTransport>),
"ServiceMember must use DriverTransport. Double check the FIDL protocol.");
zx::channel client_token, server_token;
if (zx_status_t status = zx::channel::create(0, &client_token, &server_token); status != ZX_OK) {
return zx::error(status);
}
auto [client_end, server_end] = fdf::Endpoints<typename ServiceMember::ProtocolType>::Create();
if (zx_status_t status =
fdf::ProtocolConnect(std::move(client_token), std::move(server_end.TakeHandle()));
status != ZX_OK) {
return zx::error(status);
}
if (zx_status_t status =
fdio_service_connect_at(svc_dir.handle()->get(),
component::MakeServiceMemberPath<ServiceMember>(instance).c_str(),
server_token.release());
status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(client_end));
}
// Uses the passed in server_end to make the connection.
template <typename ServiceMember>
zx::result<> DriverTransportConnect(fidl::UnownedClientEnd<fuchsia_io::Directory> svc_dir,
fdf::ServerEnd<typename ServiceMember::ProtocolType> server_end,
std::string_view instance) {
static_assert((std::is_same_v<typename ServiceMember::ProtocolType::Transport,
fidl::internal::DriverTransport>),
"ServiceMember must use DriverTransport. Double check the FIDL protocol.");
zx::channel client_token, server_token;
if (zx_status_t status = zx::channel::create(0, &client_token, &server_token); status != ZX_OK) {
return zx::error(status);
}
if (zx_status_t status =
fdf::ProtocolConnect(std::move(client_token), std::move(server_end.TakeHandle()));
status != ZX_OK) {
return zx::error(status);
}
if (zx_status_t status =
fdio_service_connect_at(svc_dir.handle()->get(),
component::MakeServiceMemberPath<ServiceMember>(instance).c_str(),
server_token.release());
status != ZX_OK) {
return zx::error(status);
}
return zx::ok();
}
} // namespace internal
// Manages a driver's namespace.
class Namespace final {
public:
// Creates a namespace from `DriverStartArgs::ns`.
static zx::result<Namespace> Create(
fidl::VectorView<fuchsia_component_runner::wire::ComponentNamespaceEntry>& entries);
// Creates a namespace from natural types version of `DriverStartArgs::ns`.
static zx::result<Namespace> Create(
std::vector<fuchsia_component_runner::ComponentNamespaceEntry>& entries);
Namespace() = default;
~Namespace();
Namespace(Namespace&& other) noexcept;
Namespace& operator=(Namespace&& other) noexcept;
void SetServiceValidator(std::optional<ServiceValidator> service_validator) {
service_validator_ = std::move(service_validator);
}
// Connect to a protocol within a driver's namespace.
// DriverTransport is not supported. Protocols using DriverTransport must be service members.
template <typename Protocol, typename = std::enable_if_t<!fidl::IsServiceMemberV<Protocol>>>
zx::result<fidl::ClientEnd<Protocol>> Connect(
const char* protocol_name = fidl::DiscoverableProtocolName<Protocol>) const {
static_assert((std::is_same_v<typename Protocol::Transport, fidl::internal::ChannelTransport>),
"Protocol must use ChannelTransport. Use a ServiceMember for DriverTransport.");
return component::ConnectAt<Protocol>(svc_dir(), protocol_name);
}
// Connects |server_end| to a protocol within a driver's namespace.
// DriverTransport is not supported. Protocols using DriverTransport must be service members.
template <typename Protocol, typename = std::enable_if_t<!fidl::IsServiceMemberV<Protocol>>>
zx::result<> Connect(fidl::ServerEnd<Protocol> server_end,
const char* protocol_name = fidl::DiscoverableProtocolName<Protocol>) const {
static_assert((std::is_same_v<typename Protocol::Transport, fidl::internal::ChannelTransport>),
"Protocol must use ChannelTransport. Use a ServiceMember for DriverTransport.");
return component::ConnectAt(svc_dir(), std::move(server_end), protocol_name);
}
// Connect to a service within a driver's namespace.
template <typename FidlService>
zx::result<typename FidlService::ServiceClient> OpenService(std::string_view instance) const {
static_assert(fidl::IsServiceV<FidlService>, "FidlService must be a service.");
return component::OpenServiceAt<FidlService>(svc_dir(), instance);
}
// Protocol must compose fuchsia.io/Node.
// DriverTransport is not supported. Protocols using DriverTransport must be service members.
template <typename Protocol>
zx::result<fidl::ClientEnd<Protocol>> Open(const char* path, fuchsia_io::Flags flags) const {
static_assert(!fidl::IsServiceMemberV<Protocol>, "Protocol must not be a ServiceMember.");
static_assert((std::is_same_v<typename Protocol::Transport, fidl::internal::ChannelTransport>),
"Protocol must use ChannelTransport. Use a ServiceMember for DriverTransport.");
auto [client_end, server_end] = fidl::Endpoints<Protocol>::Create();
zx::result result = Open(path, flags, server_end.TakeChannel());
if (result.is_error()) {
return result.take_error();
}
return zx::ok(std::move(client_end));
}
// Connects to the |ServiceMember| protocol.
//
// |instance| refers to the name of the instance of the service.
//
// Returns a ClientEnd of type corresponding to the given protocol
// e.g. fidl::ClientEnd or fdf::ClientEnd.
template <typename ServiceMember>
zx::result<fidl::internal::ClientEndType<typename ServiceMember::ProtocolType>> Connect(
std::string_view instance = component::kDefaultInstance) const {
static_assert(
fidl::IsServiceMemberV<ServiceMember>,
"ServiceMember type must be the Protocol inside of a Service, eg: fuchsia_hardware_pci::Service::Device.");
if constexpr (std::is_same_v<typename ServiceMember::ProtocolType::Transport,
fidl::internal::ChannelTransport>) {
if (service_validator_) {
if (!service_validator_->IsValidZirconServiceInstance(
std::string(ServiceMember::ServiceName), std::string(instance))) {
return zx::error(ZX_ERR_NOT_FOUND);
}
}
return component::ConnectAtMember<ServiceMember>(svc_dir(), instance);
} else if constexpr (std::is_same_v<typename ServiceMember::ProtocolType::Transport,
fidl::internal::DriverTransport>) {
if (service_validator_) {
if (!service_validator_->IsValidDriverServiceInstance(
std::string(ServiceMember::ServiceName), std::string(instance))) {
return zx::error(ZX_ERR_NOT_FOUND);
}
}
return internal::DriverTransportConnect<ServiceMember>(svc_dir(), instance);
} else {
static_assert(internal::always_false<ServiceMember>);
}
}
// Connects |server_end| to the |ServiceMember| protocol.
//
// |instance| refers to the name of the instance of the service.
//
// The type of |server_end| must correspond to the given protocol's transport
// e.g. fidl::ServerEnd for ChannelTransport or fdf::ServerEnd for DriverTransport.
template <typename ServiceMember>
zx::result<> Connect(
fidl::internal::ServerEndType<typename ServiceMember::ProtocolType> server_end,
std::string_view instance = component::kDefaultInstance) const {
static_assert(
fidl::IsServiceMemberV<ServiceMember>,
"ServiceMember type must be the Protocol inside of a Service, eg: fuchsia_hardware_pci::Service::Device.");
if constexpr (std::is_same_v<typename ServiceMember::ProtocolType::Transport,
fidl::internal::ChannelTransport>) {
if (service_validator_) {
if (!service_validator_->IsValidZirconServiceInstance(
std::string(ServiceMember::ServiceName), std::string(instance))) {
return zx::error(ZX_ERR_NOT_FOUND);
}
}
return component::ConnectAtMember<ServiceMember>(svc_dir(), std::move(server_end), instance);
} else if constexpr (std::is_same_v<typename ServiceMember::ProtocolType::Transport,
fidl::internal::DriverTransport>) {
if (service_validator_) {
if (!service_validator_->IsValidDriverServiceInstance(
std::string(ServiceMember::ServiceName), std::string(instance))) {
return zx::error(ZX_ERR_NOT_FOUND);
}
}
return internal::DriverTransportConnect<ServiceMember>(svc_dir(), std::move(server_end),
instance);
} else {
static_assert(internal::always_false<ServiceMember>);
}
}
fidl::UnownedClientEnd<fuchsia_io::Directory> svc_dir() const { return svc_dir_; }
private:
explicit Namespace(fdio_ns_t* incoming, fidl::ClientEnd<fuchsia_io::Directory> svc_dir);
Namespace(const Namespace& other) = delete;
Namespace& operator=(const Namespace& other) = delete;
// Opens |path| in the driver's namespace. DriverTransport is not supported.
zx::result<> Open(const char* path, fuchsia_io::Flags flags, zx::channel server_end) const;
fdio_ns_t* incoming_ = nullptr;
fidl::ClientEnd<fuchsia_io::Directory> svc_dir_;
std::optional<ServiceValidator> service_validator_;
};
} // namespace fdf
#endif // LIB_DRIVER_INCOMING_CPP_NAMESPACE_H_