| // Copyright 2016 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 ZIRCON_SYSTEM_CORE_DEVMGR_DEVHOST_DEVHOST_H_ |
| #define ZIRCON_SYSTEM_CORE_DEVMGR_DEVHOST_DEVHOST_H_ |
| |
| #include <ddk/binding.h> |
| #include <ddk/device.h> |
| #include <ddk/driver.h> |
| #include <ddktl/fidl.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/string.h> |
| #include <fbl/unique_ptr.h> |
| #include <fuchsia/device/manager/llcpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/async/cpp/wait.h> |
| #include <lib/zx/channel.h> |
| #include <stdint.h> |
| #include <threads.h> |
| #include <zircon/compiler.h> |
| #include <zircon/fidl.h> |
| #include <lib/zircon-internal/thread_annotations.h> |
| #include <zircon/types.h> |
| |
| #include "../shared/async-loop-owned-rpc-handler.h" |
| #include "lock.h" |
| #include "zx-device.h" |
| |
| namespace devmgr { |
| |
| namespace fuchsia = ::llcpp::fuchsia; |
| |
| struct BindContext { |
| fbl::RefPtr<zx_device_t> parent; |
| fbl::RefPtr<zx_device_t> child; |
| }; |
| |
| struct CreationContext { |
| fbl::RefPtr<zx_device_t> parent; |
| fbl::RefPtr<zx_device_t> child; |
| zx::unowned_channel rpc; |
| }; |
| |
| void devhost_set_bind_context(BindContext* ctx); |
| void devhost_set_creation_context(CreationContext* ctx); |
| |
| } // namespace devmgr |
| |
| // Nothing outside of devmgr/{devmgr,devhost,rpc-device}.c |
| // should be calling devhost_*() APIs, as this could |
| // violate the internal locking design. |
| |
| // Safe external APIs are in device.h and device_internal.h |
| |
| // Note that this must be a struct to match the public opaque declaration. |
| struct zx_driver : fbl::DoublyLinkedListable<fbl::RefPtr<zx_driver>>, fbl::RefCounted<zx_driver> { |
| static zx_status_t Create(fbl::RefPtr<zx_driver>* out_driver); |
| |
| const char* name() const { return name_; } |
| |
| zx_driver_rec_t* driver_rec() const { return driver_rec_; } |
| |
| zx_status_t status() const { return status_; } |
| |
| const fbl::String& libname() const { return libname_; } |
| |
| void set_name(const char* name) { name_ = name; } |
| |
| void set_driver_rec(zx_driver_rec_t* driver_rec) { driver_rec_ = driver_rec; } |
| |
| void set_ops(const zx_driver_ops_t* ops) { ops_ = ops; } |
| |
| void set_status(zx_status_t status) { status_ = status; } |
| |
| void set_libname(fbl::StringPiece libname) { libname_ = libname; } |
| |
| // Interface to |ops|. These names contain Op in order to not |
| // collide with e.g. RefPtr names. |
| |
| bool has_init_op() const { return ops_->init != nullptr; } |
| |
| bool has_bind_op() const { return ops_->bind != nullptr; } |
| |
| bool has_create_op() const { return ops_->create != nullptr; } |
| |
| bool has_run_unit_tests_op() const { return ops_->run_unit_tests != nullptr; } |
| |
| zx_status_t InitOp() { return ops_->init(&ctx_); } |
| |
| zx_status_t BindOp(devmgr::BindContext* bind_context, |
| const fbl::RefPtr<zx_device_t>& device) const { |
| devmgr::devhost_set_bind_context(bind_context); |
| auto status = ops_->bind(ctx_, device.get()); |
| devmgr::devhost_set_bind_context(nullptr); |
| return status; |
| } |
| |
| zx_status_t CreateOp(devmgr::CreationContext* creation_context, |
| const fbl::RefPtr<zx_device_t>& parent, const char* name, const char* args, |
| zx_handle_t rpc_channel) const { |
| devmgr::devhost_set_creation_context(creation_context); |
| auto status = ops_->create(ctx_, parent.get(), name, args, rpc_channel); |
| devmgr::devhost_set_creation_context(nullptr); |
| return status; |
| } |
| |
| void ReleaseOp() const { |
| // TODO(kulakowski/teisenbe) Consider poisoning the ops_ table on release. |
| ops_->release(ctx_); |
| } |
| |
| bool RunUnitTestsOp(const fbl::RefPtr<zx_device_t>& parent, zx::channel test_output) const { |
| return ops_->run_unit_tests(ctx_, parent.get(), test_output.release()); |
| } |
| |
| private: |
| friend fbl::unique_ptr<zx_driver> std::make_unique<zx_driver>(); |
| zx_driver() = default; |
| |
| const char* name_ = nullptr; |
| zx_driver_rec_t* driver_rec_ = nullptr; |
| const zx_driver_ops_t* ops_ = nullptr; |
| void* ctx_ = nullptr; |
| fbl::String libname_; |
| zx_status_t status_ = ZX_OK; |
| }; |
| |
| namespace devmgr { |
| |
| extern zx_protocol_device_t device_default_ops; |
| |
| // |client_remote| will only be a valid handle if the device was added with |
| // DEVICE_ADD_INVISIBLE or DEVICE_ADD_MUST_ISOLATE. |
| zx_status_t devhost_device_add(const fbl::RefPtr<zx_device_t>& dev, |
| const fbl::RefPtr<zx_device_t>& parent, |
| const zx_device_prop_t* props, uint32_t prop_count, |
| const char* proxy_args, zx::channel client_remote) REQ_DM_LOCK; |
| // Note that devhost_device_remove() takes a RefPtr rather than a const RefPtr&. |
| // It intends to consume a reference. |
| zx_status_t devhost_device_remove(fbl::RefPtr<zx_device_t> dev) REQ_DM_LOCK; |
| zx_status_t devhost_device_bind(const fbl::RefPtr<zx_device_t>& dev, |
| const char* drv_libname) REQ_DM_LOCK; |
| zx_status_t devhost_device_rebind(const fbl::RefPtr<zx_device_t>& dev) REQ_DM_LOCK; |
| zx_status_t devhost_device_unbind(const fbl::RefPtr<zx_device_t>& dev) REQ_DM_LOCK; |
| zx_status_t devhost_device_run_compatibility_tests(const fbl::RefPtr<zx_device_t>& dev, |
| int64_t hook_wait_time) REQ_DM_LOCK; |
| zx_status_t devhost_device_create(zx_driver_t* drv, const char* name, void* ctx, |
| const zx_protocol_device_t* ops, |
| fbl::RefPtr<zx_device_t>* out) REQ_DM_LOCK; |
| zx_status_t devhost_device_open(const fbl::RefPtr<zx_device_t>& dev, fbl::RefPtr<zx_device_t>* out, |
| uint32_t flags) REQ_DM_LOCK; |
| zx_status_t devhost_device_close(fbl::RefPtr<zx_device_t> dev, uint32_t flags) REQ_DM_LOCK; |
| zx_status_t devhost_device_suspend(const fbl::RefPtr<zx_device_t>& dev, uint32_t flags) REQ_DM_LOCK; |
| zx_status_t devhost_device_suspend_new(const fbl::RefPtr<zx_device_t>& dev, |
| fuchsia_device_DevicePowerState requested_state, |
| fuchsia_device_DevicePowerState *out_state); |
| zx_status_t devhost_device_resume_new(const fbl::RefPtr<zx_device_t>& dev, |
| fuchsia_device_DevicePowerState requested_state, |
| fuchsia_device_DevicePowerState *out_state); |
| void devhost_device_destroy(zx_device_t* dev) REQ_DM_LOCK; |
| |
| zx_status_t devhost_load_firmware(const fbl::RefPtr<zx_device_t>& dev, const char* path, |
| zx_handle_t* fw, size_t* size) REQ_DM_LOCK; |
| zx_status_t devhost_get_topo_path(const fbl::RefPtr<zx_device_t>& dev, char* path, size_t max, |
| size_t* actual); |
| |
| zx_status_t devhost_get_metadata(const fbl::RefPtr<zx_device_t>& dev, uint32_t type, void* buf, |
| size_t buflen, size_t* actual) REQ_DM_LOCK; |
| |
| zx_status_t devhost_get_metadata_size(const fbl::RefPtr<zx_device_t>& dev, uint32_t type, |
| size_t* size) REQ_DM_LOCK; |
| |
| zx_status_t devhost_add_metadata(const fbl::RefPtr<zx_device_t>& dev, uint32_t type, |
| const void* data, size_t length) REQ_DM_LOCK; |
| |
| zx_status_t devhost_publish_metadata(const fbl::RefPtr<zx_device_t>& dev, const char* path, |
| uint32_t type, const void* data, size_t length) REQ_DM_LOCK; |
| |
| zx_status_t devhost_device_add_composite(const fbl::RefPtr<zx_device_t>& dev, const char* name, |
| const zx_device_prop_t* props, size_t props_count, |
| const device_component_t* components, |
| size_t components_count, |
| uint32_t coresident_device_index) REQ_DM_LOCK; |
| |
| class DevhostControllerConnection : public AsyncLoopOwnedRpcHandler<DevhostControllerConnection>, |
| public fuchsia::device::manager::DevhostController::Interface { |
| public: |
| DevhostControllerConnection() = default; |
| |
| static void HandleRpc(fbl::unique_ptr<DevhostControllerConnection> conn, |
| async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status, |
| const zx_packet_signal_t* signal); |
| zx_status_t HandleRead(); |
| |
| private: |
| void CreateDevice(zx::channel rpc, ::fidl::StringView driver_path, ::zx::vmo driver, |
| ::zx::handle parent_proxy, ::fidl::StringView proxy_args, |
| uint64_t local_device_id, CreateDeviceCompleter::Sync completer) override; |
| void CreateCompositeDevice(zx::channel rpc, ::fidl::VectorView<uint64_t> components, |
| ::fidl::StringView name, uint64_t local_device_id, |
| CreateCompositeDeviceCompleter::Sync completer) override; |
| void CreateDeviceStub(zx::channel rpc, uint32_t protocol_id, uint64_t local_device_id, |
| CreateDeviceStubCompleter::Sync completer) override; |
| }; |
| |
| struct DevfsConnection : AsyncLoopOwnedRpcHandler<DevfsConnection> { |
| DevfsConnection() = default; |
| |
| static void HandleRpc(fbl::unique_ptr<DevfsConnection> conn, async_dispatcher_t* dispatcher, |
| async::WaitBase* wait, zx_status_t status, |
| const zx_packet_signal_t* signal); |
| |
| fbl::RefPtr<zx_device_t> dev; |
| size_t io_off = 0; |
| uint32_t flags = 0; |
| }; |
| |
| zx_status_t devhost_fidl_handler(fidl_msg_t* msg, fidl_txn_t* txn, void* cookie); |
| |
| // Attaches channel |c| to new state representing an open connection to |dev|. |
| zx_status_t devhost_device_connect(const fbl::RefPtr<zx_device_t>& dev, uint32_t flags, |
| zx::channel c); |
| |
| zx_status_t devhost_start_connection(fbl::unique_ptr<DevfsConnection> ios, zx::channel h); |
| |
| // routines devhost uses to talk to dev coordinator |
| // |client_remote| will only be a valid handle if the device was added with |
| // DEVICE_ADD_INVISIBLE or DEVICE_ADD_MUST_ISOLATE. |
| zx_status_t devhost_add(const fbl::RefPtr<zx_device_t>& dev, const fbl::RefPtr<zx_device_t>& child, |
| const char* proxy_args, const zx_device_prop_t* props, uint32_t prop_count, |
| zx::channel client_remote) REQ_DM_LOCK; |
| zx_status_t devhost_remove(const fbl::RefPtr<zx_device_t>& dev) REQ_DM_LOCK; |
| void devhost_make_visible(const fbl::RefPtr<zx_device_t>& dev); |
| |
| // State that is shared between the zx_device implementation and devhost-core.cpp |
| void devhost_finalize() REQ_DM_LOCK; |
| extern fbl::DoublyLinkedList<zx_device*, zx_device::DeferNode> defer_device_list USE_DM_LOCK; |
| extern int devhost_enumerators USE_DM_LOCK; |
| |
| // Lookup the a driver by name, and if it's not found, install the given vmo as |
| // that driver. |
| zx_status_t dh_find_driver(fbl::StringPiece libname, zx::vmo vmo, fbl::RefPtr<zx_driver_t>* out); |
| |
| // Construct a string describing the path of |dev| relative to its most |
| // distant ancestor in this devhost. |
| const char* mkdevpath(const fbl::RefPtr<zx_device_t>& dev, char* path, size_t max); |
| |
| // Retrieve the singleton async loop |
| async::Loop* DevhostAsyncLoop(); |
| |
| } // namespace devmgr |
| |
| #endif // ZIRCON_SYSTEM_CORE_DEVMGR_DEVHOST_DEVHOST_H_ |