blob: e439120786eb62ec74d5025b45c8d560da84f88f [file] [log] [blame]
// 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);
}