| // 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. |
| |
| #ifndef SRC_DEVICES_LIB_COMPAT_COMPAT_H_ |
| #define SRC_DEVICES_LIB_COMPAT_COMPAT_H_ |
| |
| #include <fidl/fuchsia.driver.compat/cpp/wire.h> |
| #include <fidl/fuchsia.driver.framework/cpp/wire.h> |
| #include <lib/driver2/devfs_exporter.h> |
| #include <lib/driver2/logger.h> |
| #include <lib/driver2/namespace.h> |
| #include <lib/driver2/start_args.h> |
| #include <lib/fit/defer.h> |
| #include <lib/fpromise/promise.h> |
| #include <lib/service/llcpp/service_handler.h> |
| #include <lib/sys/component/llcpp/outgoing_directory.h> |
| |
| #include <unordered_map> |
| #include <unordered_set> |
| |
| #include "src/devices/lib/compat/symbols.h" |
| #include "src/lib/storage/vfs/cpp/pseudo_dir.h" |
| #include "src/lib/storage/vfs/cpp/synchronous_vfs.h" |
| |
| namespace compat { |
| |
| // This represents a protocol that a driver is offering to a child driver. |
| struct ProtocolOffer { |
| // The name of the protocol being offered. The driver is responsible for |
| // making sure this protocol has been exported in it's outgoing directory. |
| std::string protocol_name; |
| // A callback that will be called when this protocol is going out of scope. |
| // This is useful if a driver is exposing a protocol to multiple children, |
| // and would like to perform cleanup if all children are removed. |
| std::shared_ptr<fit::deferred_callback> remove_protocol_callback; |
| }; |
| |
| // This represents a service that a driver is offering to a child driver. |
| struct ServiceOffer { |
| struct RenamedInstance { |
| std::string source_name; |
| std::string target_name; |
| }; |
| // The name of the service being offered. The driver is responsible for |
| // making sure this service has been exported in its outgoing directory. |
| std::string service_name; |
| // The list of instance names that the driver wishes to perform while offering |
| // the service. |
| std::vector<RenamedInstance> renamed_instances; |
| // The list of instances the driver wishes to offer with this service. |
| // If this is empty, all instances will be included. |
| // NOTE: If a rename is happening, this should be the list of new names. |
| std::vector<std::string> included_instances; |
| // A callback that will be called when this service offer is going out of scope. |
| // This is useful if a driver is exposing a service to multiple children, |
| // and would like to perform cleanup if all children are removed. |
| std::shared_ptr<fit::deferred_callback> remove_service_callback; |
| }; |
| |
| class ChildOffers { |
| public: |
| void AddProtocol(ProtocolOffer offer) { protocol_offers_.push_back(std::move(offer)); } |
| |
| void AddService(ServiceOffer offer) { service_offers_.push_back(std::move(offer)); } |
| |
| std::vector<fuchsia_component_decl::wire::Offer> CreateOffers(fidl::ArenaBase& arena); |
| |
| private: |
| std::vector<ProtocolOffer> protocol_offers_; |
| std::vector<ServiceOffer> service_offers_; |
| }; |
| |
| using Metadata = std::vector<uint8_t>; |
| using MetadataMap = std::unordered_map<uint32_t, const Metadata>; |
| |
| // The DeviceServer class vends the fuchsia_driver_compat::Device interface. |
| // It represents a single device. |
| class DeviceServer : public fidl::WireServer<fuchsia_driver_compat::Device> { |
| public: |
| DeviceServer(std::string topological_path, MetadataMap metadata) |
| : topological_path_(std::move(topological_path)), metadata_(std::move(metadata)) {} |
| |
| // Functions to implement the DFv1 device API. |
| zx_status_t AddMetadata(uint32_t type, const void* data, size_t size); |
| zx_status_t GetMetadata(uint32_t type, void* buf, size_t buflen, size_t* actual); |
| zx_status_t GetMetadataSize(uint32_t type, size_t* out_size); |
| |
| void set_dir(fidl::ClientEnd<fuchsia_io::Directory> dir) { dir_ = std::move(dir); } |
| |
| private: |
| // fuchsia.driver.compat.Compat |
| void GetTopologicalPath(GetTopologicalPathRequestView request, |
| GetTopologicalPathCompleter::Sync& completer) override; |
| void GetMetadata(GetMetadataRequestView request, GetMetadataCompleter::Sync& completer) override; |
| void ConnectFidl(ConnectFidlRequestView request, ConnectFidlCompleter::Sync& completer) override; |
| |
| std::string topological_path_; |
| MetadataMap metadata_; |
| fidl::ClientEnd<fuchsia_io::Directory> dir_; |
| }; |
| |
| class Child; |
| |
| // The Interop class holds information about what this component is exposing in its namespace. |
| // This class is used to expose things in the outgoing namespace in a way that a child |
| // compat driver can understand. |
| class Interop { |
| public: |
| // Create an Interop. Each parameter here is an unowned pointer, so these objects must outlive |
| // the Interop class. |
| static zx::status<Interop> Create(async_dispatcher_t* dispatcher, const driver::Namespace* ns, |
| component::OutgoingDirectory* outgoing); |
| |
| // Take a Child, and export its fuchsia.driver.compat service, and it export it to devfs. |
| fpromise::promise<void, zx_status_t> ExportChild(Child* child, fbl::RefPtr<fs::Vnode> dev_node); |
| |
| private: |
| friend class Child; |
| |
| async_dispatcher_t* dispatcher_; |
| const driver::Namespace* ns_; |
| component::OutgoingDirectory* outgoing_; |
| |
| std::unique_ptr<fs::SynchronousVfs> vfs_; |
| fbl::RefPtr<fs::PseudoDir> devfs_exports_; |
| driver::DevfsExporter exporter_; |
| }; |
| |
| zx::status<fidl::WireSharedClient<fuchsia_driver_compat::Device>> ConnectToParentDevice( |
| async_dispatcher_t* dispatcher, const driver::Namespace* ns, std::string_view name = "default"); |
| |
| // The Child class represents a child device. |
| // When a Child is removed, it will remove the services it added in the |
| // outgoing directory. |
| class Child { |
| public: |
| Child(std::string name, uint32_t proto_id, std::string topological_path, MetadataMap metadata) |
| : topological_path_(std::move(topological_path)), |
| name_(std::move(name)), |
| proto_id_(proto_id), |
| compat_device_(topological_path_, std::move(metadata)) {} |
| |
| DeviceServer& compat_device() { return compat_device_; } |
| std::string_view name() { return name_; } |
| std::string_view topological_path() { return topological_path_; } |
| uint32_t proto_id() { return proto_id_; } |
| ChildOffers& offers() { return offers_; } |
| |
| // This is a way to give the child shared ownership over something. |
| // When child is removed, there will be one less reference to callback, |
| // and callback will be called when all references are removed. |
| void AddCallback(std::shared_ptr<fit::deferred_callback> callback) { |
| callbacks_.push_back(std::move(callback)); |
| } |
| |
| // Create a vector of offers based on the service instances that have been added to the child. |
| std::vector<fuchsia_component_decl::wire::Offer> CreateOffers(fidl::ArenaBase& arena); |
| |
| private: |
| std::string topological_path_; |
| std::string name_; |
| uint32_t proto_id_; |
| |
| DeviceServer compat_device_; |
| ChildOffers offers_; |
| |
| // A list of callbacks to potentially call when this class is destructed. |
| std::vector<std::shared_ptr<fit::deferred_callback>> callbacks_; |
| }; |
| |
| } // namespace compat |
| |
| #endif // SRC_DEVICES_LIB_COMPAT_COMPAT_H_ |