设备电源管理

在 Fuchsia 中,驱动程序通过与驱动程序框架和 Fuchsia 电源框架(包括 Power Broker 和系统活动调节器)集成来参与电源管理。

系统挂起和恢复

如果平台已启用系统挂起,驱动程序管理器将为绑定到驱动程序的每个节点创建一个二进制电源元素,其中电源级别 0 表示关闭/挂起,电源级别 1 表示开启/活跃。电源元素的依赖项将由绑定到驱动程序的节点祖先节点的电源元素以及其他一些电源元素(例如 CPU 电源元素、存储电源元素等)组成。这可确保节点的挂起/恢复顺序正确,以便在祖先节点挂起时,绑定驱动程序不会尝试使用其祖先节点提供的服务。

处理暂停和恢复

驱动程序可以通过几种不同的方式处理挂起和恢复,如下所述。

运行时管理的调度程序

建议驱动程序通过启用运行时管理的调度程序来支持暂停和恢复。在此状态下,驱动程序主机(而非驱动程序实例)会运行与驱动程序的电源元素关联的 fuchsia.power.broker/ElementRunner,并与驱动程序运行时协调电源转换:

  1. 挂起:当驱动程序的电源元素转换为挂起状态 (0) 时,驱动程序运行时将:
    • 暂停所有受电源管理的分发器。
    • 将已加入队列的任务和新到达的任务移至休眠队列。
    • 等待正在执行的回调完成。
    • 在驱动程序的默认调度程序的常年运行视图上调用驱动程序的挂起钩子。
  2. 恢复:当驱动程序的电源元素转换为活动状态 (1) 或注册的唤醒向量触发时,运行时将:
    • 首先在常年运行调度程序上调用驱动程序的恢复钩子,以便恢复硬件状态。
    • 取消暂停受电源管理的分发器(先执行所有触发的唤醒向量任务,然后执行已加入队列的任务)。
  3. 在暂停时停止:如果驱动程序框架在驱动程序暂停时将其停止,则运行时将:
    • 恢复司机(SystemResume 或 system_resume)。
    • 调用驱动程序的停止钩子(Stop 或 stop)。

启用运行时管理的调度程序

您可以在驱动程序的 .cml 文件中设置 power_managed_dispatchers_enabled: "true",以启用运行时管理的调度程序。添加 driver_component/power_managed_dispatchers.shard.cml 也会在路由所需的电源协议时为您执行此操作:

include: [
    // Enables runtime-managed dispatchers.
    "driver_component/power_managed_dispatchers.shard.cml",
],
program: {
    runner: "driver",
    binary: "driver/my_driver.so",
    bind: "meta/bind/my_driver.bindbc",
},

驱动程序可以实现挂起和恢复生命周期方法,以便在电源管理调度程序暂停后将硬件置于低功耗状态,并在电源管理调度程序在唤醒后解除暂停之前恢复硬件状态。

当唤醒由驱动程序自身注册的某个运行时唤醒向量(例如绑定到电源管理调度器的中断或通道等待)触发时,驱动程序宿主会自动通过 fuchsia.power.broker/Topology.Lease 获取驱动程序电源元素的租约,并将 LeaseToken(C++ 中的 pe_lease 或 Rust 中的 lease)传递给恢复钩子。如果驱动程序作为另一个驱动程序的依赖项恢复,或者在系统级恢复期间恢复,则租用参数为 std::nullopt(或 None)。

C++

在 C++ 中,替换 fdf::DriverBase2 上的 SystemSuspend 和 SystemResume:

#include <lib/driver/component/cpp/driver_base2.h>

class MyDriver : public fdf::DriverBase2 {
 public:
  MyDriver() : fdf::DriverBase2("my_driver") {}

  void SystemSuspend(fdf::SuspendCompleter completer) override {
    // Put hardware into low-power mode or save register state.
    completer(zx::ok());
  }

  void SystemResume(std::optional<fuchsia_power_broker::LeaseToken> pe_lease,
                    fdf::ResumeCompleter completer) override {
    // Restore hardware configuration. If woken by a wake vector, retain
    // `pe_lease` until the wake task finishes handling the event.
    completer(zx::ok());
  }
};

Rust

在 Rust 中,在 fdf_component::Driver 特征上实现 system_suspend 和 system_resume:

impl fdf_component::Driver for MyDriver {
    async fn system_suspend(&self) -> Result<(), fdf_component::DriverError> {
        // Put hardware into low-power mode or save register state.
        Ok(())
    }

