blob: 14a7048c5958dff26d49aaeec6c282ffa71fe56d [file] [log] [blame]
// Copyright 2019 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 "suspend_task.h"
#include "coordinator.h"
namespace {
bool IsDeviceBeingRemoved(fbl::RefPtr<Device> device) {
auto remove_task = device->GetActiveRemove();
if ((device->state() == Device::State::kUnbinding) || (device->state() == Device::State::kDead) ||
(remove_task != nullptr)) {
return true;
}
return false;
}
} // namespace
SuspendTask::SuspendTask(fbl::RefPtr<Device> device, uint32_t flags, Completion completion)
: Task(device->coordinator->dispatcher(), std::move(completion)),
device_(std::move(device)),
flags_(flags) {}
SuspendTask::~SuspendTask() = default;
fbl::RefPtr<SuspendTask> SuspendTask::Create(fbl::RefPtr<Device> device, uint32_t flags,
Completion completion) {
return fbl::MakeRefCounted<SuspendTask>(std::move(device), flags, std::move(completion));
}
void SuspendTask::Run() {
bool found_more_dependencies = false;
for (auto& child : device_->children()) {
// Use a switch statement here so that this gets reconsidered if we add
// more states.
switch (child.state()) {
// If the device is dead, any existing suspend task would have been forcibly completed.
case Device::State::kDead:
case Device::State::kUnbinding:
case Device::State::kSuspended:
continue;
case Device::State::kInitializing:
case Device::State::kSuspending:
case Device::State::kActive:
case Device::State::kResuming:
case Device::State::kResumed:
break;
}
if (!IsDeviceBeingRemoved(fbl::RefPtr(&child))) {
AddDependency(child.RequestSuspendTask(flags_));
found_more_dependencies = true;
}
}
if (found_more_dependencies) {
return;
}
// Handle the device proxy, if it exists, after children since they might
// depend on it.
if (device_->proxy() != nullptr) {
switch (device_->proxy()->state()) {
case Device::State::kDead:
case Device::State::kSuspended:
case Device::State::kResuming:
case Device::State::kResumed:
break;
case Device::State::kInitializing:
case Device::State::kUnbinding:
case Device::State::kSuspending:
case Device::State::kActive: {
AddDependency(device_->proxy()->RequestSuspendTask(flags_));
return;
}
}
}
if (device_->state() == Device::State::kInitializing) {
auto init_task = device_->GetActiveInit();
ZX_ASSERT(init_task != nullptr);
AddDependency(init_task);
return;
}
// The device is about to be removed, complete suspend right away
if (IsDeviceBeingRemoved(device_)) {
return Complete(ZX_OK);
}
// The device is about to be resumed, wait for it to complete.
if (device_->state() == Device::State::kResuming) {
auto resume_task = device_->GetActiveResume();
AddDependency(resume_task);
return;
}
// Check if this device is not in a driver_host. This happens for the
// top-level devices like /sys provided by devcoordinator,
// or the device is already dead.
if (device_->host() == nullptr) {
// device shouldn't be set to suspended if it's already dead
if (device_->state() != Device::State::kDead) {
device_->set_state(Device::State::kSuspended);
}
return Complete(ZX_OK);
}
auto completion = [this](zx_status_t status) { Complete(status); };
zx_status_t status = device_->SendSuspend(flags_, std::move(completion));
if (status != ZX_OK) {
Complete(status);
}
}