| // 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/mutex.h> |
| #include <fbl/recycler.h> |
| #include <fbl/ref_counted_upgradeable.h> |
| #include <fbl/ref_ptr.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/eventpair.h> |
| #include <zircon/compiler.h> |
| #include <zircon/thread_annotations.h> |
| |
| namespace devmgr { |
| |
| struct DevcoordinatorConnection; |
| 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 ops->open(ctx, dev_out, flags); |
| } |
| |
| zx_status_t OpenAtOp(zx_device_t** dev_out, const char* path, uint32_t flags) { |
| return ops->open_at(ctx, dev_out, path, flags); |
| } |
| |
| zx_status_t CloseOp(uint32_t flags) { return ops->close(ctx, flags); } |
| |
| void UnbindOp() { ops->unbind(ctx); } |
| |
| void ReleaseOp() { ops->release(ctx); } |
| |
| zx_status_t SuspendOp(uint32_t flags) { return ops->suspend(ctx, flags); } |
| |
| zx_status_t ResumeOp(uint32_t flags) { return ops->resume(ctx, flags); } |
| |
| zx_status_t ReadOp(void* buf, size_t count, zx_off_t off, size_t* actual) { |
| return ops->read(ctx, buf, count, off, actual); |
| } |
| |
| zx_status_t WriteOp(const void* buf, size_t count, zx_off_t off, size_t* actual) { |
| return ops->write(ctx, buf, count, off, actual); |
| } |
| |
| zx_off_t GetSizeOp() { return ops->get_size(ctx); } |
| |
| 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 ops->ioctl(ctx, op, in_buf, in_len, out_buf, out_len, out_actual); |
| } |
| |
| zx_status_t MessageOp(fidl_msg_t* msg, fidl_txn_t* txn) { return ops->message(ctx, msg, txn); } |
| |
| uintptr_t magic = DEV_MAGIC; |
| |
| 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::DevcoordinatorConnection*> conn = nullptr; |
| |
| fbl::Mutex proxy_ios_lock; |
| devmgr::ProxyIostate* proxy_ios TA_GUARDED(proxy_ios_lock) = nullptr; |
| |
| char name[ZX_DEVICE_NAME_MAX + 1] = {}; |
| |
| private: |
| zx_device() = default; |
| |
| friend class fbl::Recyclable<zx_device_t>; |
| void fbl_recycle(); |
| }; |
| |
| // 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_at(const fbl::RefPtr<zx_device_t>& dev, fbl::RefPtr<zx_device_t>* out, |
| const char* path, uint32_t flags); |
| // Note that device_close() is intended to consume a reference (logically, the |
| // one created by device_open_at). |
| zx_status_t device_close(fbl::RefPtr<zx_device_t> dev, uint32_t flags); |