编写设备驱动程序

第 12 章 电源管理

电源管理提供控制和管理计算机系统或设备的电源使用情况的功能。使用电源管理,可使系统在空闲时消耗较少的电量,在未使用时完全关闭电源,从而节省能源。例如,桌面计算机系统耗电量很大,但经常处于空闲状态,尤其是在夜间。电源管理软件可以检测到系统未被使用的情况。因此,电源管理可以关闭系统或其中某些组件的电源。

本章介绍有关以下主题的信息:

电源管理框架

Solaris 电源管理框架依靠设备驱动程序来实现特定于设备的电源管理功能。该框架分两部分实现:

设备电源管理

该框架使设备可在经过指定的空闲时间间隔后降低能耗。在电源管理过程中,系统软件会检查空闲设备。电源管理框架会导出接口,通过这些接口,可以在系统软件与设备驱动程序之间进行通信。

Solaris 电源管理框架提供了下列设备电源管理功能:

系统电源管理

系统电源管理可在关闭系统电源之前保存系统状态。因此,系统可以在重新打开时立即返回到相同状态。

要关闭整个系统并返回到关闭前的状态,请执行以下步骤:


仅适用于 SPARC –

当前,仅在 Solaris OS 支持的某些 SPARC 系统上实现了系统电源管理。有关更多信息,请参见 power.conf(4) 手册页。


Solaris OS 中的系统电源管理框架提供了下列系统电源管理功能:

设备电源管理模型

以下各节详细介绍设备电源管理模型。该模型包括以下元素:

电源管理组件

如果在设备处于空闲状态时可以减少设备能耗,则该设备是电源可管理设备。从概念上讲,电源可管理设备由许多电源可管理硬件单元组成,这些硬件单元称为组件

设备驱动程序通知系统有关设备组件及其相关电源级别的信息。因此,在驱动程序初始化期间,驱动程序会在其 attach(9E) 入口点中创建 pm-components(9P) 属性。

大多数电源可管理设备仅实现单个组件。例如,磁盘就是一个电源可管理的单组件设备,当磁盘处于空闲状态时,可以停止磁盘主轴马达以节省电能。

如果一个设备具有多个可单独控制的电源可管理单元,则该设备应实现多个组件。

例如,配有监视器的帧缓存器卡就是一个电源可管理的双组件设备。帧缓存器电子设备是第一个组件 [组件 0]。未使用帧缓存器电子设备时,其能耗将会降低。监视器是第二个组件 [组件 1]。未使用监视器时,监视器也可以进入低能耗模式。系统将帧缓存器电子设备和监视器视为一个由两个组件组成的设备。

多个电源管理组件

对于电源管理框架而言,所有组件均“一视同仁”,并且组件之间完全无关。如果组件状态不完全兼容,则设备驱动程序必须确保不会出现不需要的状态组合。例如,帧缓存器/监视器卡具有以下几种可能状态: D0D1D2D3。与卡连接的监视器具有以下几种可能状态: OnStandbySuspendOff。这些状态并不一定相互兼容。例如,如果监视器处于 On 状态,则帧缓存器必须处于 D0 状态(即完全打开)。如果在帧缓存器处于 D3 状态时,其驱动程序收到一个请求,要求打开监视器电源使监视器处于 On 状态,则在将监视器设置为 On 之前,驱动程序必须调用 pm_raise_power(9F) 才能启动帧缓存器。如果系统在监视器处于 On 状态时请求降低帧缓存器的电能供给,则驱动程序必须拒绝该请求。

电源管理状态

每个设备组件都可处于以下两种状态之一:繁忙空闲。设备驱动程序通过调用 pm_busy_component(9F)pm_idle_component(9F) 通知框架设备状态的更改。最初创建组件时,组件被视为空闲状态。

电源级别

通过设备导出的 pm-components 属性,设备电源管理框架可了解设备支持的电源级别。电源级别值必须是正整数。对电源级别的解释由设备驱动程序编写者确定。在 pm-components 属性中必须按单一递增顺序列出电源级别。该框架将 0 电源级别解释为关闭。如果框架由于相关性必须打开设备电源,则它会将每个组件都设置为其最高电源级别。

