| // Copyright 2018 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. |
| |
| #include "zx-device.h" |
| |
| #include <fbl/auto_call.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/mutex.h> |
| #include "composite-device.h" |
| #include "devhost.h" |
| |
| zx_status_t zx_device::Create(fbl::RefPtr<zx_device>* out_dev) { |
| *out_dev = fbl::AdoptRef(new zx_device()); |
| return ZX_OK; |
| } |
| |
| void zx_device::PushBindConn(const fs::FidlConnection& conn) { |
| fbl::AutoLock<fbl::Mutex> lock(&bind_conn_lock_); |
| bind_conn_.push_back(conn); |
| } |
| |
| bool zx_device::PopBindConn(fs::FidlConnection* conn) { |
| fbl::AutoLock<fbl::Mutex> lock(&bind_conn_lock_); |
| if (bind_conn_.is_empty()) { |
| return false; |
| } |
| *conn = bind_conn_[0]; |
| bind_conn_.erase(0); |
| return true; |
| } |
| |
| // We must disable thread-safety analysis due to not being able to statically |
| // guarantee the lock holding invariant. Instead, we acquire the lock if |
| // it's not already being held by the current thread. |
| void zx_device::fbl_recycle() TA_NO_THREAD_SAFETY_ANALYSIS { |
| bool acq_lock = !devmgr::DM_LOCK_HELD(); |
| if (acq_lock) { |
| devmgr::DM_LOCK(); |
| } |
| auto unlock = fbl::MakeAutoCall([acq_lock]() TA_NO_THREAD_SAFETY_ANALYSIS { |
| if (acq_lock) { |
| devmgr::DM_UNLOCK(); |
| } |
| }); |
| |
| if (this->flags & DEV_FLAG_INSTANCE) { |
| // these don't get removed, so mark dead state here |
| this->flags |= DEV_FLAG_DEAD | DEV_FLAG_VERY_DEAD; |
| } |
| if (this->flags & DEV_FLAG_BUSY) { |
| // this can happen if creation fails |
| // the caller to device_add() will free it |
| printf("device: %p(%s): ref=0, busy, not releasing\n", this, this->name); |
| return; |
| } |
| #if TRACE_ADD_REMOVE |
| printf("device: %p(%s): ref=0. releasing.\n", this, this->name); |
| #endif |
| |
| if (!(this->flags & DEV_FLAG_VERY_DEAD)) { |
| printf("device: %p(%s): only mostly dead (this is bad)\n", this, this->name); |
| } |
| if (!this->children.is_empty()) { |
| printf("device: %p(%s): still has children! not good.\n", this, this->name); |
| } |
| |
| composite_.reset(); |
| this->event.reset(); |
| this->local_event.reset(); |
| |
| // Put on the defered work list for finalization |
| devmgr::defer_device_list.push_back(this); |
| |
| // Immediately finalize if there's not an active enumerator |
| if (devmgr::devhost_enumerators == 0) { |
| devmgr::devhost_finalize(); |
| } |
| } |
| |
| static fbl::Mutex local_id_map_lock_; |
| static fbl::WAVLTree<uint64_t, fbl::RefPtr<zx_device>, zx_device::LocalIdKeyTraits, |
| zx_device::LocalIdNode> local_id_map_ TA_GUARDED(local_id_map_lock_); |
| |
| void zx_device::set_local_id(uint64_t id) { |
| // If this is the last reference, we want it to go away outside of the lock |
| fbl::RefPtr<zx_device> old_entry; |
| |
| fbl::AutoLock guard(&local_id_map_lock_); |
| if (local_id_ != 0) { |
| old_entry = local_id_map_.erase(*this); |
| ZX_ASSERT(old_entry.get() == this); |
| } |
| |
| local_id_ = id; |
| if (id != 0) { |
| local_id_map_.insert(fbl::WrapRefPtr(this)); |
| } |
| } |
| |
| fbl::RefPtr<zx_device> zx_device::GetDeviceFromLocalId(uint64_t local_id) { |
| fbl::AutoLock guard(&local_id_map_lock_); |
| auto itr = local_id_map_.find(local_id); |
| if (itr == local_id_map_.end()) { |
| return nullptr; |
| } |
| return fbl::WrapRefPtr(&*itr); |
| } |
| |
| fbl::RefPtr<devmgr::CompositeDevice> zx_device::take_composite() { |
| return std::move(composite_); |
| } |
| |
| void zx_device::set_composite(fbl::RefPtr<devmgr::CompositeDevice> composite) { |
| composite_ = std::move(composite); |
| } |