| // 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. |
| |
| #pragma once |
| |
| #include <atomic> |
| #include <ddk/device.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/intrusive_wavl_tree.h> |
| #include <fbl/mutex.h> |
| #include <fbl/recycler.h> |
| #include <fbl/ref_counted_upgradeable.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/vector.h> |
| #include <fs/handler.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/eventpair.h> |
| #include <zircon/compiler.h> |
| #include <zircon/thread_annotations.h> |
| |
| namespace devmgr { |
| |
| class CompositeDevice; |
| class DeviceControllerConnection; |
| struct ProxyIostate; |
| |
| } // namespace devmgr |
| |
| #define DEV_MAGIC 'MDEV' |
| |
| // This needs to be a struct, not a class, to match the public definition |
| struct zx_device : fbl::RefCountedUpgradeable<zx_device>, fbl::Recyclable<zx_device> { |
| ~zx_device() = default; |
| |
| zx_device(const zx_device&) = delete; |
| zx_device& operator=(const zx_device&) = delete; |
| |
| static zx_status_t Create(fbl::RefPtr<zx_device>* out_dev); |
| |
| zx_status_t OpenOp(zx_device_t** dev_out, uint32_t flags) { |
| return Dispatch(ops->open, ZX_OK, dev_out, flags); |
| } |
| |
| zx_status_t CloseOp(uint32_t flags) { |
| return Dispatch(ops->close, ZX_OK, flags); |
| } |
| |
| void UnbindOp() { |
| Dispatch(ops->unbind); |
| } |
| |
| void ReleaseOp() { |
| Dispatch(ops->release); |
| } |
| |
| zx_status_t SuspendOp(uint32_t flags) { |
| return Dispatch(ops->suspend, ZX_ERR_NOT_SUPPORTED, flags); |
| } |
| |
| zx_status_t ResumeOp(uint32_t flags) { |
| return Dispatch(ops->resume, ZX_ERR_NOT_SUPPORTED, flags); |
| } |
| |
| zx_status_t ReadOp(void* buf, size_t count, zx_off_t off, size_t* actual) { |
| return Dispatch(ops->read, ZX_ERR_NOT_SUPPORTED, buf, count, off, actual); |
| } |
| |
| zx_status_t WriteOp(const void* buf, size_t count, zx_off_t off, size_t* actual) { |
| return Dispatch(ops->write, ZX_ERR_NOT_SUPPORTED, buf, count, off, actual); |
| } |
| |
| zx_off_t GetSizeOp() { |
| return Dispatch(ops->get_size, 0lu); |
| } |
| |
| zx_status_t IoctlOp(uint32_t op, const void* in_buf, size_t in_len, void* out_buf, |
| size_t out_len, size_t* out_actual) { |
| return Dispatch(ops->ioctl, ZX_ERR_NOT_SUPPORTED, op, in_buf, in_len, out_buf, out_len, out_actual); |
| } |
| |
| zx_status_t MessageOp(fidl_msg_t* msg, fidl_txn_t* txn) { |
| return Dispatch(ops->message, ZX_ERR_NOT_SUPPORTED, msg, txn); |
| } |
| |
| void PushBindConn(const fs::FidlConnection& conn); |
| bool PopBindConn(fs::FidlConnection* conn); |
| |
| // Check if this devhost has a device with the given ID, and if so returns a |
| // reference to it. |
| static fbl::RefPtr<zx_device> GetDeviceFromLocalId(uint64_t local_id); |
| |
| uint64_t local_id() const { return local_id_; } |
| void set_local_id(uint64_t id); |
| |
| uintptr_t magic = DEV_MAGIC; |
| |
| const zx_protocol_device_t* ops = nullptr; |
| |
| // reserved for driver use; will not be touched by devmgr |
| void* ctx = nullptr; |
| |
| uint32_t flags = 0; |
| |
| zx::eventpair event; |
| zx::eventpair local_event; |
| // The RPC channel is owned by |conn| |
| zx::unowned_channel rpc; |
| |
| // most devices implement a single |
| // protocol beyond the base device protocol |
| uint32_t protocol_id = 0; |
| void* protocol_ops = nullptr; |
| |
| // driver that has published this device |
| zx_driver_t* driver = nullptr; |
| |
| // parent in the device tree |
| fbl::RefPtr<zx_device_t> parent; |
| |
| // for the parent's device_list |
| fbl::DoublyLinkedListNodeState<zx_device*> node; |
| struct Node { |
| static fbl::DoublyLinkedListNodeState<zx_device*>& node_state(zx_device& obj) { |
| return obj.node; |
| } |
| }; |
| |
| // list of this device's children in the device tree |
| fbl::DoublyLinkedList<zx_device*, Node> children; |
| |
| // list node for the defer_device_list |
| fbl::DoublyLinkedListNodeState<zx_device*> defer; |
| struct DeferNode { |
| static fbl::DoublyLinkedListNodeState<zx_device*>& node_state(zx_device& obj) { |
| return obj.defer; |
| } |
| }; |
| |
| // This is an atomic so that the connection's async loop can inspect this |
| // value to determine if an expected shutdown is happening. See comments in |
| // devhost_remove(). |
| std::atomic<devmgr::DeviceControllerConnection*> conn = nullptr; |
| |
| fbl::Mutex proxy_ios_lock; |
| devmgr::ProxyIostate* proxy_ios TA_GUARDED(proxy_ios_lock) = nullptr; |
| |
| char name[ZX_DEVICE_NAME_MAX + 1] = {}; |
| |
| // Trait structures for the local ID map |
| struct LocalIdNode { |
| static fbl::WAVLTreeNodeState<fbl::RefPtr<zx_device>>& node_state(zx_device& obj) { |
| return obj.local_id_node_; |
| } |
| }; |
| struct LocalIdKeyTraits { |
| static uint64_t GetKey(const zx_device& obj) { return obj.local_id_; } |
| static bool LessThan(const uint64_t& key1, const uint64_t& key2) { return key1 < key2; } |
| static bool EqualTo (const uint64_t& key1, const uint64_t& key2) { return key1 == key2; } |
| }; |
| |
| void set_composite(fbl::RefPtr<devmgr::CompositeDevice> composite); |
| fbl::RefPtr<devmgr::CompositeDevice> take_composite(); |
| |
| private: |
| zx_device() = default; |
| |
| friend class fbl::Recyclable<zx_device_t>; |
| void fbl_recycle(); |
| |
| // Templates that dispatch the protocol operations if they were set. |
| // If they were not set, the second paramater is returned to the caller |
| // (usually ZX_ERR_NOT_SUPPORTED) |
| template <typename RetType, typename... ArgTypes> |
| RetType Dispatch(RetType (*op)(void* ctx, ArgTypes...), |
| RetType fallback, ArgTypes... args) { |
| return op ? (*op)(ctx, args...) : fallback; |
| } |
| |
| template <typename... ArgTypes> |
| void Dispatch(void (*op)(void* ctx, ArgTypes...), ArgTypes... args) { |
| if (op) { |
| (*op)(ctx, args...); |
| } |
| } |
| |
| // If this device is a component of a composite, this points to the |
| // composite control structure. |
| fbl::RefPtr<devmgr::CompositeDevice> composite_; |
| |
| fbl::WAVLTreeNodeState<fbl::RefPtr<zx_device>> local_id_node_; |
| |
| // Identifier assigned by devmgr that can be used to assemble composite |
| // devices. |
| uint64_t local_id_ = 0; |
| |
| // The connection associated with a fuchsia.device.Controller/Bind. |
| fbl::Mutex bind_conn_lock_; |
| fbl::Vector<fs::FidlConnection> bind_conn_ TA_GUARDED(bind_conn_lock_); |
| }; |
| |
| // zx_device_t objects must be created or initialized by the driver manager's |
| // device_create() function. Drivers MAY NOT touch any |
| // fields in the zx_device_t, except for the protocol_id and protocol_ops |
| // fields which it may fill out after init and before device_add() is called, |
| // and the ctx field which may be used to store driver-specific data. |
| |
| // clang-format off |
| |
| #define DEV_FLAG_DEAD 0x00000001 // being deleted |
| #define DEV_FLAG_VERY_DEAD 0x00000002 // safe for ref0 and release() |
| #define DEV_FLAG_UNBINDABLE 0x00000004 // nobody may bind to this device |
| #define DEV_FLAG_BUSY 0x00000010 // device being created |
| #define DEV_FLAG_INSTANCE 0x00000020 // this device was created-on-open |
| #define DEV_FLAG_MULTI_BIND 0x00000080 // this device accepts many children |
| #define DEV_FLAG_ADDED 0x00000100 // device_add() has been called for this device |
| #define DEV_FLAG_INVISIBLE 0x00000200 // device not visible via devfs |
| #define DEV_FLAG_UNBOUND 0x00000400 // informed that it should self-delete asap |
| #define DEV_FLAG_WANTS_REBIND 0x00000800 // when last child goes, rebind this device |
| |
| // clang-format on |
| |
| zx_status_t device_bind(const fbl::RefPtr<zx_device_t>& dev, const char* drv_libname); |
| zx_status_t device_unbind(const fbl::RefPtr<zx_device_t>& dev); |
| zx_status_t device_open(const fbl::RefPtr<zx_device_t>& dev, fbl::RefPtr<zx_device_t>* out, |
| uint32_t flags); |
| // Note that device_close() is intended to consume a reference (logically, the |
| // one created by device_open). |
| zx_status_t device_close(fbl::RefPtr<zx_device_t> dev, uint32_t flags); |