以下示例给出了某驱动程序的 .conf 文件中的 pm-components 项,该驱动程序实现了一个电源管理组件(即磁盘主轴马达)。磁盘轴马达是组件 0。该轴马达支持两个电源级别。这两个级别表示“停止”和“全速旋转”。


示例 12–1 pm-component 项样例

pm-components="NAME=Spindle Motor", "0=Stopped", "1=Full Speed";

以下示例说明如何在驱动程序的 attach() 例程中实现示例 12–1


示例 12–2 使用 pm-components 属性的 attach(9E) 例程

static char *pmcomps[] = {
    "NAME=Spindle Motor",
    "0=Stopped",
    "1=Full Speed"
};
/* ... */
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
    /* ... */
    if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
        "pm-components", &pmcomp[0],
        sizeof (pmcomps) / sizeof (char *)) != DDI_PROP_SUCCESS)
        goto failed;
    /* ... */

以下示例给出了实现两个组件的帧缓存器。组件 0 是支持四个不同电源级别的帧缓存器电子设备。组件 1 表示所连接的监视器的电源管理状态。


示例 12–3 多组件 pm-components

pm-components="NAME=Frame Buffer", "0=Off", "1=Suspend", \
    "2=Standby", "3=On",
    "NAME=Monitor", "0=Off", "1=Suspend", "2=Standby", "3=On";

首次连接设备驱动程序时,框架并不了解设备的电源级别。在以下情况下,会进行电源转换:

进行电源转换后,框架将开始跟踪每个设备组件的电源级别。如果驱动程序已通知框架电源级别,则也会进行跟踪。驱动程序通过调用 pm_power_has_changed(9F) 通知框架电源级别的更改。

系统将计算每个可能的电源转换的缺省阈值。这些阈值基于系统空闲阈值。可以使用 pmconfigpower.conf(4) 覆盖缺省阈值。当组件电源级别未知时,将使用基于系统空闲阈值的其他缺省阈值。

电源管理相关性

某些设备的电源应仅在关闭其他设备的电源时关闭。例如,如果允许关闭 CD-ROM 驱动器的电源,则可能会丢失一些必需功能,如弹出 CD 的功能。

为了防止设备独立关闭电源,可以使该设备依赖于电源可能保持打开的其他设备。通常,设备依赖于帧缓存器,因为在用户使用系统时监视器通常处于打开状态。

power.conf(4) 文件指定设备之间的相关性。(设备树中的父节点隐式依赖于其子节点。电源管理框架会自动处理此相关性。)可以使用以下格式的 power.conf(4) 项指定特定的相关性:

device-dependency dependent-phys-path phys-path

其中,dependent-phys-path 是电源保持打开状态的设备,如 CD-ROM 驱动器。phys-path 表示要依赖于其电源状态的设备,如帧缓存器。

power.conf 中为插入系统的每个新设备添加一个项将非常麻烦。可以使用以下语法,以一种更通用的方式指明相关性:

device-dependency-property property phys-path

这种项要求任何导出属性 property 的设备都必须依赖于 phys-path 指定的设备。由于此相关性尤其适用于可移除介质设备,因此缺省情况下 /etc/power.conf 包含以下行:

device_dependent-property  removable-media  /dev/fb

使用此语法,除非关闭控制台帧缓存器的电源,否则无法关闭导出 removable-media 属性的设备的电源。

有关更多信息,请参见 power.conf(4)removable-media(9P) 手册页。

设备的自动电源管理

如果 pmconfigpower.conf(4) 启用了自动电源管理,则具有 pm-components(9P) 属性的所有设备都将自动使用电源管理。当组件空闲一段缺省时间后,组件将自动降低到下一个最低电源级别。缺省时间段由电源管理框架计算,用于在系统空闲阈值内将整个设备设置为其最低能耗状态。


注 –

缺省情况下,1999 年 7 月 1 日后首次发布的所有 SPARC 桌面系统上都启用了自动电源管理。对于所有其他系统,缺省情况下禁用此功能。要确定您的计算机上是否启用了自动电源管理,请参阅 power.conf(4) 手册页中的说明。


可以使用 power.conf(4) 覆盖框架计算的缺省值。

设备电源管理接口

