blob: ffb60ac89b5592a4113ecdbca1a4382e5d727adc [file] [log] [blame]
// Copyright 2025 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.
#ifndef LIB_DRIVER_POWER_CPP_SUSPEND_H_
#define LIB_DRIVER_POWER_CPP_SUSPEND_H_
#include <fidl/fuchsia.power.system/cpp/fidl.h>
#include <lib/driver/component/cpp/driver_base.h>
#include <lib/driver/incoming/cpp/namespace.h>
#include <lib/zx/result.h>
#include <optional>
#include <type_traits>
namespace fdf_power {
namespace internal {
zx::result<fidl::ServerEnd<fuchsia_power_system::SuspendBlocker>> RegisterSuspendHooks(
fdf::Namespace& incoming, std::string_view name);
}
// This class is a wrapper for a callback type that must be called into exactly once
// before destruction. It is a move only type.
class Completer {
public:
explicit Completer(fit::callback<void()> callback) : callback_(std::move(callback)) {}
Completer(Completer&& other) noexcept : callback_(std::move(other.callback_)) {
other.callback_ = std::nullopt;
}
Completer(const Completer&) = delete;
Completer& operator=(const Completer&) = delete;
~Completer();
// Calls the wrapped callback function.
// This method should not be invoked more than once.
void operator()();
private:
std::optional<fit::callback<void()>> callback_;
};
// This is the completer for the Suspend operation in |Suspendable|.
class SuspendCompleter final : public Completer {
public:
using Completer::Completer;
using Completer::operator();
};
// This is the completer for the Resume operation in |Suspendable|.
class ResumeCompleter final : public Completer {
public:
using Completer::Completer;
using Completer::operator();
};
template <typename Driver>
class Suspendable {
public:
// Interface to be implemented.
virtual void Suspend(SuspendCompleter completer) = 0;
virtual void Resume(ResumeCompleter completer) = 0;
virtual bool SuspendEnabled() = 0;
explicit Suspendable() : server_(this) {
static_cast<Driver*>(this)->RegisterInitMethods(
fit::bind_member(this, &Suspendable::RegisterSuspendHooks));
}
// Returns true if:
// * suspend was enabled and we successfully registered with SAG
// * suspend was not enabled
// Returns false if suspend was enabled and we failed to register with SAG.
bool SuspendActive() { return binding_.has_value(); }
virtual ~Suspendable() = default;
private:
class Server : public fidl::Server<fuchsia_power_system::SuspendBlocker> {
public:
explicit Server(Suspendable<Driver>* parent) : parent_(parent) {}
private:
void BeforeSuspend(BeforeSuspendCompleter::Sync& completer) override {
parent_->Suspend(
SuspendCompleter([completer = completer.ToAsync()]() mutable { completer.Reply(); }));
}
void AfterResume(AfterResumeCompleter::Sync& completer) override {
parent_->Resume(
ResumeCompleter([completer = completer.ToAsync()]() mutable { completer.Reply(); }));
}
void handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_power_system::SuspendBlocker> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override {}
Suspendable<Driver>* parent_;
};
zx::result<> RegisterSuspendHooks(async_dispatcher_t* dispatcher, fdf::Namespace& incoming,
std::string_view name) {
if (!SuspendEnabled()) {
return zx::ok();
}
zx::result server_end = internal::RegisterSuspendHooks(incoming, name);
if (server_end.is_error()) {
return server_end.take_error();
}
binding_.emplace(dispatcher, std::move(server_end.value()), &server_,
fidl::kIgnoreBindingClosure);
return zx::ok();
}
Server server_;
std::optional<fidl::ServerBinding<fuchsia_power_system::SuspendBlocker>> binding_;
};
} // namespace fdf_power
#endif