    async fn system_resume(
        &self,
        lease: Option<zx::EventPair>,
    ) -> Result<(), fdf_component::DriverError> {
        // Restore hardware configuration. If woken by a wake vector, retain
        // `lease` until the wake task finishes handling the event.
        Ok(())
    }
}

驱动程序拥有的暂停

此方法适用于希望监听挂起和恢复但不希望其调度程序在挂起期间暂停的驱动程序。

启用驱动程序拥有的挂起

在驱动程序的 .cml 文件中设置 suspend_enabled: "true"。

如果您将 suspend_enabled 设置为 "true",请确保将 power_managed_dispatchers_enabled 设置为 "false"(或省略),否则 power_managed_dispatchers_enabled 具有优先权,并且挂起事件永远不会转发到 fdf_power::Suspendable (SuspendableDriver)。

program: {
    runner: "driver",
    binary: "driver/my_driver.so",
    bind: "meta/bind/my_driver.bindbc",
    suspend_enabled: "true",
},

C++

在 C++ 中,从 fdf_power::Suspendable 继承,提供 take_power_element_runner(),并在 Start 期间调用 InitializeSuspend():

#include <lib/driver/component/cpp/driver_base2.h>
#include <lib/driver/power/cpp/suspend.h>

class MyDriver : public fdf::DriverBase2,
                 public fdf_power::Suspendable<MyDriver> {
 public:
  MyDriver() : fdf::DriverBase2("my_driver") {}

  zx::result<> Start(fdf::DriverContext context) override {
    power_element_runner_ = context.take_power_element_runner();
    if (zx::result result =
            InitializeSuspend(dispatcher(), context.incoming(), name());
        result.is_error()) {
      return result.take_error();
    }
    return zx::ok();
  }

  void Suspend(fdf_power::SuspendCompleter completer) override {
    // Drain internal queues and put hardware into a low-power state.
    completer();
  }

  void Resume(fdf_power::ResumeCompleter completer) override {
    // Restore hardware state and resume internal queues.
    completer();
  }

  bool SuspendEnabled() override { return true; }

  std::optional<fidl::ServerEnd<fuchsia_power_broker::ElementRunner>>
  take_power_element_runner() {
    return std::move(power_element_runner_);
  }

 private:
  std::optional<fidl::ServerEnd<fuchsia_power_broker::ElementRunner>>
      power_element_runner_;
};

Rust

在 Rust 中,实现 fdf_power::SuspendableDriver 并注册封装在 fdf_power::Suspendable 中的驱动程序:

use fdf_component::{Driver, DriverContext, DriverError, driver_register};
use fdf_power::{Suspendable, SuspendableDriver};

impl SuspendableDriver for MyDriver {
    async fn suspend(&self) {
        // Drain internal queues and put hardware into a low-power state.
    }

    async fn resume(&self) {
        // Restore hardware state and resume internal queues.
    }

    fn suspend_enabled(&self) -> bool {
        true
    }
}

driver_register!(Suspendable<MyDriver>);

自定义电源元素

除了由驱动程序管理器创建的电源元素外,驱动程序还可以通过 fuchsia.power.broker/Topology(或 //sdk/lib/driver/power/cpp 中的辅助库)手动创建和管理自己的自定义电源元素,这些元素具有各种电源级别和依赖项。自定义电源元素可以与 power_managed_dispatchers_enabled 或 suspend_enabled 一起使用,但自定义电源元素上的级别转换不会暂停驱动程序的调度程序。

电源租约和唤醒租约

  • Power Broker 租用:当已注册的唤醒向量通过运行时管理的调度程序唤醒挂起的驱动程序时,驱动程序主机通过 fuchsia.power.broker/Topology.Lease 获取驱动程序电源元素的租用,并将其传递给 SystemResume (system_resume)。由于驱动程序的电源元素依赖于其祖先驱动程序和 CPU 电源元素,因此保留此租用可使设备和 CPU 保持活跃状态,直到唤醒事件得到处理或移交给上游。
  • 系统唤醒租约:如果驱动程序需要直接阻止系统在运行时管理的唤醒向量之外挂起,可以从系统活动调节器(fuchsia.power.system/ActivityGovernor.AcquireWakeLease,由 fdf_power::WakeLeaseProvider 和 fdf_power::TimeoutWakeLease 等辅助程序封装)获取唤醒租约。

如需详细了解电源框架以及如何测试挂起/恢复,请参阅测试挂起和恢复。