支持包含电源可管理组件的设备的设备驱动程序必须创建 pm-components(9P) 属性。该属性向系统指明设备包含电源可管理组件。pm-components 还向系统指明可用的电源级别。通常,驱动程序通过从其 attach(9E) 入口点调用 ddi_prop_update_string_array(9F) 来通知系统。另一种通知系统的方法是使用 driver.conf(4) 文件。有关详细信息,请参见 pm-components(9P) 手册页。

繁忙-空闲状态转换

驱动程序必须始终使框架了解从空闲到繁忙或从繁忙到空闲的设备状态转换。进行这些转换的位置完全特定于设备。繁忙与空闲状态之间的转换取决于设备的性质以及特定组件具备的状态转换特性。例如,SCSI 磁盘目标驱动程序通常导出单个组件,该组件表示 SCSI 目标磁盘驱动器是启动状态还是停止状态。当驱动器有未解决的请求时,该组件将标记为繁忙。完成最后一个排队请求后,该组件将标记为空闲。某些组件创建后从未标记为繁忙。例如,pm-components(9P) 创建的组件就一直处于空闲状态。

pm_busy_component(9F)pm_idle_component(9F) 接口将通知电源管理框架繁忙/空闲状态转换。pm_busy_component(9F) 调用的语法如下所示:

int pm_busy_component(dev_info_t *dip, int component);

pm_busy_component(9F)component 标记为繁忙。当组件为繁忙状态时,不应关闭该组件的电源。如果已关闭组件电源,则将该组件标记为繁忙不会更改电源级别。为此,驱动程序需要调用 pm_raise_power(9F)。对 pm_busy_component(9F) 的调用具有累积性,因此要使组件处于空闲状态,需要调用相应次数的 pm_idle_component(9F)。

pm_idle_component(9F) 例程的语法如下所示:

int pm_idle_component(dev_info_t *dip, int component);

pm_idle_component(9F)component 标记为空闲。可以关闭空闲组件的电源。要使组件处于空闲状态,必须针对 pm_busy_component(9F) 的每次调用调用 pm_idle_component(9F) 一次。

设备能耗状态转换

设备驱动程序可以调用 pm_raise_power(9F) 来请求将组件至少设置为给定的电源级别。使用已关闭电源的组件之前,必须采用这种方式设置电源级别。例如,如果已关闭磁盘电源,则 SCSI 磁盘目标驱动程序的 read(9E) 例程可能需要启动磁盘。pm_raise_power(9F) 函数请求电源管理框架将设备能耗状态转换为较高的电源级别。通常,由框架来降低组件的电源级别。但是,设备驱动程序在分离时应调用 pm_lower_power(9F),以便尽可能地降低未使用设备的能耗。

对于某些设备来说,关闭电源可能会产生风险。例如,某些磁带机在关闭电源时会损坏磁带。同样,某些磁盘驱动器在开关电源过程中的容错能力有限,因为每次开关电源都会导致磁头停放。应使用 no-involuntary-power-cycles(9P) 属性通知系统,设备驱动程序应控制设备的所有关开机循环。此方法可防止在分离设备驱动程序时切断设备电源,除非驱动程序已从其 detach(9E) 入口点调用 pm_lower_power(9F) 关闭设备电源。

驱动程序发现某个操作所需组件的电源级别不够高时,会调用 pm_raise_power(9F) 函数。该接口会使驱动程序将组件的当前电源级别提高到所需级别。该调用还会将依赖于该设备的所有设备恢复到全功率。

如果在不再需要访问某个设备后分离该设备,则将调用 pm_lower_power(9F)。调用 pm_lower_power(9F) 可将每个组件设置为最低电源级别,从而使设备在未使用时尽可能少地消耗电量。必须从 detach() 入口点调用 pm_lower_power() 函数。如果从驱动程序的任何其他部分调用 pm_lower_power() 函数,该函数不会起作用。

调用 pm_power_has_changed(9F) 函数可通知框架有关电源转换的情况。转换可能是由于设备更改了自己的电源级别而导致。转换也可能是由于暂停/恢复等操作而导致。pm_power_has_changed(9F) 的语法与 pm_raise_power(9F) 的语法相同。

power() 入口点

电源管理框架使用 power(9E) 入口点。

power() 使用以下语法:

int power(dev_info_t *dip, int component, int level);

