| // 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 |