需要更改组件的电源级别时,系统将调用 power(9E) 入口点。该入口点执行的操作特定于设备驱动程序。在上面提到的 SCSI 目标磁盘驱动程序示例中,如果将电源级别设置为 0,则会发送 SCSI 命令停止磁盘运转,而如果将电源级别设置为全电源级别,则会发送 SCSI 命令启动磁盘。

如果电源转换导致设备丢失状态,则驱动程序必须将任何必需的状态保存在内存中,以便将来恢复。如果电源转换要求先恢复保存的状态,然后才能再次使用设备,则驱动程序必须恢复该状态。该框架并未对哪些电源事务会导致丢失自动电源管理设备的状态作出假设,也未对哪些电源事务会要求恢复自动电源管理设备的状态作出假设。以下示例给出了一个 power() 例程样例。


示例 12–4 将 power() 例程用于单组件设备

int
xxpower(dev_info_t *dip, int component, int level)
{
    struct xxstate *xsp;
    int instance;

    instance = ddi_get_instance(dip);
    xsp = ddi_get_soft_state(statep, instance);
   /*
    * Make sure the request is valid
    */
    if (!xx_valid_power_level(component, level))
        return (DDI_FAILURE);
    mutex_enter(&xsp->mu);
   /*
    * If the device is busy, don't lower its power level
    */
    if (xsp->xx_busy[component] &&
        xsp->xx_power_level[component] > level) {
        mutex_exit(&xsp->mu);
        return (DDI_FAILURE);
    }

    if (xsp->xx_power_level[component] != level) {
       /*
        * device- and component-specific setting of power level
        * goes here
        */
        xsp->xx_power_level[component] = level;
    }
    mutex_exit(&xsp->mu);
    return (DDI_SUCCESS);
}

以下示例是包含两个组件的设备的 power() 例程,其中,组件 1 为打开状态时,组件 0 必须也为打开状态。


示例 12–5 多组件设备的 power (9E) 例程

int
xxpower(dev_info_t *dip, int component, int level)
{
    struct xxstate *xsp;
    int instance;

    instance = ddi_get_instance(dip);
    xsp = ddi_get_soft_state(statep, instance);
   /*
    * Make sure the request is valid
    */
    if (!xx_valid_power_level(component, level))
        return (DDI_FAILURE);
    mutex_enter(&xsp->mu);
   /*
    * If the device is busy, don't lower its power level
    */
    if (xsp->xx_busy[component] &&
        xsp->xx_power_level[component] > level) {
        mutex_exit(&xsp->mu);
        return (DDI_FAILURE);
    }
   /*
    * This code implements inter-component dependencies:
    * If we are bringing up component 1 and component 0 
    * is off, we must bring component 0 up first, and if
    * we are asked to shut down component 0 while component
    * 1 is up we must refuse
    */
    if (component == 1 && level > 0 && xsp->xx_power_level[0] == 0) {
        xsp->xx_busy[0]++;
        if (pm_busy_component(dip, 0) != DDI_SUCCESS) {
           /*
            * This can only happen if the args to 
            * pm_busy_component()
            * are wrong, or pm-components property was not
            * exported by the driver.
            */
            xsp->xx_busy[0]--;
            mutex_exit(&xsp->mu);
            cmn_err(CE_WARN, "xxpower pm_busy_component() 
                failed");
            return (DDI_FAILURE);
        }
        mutex_exit(&xsp->mu);
        if (pm_raise_power(dip, 0, XX_FULL_POWER_0) != DDI_SUCCESS)
            return (DDI_FAILURE);
        mutex_enter(&xsp->mu);
    }
    if (component == 0 && level == 0 && xsp->xx_power_level[1] != 0) {
        mutex_exit(&xsp->mu);
        return (DDI_FAILURE);
    }
    if (xsp->xx_power_level[component] != level) {
       /*
        * device- and component-specific setting of power level
        * goes here
        */
        xsp->xx_power_level[component] = level;
    }
    mutex_exit(&xsp->mu);
    return (DDI_SUCCESS);
}

系统电源管理模型

本节详细介绍系统电源管理模型。该模型包括以下组件:

自动关闭阈值

经过一段可配置空闲时间后,系统可以自动关闭(即关闭电源)。该时间段称为自动关闭阈值。缺省情况下,将对在 1995 年 10 月 1 日到 1999 年 7 月 1 日间首次发布的 SPARC 桌面系统启用此行为。有关更多信息,请参见 power.conf(4) 手册页。可以使用 dtpower(1M) 或 power.conf(4) 覆盖自动关闭。

繁忙状态

可以采用几种方法度量系统的繁忙状态。当前支持的内置度量标准项包括键盘字符、鼠标活动、tty 字符、平均负荷值、磁盘读取和 NFS 请求。其中任何一项都可使系统处于繁忙状态。除内置度量标准外,还定义了一个接口,用于运行用户指定的可以表明系统处于繁忙状态的进程。

硬件状态

导出 reg 属性的设备被视为具有硬件状态,关闭系统之前,必须保存该硬件状态。没有 reg 属性的设备被视为无状态设备。但是,设备驱动程序可以另外一种方式处理这种情况。

如果驱动程序导出值为 needs-suspend-resumepm-hardware-state 属性,则必须调用具有硬件状态但没有 reg 属性的设备(如 SCSI 驱动程序),才能保存并恢复状态。否则,缺少 reg 属性即表示设备没有硬件状态。有关设备属性的信息,请参见第 4 章

具有 reg 属性但没有硬件状态的设备可以导出值为 no-suspend-resumepm-hardware-state 属性。将 no-suspend-resumepm-hardware-state 属性配合使用,可防止框架调用驱动程序来保存并恢复该状态。有关电源管理属性的更多信息,请参见 pm-components(9P) 手册页。

系统的自动电源管理

如果符合以下条件,系统将会关闭:

系统电源管理使用的入口点

系统电源管理将命令DDI_SUSPEND 传递给 detach(9E) 驱动程序入口点,以请求驱动程序保存设备硬件状态。系统电源管理将命令 DDI_RESUME 传递给 attach(9E) 驱动程序入口点,以请求驱动程序恢复设备硬件状态。

detach() 入口点

detach(9E) 的语法如下所示:

int detach(dev_info_t *dip, ddi_detach_cmd_t cmd);

具有 reg 属性或 pm-hardware-state 属性设置为 needs-suspend-resume 的设备必须能够保存设备的硬件状态。框架调用驱动程序的 detach(9E) 入口点使驱动程序保存状态,以便在系统电源重新打开后进行恢复。要处理 DDI_SUSPEND 命令,detach(9E) 必须执行以下任务:

如果驱动程序无法暂停设备并将其状态保存到内存,则驱动程序必须返回 DDI_FAILURE。然后,框架将异常中止系统电源管理操作。

在某些情况下,关闭设备电源存在一定风险。例如,如果关闭内含磁带的磁带机电源,则该磁带可能会损坏。在这种情况下,attach(9E) 应执行以下操作:

如果上述两种操作的结果都是肯定的,则应拒绝 DDI_SUSPEND 请求。示例 12–6 给出了使用 ddi_removing_power(9F) 检查 DDI_SUSPEND 命令是否会产生问题的 attach(9E) 例程。

必须接受转储请求。框架使用 dump(9E) 入口点写出包含内存内容的状态文件。有关使用该入口点时对设备驱动程序强加的限制,请参见 dump(9E) 手册页。

使用 DDI_SUSPEND 命令调用电源可管理组件的 detach(9E) 入口点时,应保存关闭设备电源时的状态。驱动程序应取消待处理的超时。驱动程序还应禁止对 pm_raise_power(9F) 的任何调用,但 dump(9E) 请求除外。通过使用 DDI_RESUME 命令调用 attach(9E) 来恢复设备时,可以恢复超时以及对 pm_raise_power () 的调用。驱动程序必须掌握其足够的状态信息,才能够正确处理这种可能发生的情况。以下示例给出了实现 DDI_SUSPEND 命令的 detach(9E) 例程。


示例 12–6 实现 DDI_SUSPEND 的 detach(9E) 例程

int
xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
    struct xxstate *xsp;
    int instance;

    instance = ddi_get_instance(dip);
    xsp = ddi_get_soft_state(statep, instance);

    switch (cmd) {
    case DDI_DETACH:
       /* ... */
    case DDI_SUSPEND:
       /*
        * We do not allow DDI_SUSPEND if power will be removed and
        * we have a device that damages tape when power is removed
        * We do support DDI_SUSPEND for Device Reconfiguration.
        */
        if (ddi_removing_power(dip) && xxdamages_tape(dip))
            return (DDI_FAILURE);
        mutex_enter(&xsp->mu);
        xsp->xx_suspended = 1;  /* stop new operations */
       /*
        * Sleep waiting for all the commands to be completed
        *
        * If a callback is outstanding which cannot be cancelled
        * then either wait for the callback to complete or fail the
        * suspend request
        *
        * This section is only needed if the driver maintains a
        * running timeout
        */
        if (xsp->xx_timeout_id) {
            timeout_id_t temp_timeout_id = xsp->xx_timeout_id;

            xsp->xx_timeout_id = 0;
            mutex_exit(&xsp->mu);
            untimeout(temp_timeout_id);
            mutex_enter(&xsp->mu);
        }
        if (!xsp->xx_state_saved) {
           /*
            * Save device register contents into
            * xsp->xx_device_state
            */
        }
        mutex_exit(&xsp->mu);
        return (DDI_SUCCESS);
    default:
        return (DDI_FAILURE);
}

attach() 入口点

attach(9E) 的语法如下所示:

int attach(dev_info_t *dip, ddi_attach_cmd_t cmd);

恢复系统电源后,每个具有 reg 属性或具有值为 needs-suspend-resumepm-hardware-state 属性的设备都会使用 DDI_RESUME 命令值调用其 attach(9E) 入口点。如果系统关闭异常中止,则即使尚未关闭电源,也会调用每个暂停的驱动程序以进行恢复。因此,attach(9E) 中的恢复代码不能对系统是否已实际断电作出任何假设。

电源管理框架认为组件的电源级别在执行 DDI_RESUME 时未知。根据设备性质,驱动程序编写者有两种选择:

以下示例给出了使用 DDI_RESUME 命令的 attach(9E) 例程。


示例 12–7 实现 DDI_RESUME 的 attach(9E) 例程

int
xxattach(devinfo_t *dip, ddi_attach_cmd_t cmd)
{
    struct xxstate *xsp;
    int    instance;

    instance = ddi_get_instance(dip);
    xsp = ddi_get_soft_state(statep, instance);

    switch (cmd) {
    case DDI_ATTACH:
    /* ... */
    case DDI_RESUME:
        mutex_enter(&xsp->mu);
        if (xsp->xx_pm_state_saved) {
            /*
             * Restore device register contents from
             * xsp->xx_device_state
             */
        }
       /*
        * This section is optional and only needed if the
        * driver maintains a running timeout
        */
        xsp->xx_timeout_id = timeout( /* ... */ );

        xsp->xx_suspended = 0;        /* allow new operations */
        cv_broadcast(&xsp->xx_suspend_cv);
       /* If it is possible to determine in a device-specific 
        * way what the power levels of components are without 
        * powering the components up,
        * then the following code is recommended
        */
        for (i = 0; i < num_components; i++) {
            xsp->xx_power_level[i] = xx_get_power_level(dip, i);
            if (xsp->xx_power_level[i] != XX_LEVEL_UNKNOWN)
                (void) pm_power_has_changed(dip, i, 
                    xsp->xx_power_level[i]);
        }
        mutex_exit(&xsp->mu);
        return(DDI_SUCCESS);
    default:
        return(DDI_FAILURE);
    }
}


注 –

detach(9E)attach(9E) 接口也可用于恢复处于静止状态的系统。


电源管理设备访问示例

如果支持电源管理,并且按示例 12–6示例 12–7 中的方式使用 detach(9E)attach(9E),则可以从用户上下文(例如,read(2)write(2)ioctl(2))访问该设备。

以下示例演示了该方法。该示例假定要执行的操作需要以电源级别 level 运行的组件 component


示例 12–8 设备访问

mutex_enter(&xsp->mu);
/*
 * Block command while device is suspended by DDI_SUSPEND
 */
while (xsp->xx_suspended)
    cv_wait(&xsp->xx_suspend_cv, &xsp->mu);
/*
 * Mark component busy so xx_power() will reject attempt to lower power
 */
xsp->xx_busy[component]++;
if (pm_busy_component(dip, component) != DDI_SUCCESS) {
    xsp->xx_busy[component]--;
   /*
    * Log error and abort
    */
}
if (xsp->xx_power_level[component] < level) {
    mutex_exit(&xsp->mu);
    if (pm_raise_power(dip, component, level) != DDI_SUCCESS) {
       /*
        * Log error and abort
        */
    }
    mutex_enter(&xsp->mu);
}

当设备操作(例如,设备的中断处理程序执行的操作)完成时,可以使用以下示例中的代码段。


示例 12–9 设备操作完成

/*
 * For each command completion, decrement the busy count and unstack
 * the pm_busy_component() call by calling pm_idle_component(). This
 * will allow device power to be lowered when all commands complete
 * (all pm_busy_component() counts are unstacked)
 */
xsp->xx_busy[component]--;
if (pm_idle_component(dip, component) != DDI_SUCCESS) {
    xsp->xx_busy[component]++;
   /*
    * Log error and abort
    */
}
/*
 * If no more outstanding commands, wake up anyone (like DDI_SUSPEND)
 * waiting for all commands to  be completed
 */

电源管理控制流程

图 12–1 说明了电源管理框架中的控制流程。

完成组件活动后,驱动程序可以调用 pm_idle_component(9F) 将该组件标记为空闲。如果组件在其阈值时间内一直处于空闲状态,则框架可以将该组件的能耗降低到下一个较低级别。框架调用 power(9E) 函数将组件的能耗设置为支持的下一个较低电源级别(如果存在较低级别)。当组件处于繁忙状态时,驱动程序的 power(9E) 函数应拒绝任何降低该组件电源级别的尝试。在转换到较低级别之前 power(9E) 函数应保存可能在转换过程中丢失的任何状态。

需要较高级别的组件时,驱动程序将调用 pm_busy_component(9F)。此调用将阻止框架进一步降低能耗,然后针对组件调用 pm_raise_power(9F)。在对 pm_raise_power(9F) 的调用返回之前,框架接着调用 power(9E) 以提高组件的能耗。驱动程序的 power(9E) 代码必须恢复在较低级别中丢失、但在较高级别中需要的任何状态。

分离某个驱动程序时,该驱动程序应针对每个组件调用 pm_lower_power(9F),以将其能耗降低到最低级别。在对 pm_lower_power(9F) 的调用返回之前,框架可以随后调用驱动程序的 power(9E) 例程以降低组件的能耗。

图 12–1 电源管理概念状态图

图中显示了电源管理例程中的控制流。

电源管理接口的更改

在 Solaris 8 发行版之前,设备的电源管理不是自动的。开发者必须为要管理其电源的每个设备在 /etc/power.conf 中添加一项。框架假定所有设备都只支持两个电源级别: 0 和标准能耗。

电源假定所有其他组件对组件 0 具有隐式相关性。当组件 0 更改为级别 0 时,使用 DDI_PM_SUSPEND 命令调用驱动程序的 detach(9E) 来保存硬件状态。当组件 0 的级别 0 发生更改时,使用命令 DDI_PM_RESUME 调用 attach(9E) 例程来恢复硬件状态。

以下接口和命令已过时,现在仍支持它们是为了适于采用二进制的场合:

从 Solaris 8 发行版开始,如果启用了 autopm,则导出 pm-components 属性的设备将自动使用电源管理。

现在,框架可以通过 pm-components 属性了解每个设备支持的电源级别。

框架对设备不同组件之间的相关性不会作出任何假设。更改电源级别时,设备驱动程序负责根据需要保存并恢复硬件状态。

通过这些更改,电源管理框架可以处理新兴的设备技术。现在,电源管理可以节省更多的电。框架可以自动检测哪些设备能够省电。框架可以使用设备的中间能耗状态。现在,系统可以实现节能目标,而无需关闭整个系统的电源,也无需使用任何功能。

表 12–1 电源管理接口

已删除的接口 

等效接口 

pm_create_components(9F)

pm-components(9P)

pm_set_normal_power(9F)

pm-components(9P)

pm_destroy_components(9F)

无 

pm_get_normal_power(9F)

无 

ddi_dev_is_needed(9F)

pm_raise_power(9F)

无 

pm_lower_power(9F)

无 

pm_power_has_changed(9F)

DDI_PM_SUSPEND 

无 

DDI_PM_RESUME 

无