设备电源管理模型
第12 章• 电源管理213
示例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";
首次连接设备驱动程序时,框架并不了解设备的电源级别。在以下情况下,会进行电源转
换:
设备电源管理模型
214 编写设备驱动程序• 2006 年11 月
驱动程序调用pm_raise_power(9F) 或pm_lower_power(9F)。
由于超出时间阈值,框架降低了组件的电源级别。
另一个设备更换了电源,而这两个设备之间存在相关性。请参见第215 页中的“电源管
理相关性”。
进行电源转换后,框架将开始跟踪每个设备组件的电源级别。如果驱动程序已通知框架电
源级别,则也会进行跟踪。驱动程序通过调用pm_power_has_changed(9F) 通知框架电源级别
的更改。
系统将计算每个可能的电源转换的缺省阈值。这些阈值基于系统空闲阈值。可以使用
pmconfig 或power.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) 手册页。
设备电源管理模型
第12 章• 电源管理215
设备的自动电源管理
如果pmconfig 或power.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 标记为繁忙。当组件为繁忙状态时,不应关闭该组件的
电源。如果已关闭组件电源,则将该组件标记为繁忙不会更改电源级别。为此,驱动程序
需要调用ddi_dev_is_needed(9F)。对pm_busy_component(9F) 的调用会渐增电源级别,并且
要使组件处于空闲状态,需要调用相应次数的pm_idle_component(9F)。
设备电源管理模型
216 编写设备驱动程序• 2006 年11 月
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) 一
次。
设备能耗状态转换
设备驱动程序可以调用ddi_dev_is_needed(9F) 来请求将组件至少设置为给定的电源级别。
使用已关闭电源的组件之前,必须采用这种方式设置电源级别。例如,如果已关闭磁盘电
源,则SCSI 磁盘目标驱动程序的read(9E) 例程可能需要启动磁盘。ddi_dev_is_needed(9F)
请求电源管理框架将设备能耗状态转换为较高的电源级别。通常,由框架来降低组件的电
源级别。但是,设备驱动程序在分离时应调用pm_lower_power(9F),以便尽可能地降低未使
用设备的能耗。
对于某些设备来说,关闭电源可能会产生风险。例如,某些磁带机在关闭电源时会损坏磁
带。同样,某些磁盘驱动器在开关电源过程中的容错能力有限,因为每次开关电源都会导
致磁头停放。应使用no-involuntary-power-cycles(9P) 属性通知系统,设备驱动程序应控
制设备的所有电源循环。此方法可防止在分离设备驱动程序时关闭设备电源,除非驱动程
序已调用pm_raise_power(9F) 关闭设备电源。
驱动程序发现某个操作所需组件的电源级别不够高时,会调用ddi_dev_is_needed(9F)。该
接口会使驱动程序将组件的当前电源级别提高到所需级别。该调用还会将依赖于该设备的
所有设备恢复到全功率。
如果在不再需要访问某个设备后分离该设备,则将调用pm_raise_power(9F)。调用
pm_lower_power() 可将每个组件设置为最低电源级别,从而使设备在未使用时尽可能少地
消耗电量。pm_raise_power(9F) 的语法与ddi_dev_is_needed(9F) 的语法相同。
调用pm_power_has_changed(9F) 可通知框架电源转换。转换可能是由于设备更改了自己的电
源级别而导致。转换也可能是由于暂停/恢复等操作而导致。pm_power_has_changed(9F) 的
语法与ddi_dev_is_needed(9F) 的语法相同。
power() 入口点
电源管理框架使用power(9E) 入口点。
power() 使用以下语法:
int power(dev_info_t *dip, int component, int level);
需要更改组件的电源级别时,系统将调用power(9E) 入口点。该入口点执行的操作特定于设
备驱动程序。在上面提到的SCSI 目标磁盘驱动程序示例中,如果将电源级别设置为0,则
会发送SCSI 命令停止磁盘运转,而如果将电源级别设置为全电源级别,则会发送SCSI 命令
启动磁盘。
设备电源管理模型
第12 章• 电源管理217
如果电源转换导致设备丢失状态,则驱动程序必须将任何必需的状态保存在内存中,以便
将来恢复。如果电源转换要求先恢复保存的状态,然后才能再次使用设备,则驱动程序必
须恢复该状态。该框架并未对哪些功率事务会导致丢失自动电源管理设备的状态做出假
设,也未对哪些功率事务会要求恢复自动电源管理设备的状态做出假设。以下示例给出了
一个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);
设备电源管理模型
218 编写设备驱动程序• 2006 年11 月
示例12–4 将power() 例程用于单组件设备(续)
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;
设备电源管理模型
第12 章• 电源管理219
示例12–5 多组件设备的power(9E) 例程(续)
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
设备电源管理模型
220 编写设备驱动程序• 2006 年11 月
示例12–5 多组件设备的power(9E) 例程(续)
*/
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);
设备电源管理模型
第12 章• 电源管理221
示例12–5 多组件设备的power(9E) 例程(续)
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) 覆盖自动关闭。
系统电源管理模型
222 编写设备驱动程序• 2006 年11 月
繁忙状态
可以采用几种方法度量系统的繁忙状态。当前支持的内置度量标准项包括键盘字符、鼠标
活动、tty 字符、平均负载、磁盘读取和NFS 请求。其中任何一项都可使系统处于繁忙状
态。除内置度量标准外,还定义了一个接口,用于运行用户指定的可以表明系统处于繁忙
状态的进程。
硬件状态
导出reg 属性的设备视为具有硬件状态,关闭系统之前,必须保存该硬件状态。没有reg 属
性的设备视为无状态设备。但是,设备驱动程序可以另外一种方式处理这种情况。
如果驱动程序导出值为needs-suspend-resume 的pm-hardware-state 属性,则必须调用具有
硬件状态但没有reg 属性的设备(如SCSI 驱动程序),才能保存并恢复状态。否则,缺少
reg 属性即表示设备没有硬件状态。有关设备属性的信息,请参见第4 章。
具有reg 属性但没有硬件状态的设备可以导出值为no-suspend-resume 的pm-hardware-state
属性。将no-suspend-resume 与pm-hardware-state 属性配合使用,可防止框架调用驱动程
序来保存并恢复该状态。有关电源管理属性的更多信息,请参见pm-components(9P) 手册
页。
系统的自动电源管理
如果符合以下条件,系统将会关闭:
dtpower(1M) 或power.conf(4) 启用了自动关闭。
系统空闲的时间长度已达到自动关闭阈值(分钟)。
满足power.conf 中指定的所有度量标准。
系统电源管理使用的入口点
系统电源管理还将命令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) 必须执行以下任务:
系统电源管理模型
第12 章• 电源管理223
在设备恢复之前,阻止启动进一步操作,但dump(9E) 请求除外。
一直等到未完成的操作完成为止。如果可以重新启动未完成的操作,则可以中止该操
作。
取消待处理的任何超时和回调。
将任何易失的硬件状态保存到内存。该状态包含设备寄存器的内容,此外还可以包含下
载的固件。
如果驱动程序无法暂停设备并将其状态保存到内存,则驱动程序必须返回DDI_FAILURE。然
后,框架将异常中止系统电源管理操作。
在某些情况下,关闭设备电源存在一定风险。例如,如果关闭内含磁带的磁带机电源,则
该磁带可能会损坏。在这种情况下,attach(9E) 应执行以下操作:
调用ddi_removing_power(9F) 以确定DDI_SUSPEND 命令是否会关闭设备的电源。
确定关闭电源是否会产生问题。
如果上述两种操作的结果都是肯定的,则应拒绝DDI_SUSPEND 请求。示例12–6 给出了使用
ddi_removing_power(9F) 检查DDI_SUSPEND 命令是否会产生问题的attach(9E) 例程。
必须接受转储请求。框架使用dump(9E) 入口点写出包含内存内容的状态文件。有关使用该
入口点时对设备驱动程序强加的限制,请参见dump(9E) 手册页。
对电源可管理组件调用包含DDI_SUSPEND 命令的detach(9E) 入口点时,应保存关闭设备电源
时的状态。驱动程序应取消待处理的超时。驱动程序还应禁止对ddi_dev_is_needed(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);
系统电源管理模型
224 编写设备驱动程序• 2006 年11 月
示例12–6 实现DDI_SUSPEND 的detach(9E) 例程(续)
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
*/
[...]
/*
系统电源管理模型
第12 章• 电源管理225
示例12–6 实现DDI_SUSPEND 的detach(9E) 例程(续)
* 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
系统电源管理模型
226 编写设备驱动程序• 2006 年11 月
示例12–6 实现DDI_SUSPEND 的detach(9E) 例程(续)
*/
[...]
}
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-resume 的
pm-hardware-state 属性的设备都会使用DDI_RESUME 命令值调用其attach(9E) 入口点。如果
系统关闭异常中止,则即使尚未关闭电源,也会调用每个暂停的驱动程序以进行恢复。因
此,attach(9E) 中的恢复代码不能对系统是否已实际断电作出任何假设。
电源管理框架认为组件的电源级别在执行DDI_RESUME 时未知。根据设备性质,驱动程序编
写者有两种选择:
如果驱动程序无需打开组件电源即可确定设备组件的实际电源级别(如通过读取寄存
器),则驱动程序应通过调用pm_power_has_changed(9F) 来通知框架每个组件的电源级
别。
如果驱动程序无法确定组件的电源级别,则驱动程序应在首次访问各个组件之前,在内
部将每个组件标记为未知并调用pm_raise_power(9F)。
以下示例给出了使用DDI_RESUME 命令的attach(9E) 例程。
示例12–7 实现DDI_RESUME 的attach(9E) 例程
int
xxattach(devinfo_t *dip, ddi_attach_cmd_t cmd)
系统电源管理模型
第12 章• 电源管理227
示例12–7 实现DDI_RESUME 的attach(9E) 例程(续)
{
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
系统电源管理模型
228 编写设备驱动程序• 2006 年11 月
示例12–7 实现DDI_RESUME 的attach(9E) 例程(续)
*/
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);
}
}
系统电源管理模型
第12 章• 电源管理229
注– 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 via DDI_SUSPEND
*/
while (xsp->xx_suspended)
cv_wait(&xsp->xx_suspend_cv, &xsp->mu);
/*
* Mark component busy so 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
*/
电源管理设备访问示例
230 编写设备驱动程序• 2006 年11 月
示例12–8 设备访问(续)
[...]
}
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)
电源管理设备访问示例
第12 章• 电源管理231
示例12–9 设备操作完成(续)
*/
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)。此调用将阻止框架进一
步降能耗,然后针对组件调用ddi_dev_is_needed(9F)。在对pm_raise_power(9F) 的调用返
回之前,框架接着调用power(9E) 以提高组件的能耗。驱动程序的power(9E) 代码必须恢复
在较低级别中丢失、但在较高级别中需要的任何状态。
电源管理控制流程
232 编写设备驱动程序• 2006 年11 月
分离某个驱动程序时,该驱动程序应针对每个组件调用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) 来恢复硬件状态。
电源管理接口的更改
第12 章• 电源管理233
以下接口和命令已过时,现在仍支持它们是为了适于采用二进制的场合:
ddi_dev_is_needed(9F)
pm_create_components(9F)
pm_destroy_components(9F)
pm_get_normal_power(9F)
pm_set_normal_power(9F)
DDI_PM_SUSPEND
DDI_PM_RESUME
从Solaris 8 发行版开始,如果启用了autopm,则导出pm-components 属性的设备将自动使用
电源管理。
现在,框架可以通过pm-components 属性了解每个设备支持的电源级别。
框架对设备不同组件之间的相关性不会作出任何假设。更改电源级别时,设备驱动程序负
责根据需要保存并恢复硬件状态。
通过这些更改,电源管理框架可以处理新兴的设备技术。现在,电源管理可以节省更多的
电。框架可以自动检测哪些设备能够省电。框架可以使用设备的中间能耗状态。现在,系
统可以实现节能目标,而无需关闭整个系统的电源,也无需使用任何功能。
表12–1电源管理接口
已删除的接口等效的Solaris 10 接口
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 无
电源管理接口的更改
234 编写设备驱动程序• 2006 年11 月
分层驱动程序接口(Layered Driver Interface,
LDI)
LDI 是一组DDI/DKI,内核模块可以使用它来访问系统中的其他设备。另外使用LDI 还可
以确定内核模块当前使用的设备。
本章包含以下主题:
第236 页中的“内核接口”
第262 页中的“用户接口”
LDI 概述
LDI 包括以下两类接口:
内核接口。用户应用程序使用系统调用来打开、读取和写入由内核中的设备驱动程序管
理的设备。内核模块可以使用LDI 内核接口来打开、读取和写入由内核中的另一设备驱
动程序管理的设备。例如,用户应用程序可使用read(2) 而内核模块可使用ldi_read(9F)
来读取同一设备。请参见第236 页中的“内核接口”。
用户接口。LDI 用户接口可为用户进程提供有关内核中其他设备当前使用哪些设备的信
息。请参见第262 页中的“用户接口”。
介绍LDI 时经常用到下列术语:
Target Device(目标设备)。目标设备是内核中的设备,由设备驱动程序管理并由设备
消费方访问。
Device Consumer(设备消费方)。设备消费方是打开并访问目标设备的用户进程或内
核模块。设备消费方通常对目标设备执行open、read、write 或ioctl 之类的操作。
Kernel Device Consumer(内核设备消费方)。内核设备消费方是一种特定类型的设备
消费方,它是访问目标设备的内核模块。通常情况下,内核设备消费方不是用于管理要
访问的目标设备的设备驱动程序。相反,内核设备消费方通过管理目标设备的设备驱动
程序间接访问目标设备。
13 第1 3 章
235
Layered Driver(分层驱动程序)。分层驱动程序是一种特定类型的内核设备消费方。分
层驱动程序是一种不直接管理任何硬件的内核驱动程序。相反,分层驱动程序通过管理
目标设备的设备驱动程序间接访问这些目标设备中的一个或多个设备。例如,卷管理器
和STREAMS 多路复用器就是比较典型的分层驱动程序。
内核接口
通过某些LDI 内核接口,LDI 可以跟踪和报告内核设备使用信息。请参见第236 页中的“分
层标识符-内核设备消费方”。
通过其他LDI 内核接口,内核模块可以对目标设备执行open、read 和write 之类的访问操
作。另外,通过这些LDI 内核接口,内核设备消费方可以查询有关目标设备的属性和事件
信息。请参见第237 页中的“分层驱动程序句柄-目标设备”。
第240 页中的“LDI 内核接口示例”介绍了使用其中多个LDI 接口的驱动程序示例。
分层标识符-内核设备消费方
通过分层标识符,LDI 可以跟踪和报告内核设备使用信息。分层标识符(ldi_ident_t) 用于
标识内核设备消费方。内核设备消费方必须先获取分层标识符,然后才能使用LDI 打开目
标设备。
分层驱动程序是唯一受支持的内核设备消费方类型。因此,分层驱动程序必须获取与设备
编号、设备信息节点或分层驱动程序流关联的分层标识符。分层标识符与分层驱动程序关
联。分层标识符与目标设备没有关联。
可以通过libdevinfo(3LIB) 接口、fuser(1M) 命令或prtconf(1M) 命令,检索通过LDI 收集
的内核设备使用信息。例如,使用prtconf(1M) 命令可以显示分层驱动程序正在访问哪些
目标设备,或者哪些分层驱动程序正在访问特定目标设备。要了解有关如何检索设备使用
情况的更多信息,请参见第262 页中的“用户接口”。
下面介绍了LDI 分层标识符接口:
ldi_ident_t 分层标识符。属于不透明类型。
ldi_ident_from_dev(9F) 分配和检索与dev_t 设备编号关联的分层标识符。
ldi_ident_from_dip(9F) 分配和检索与dev_info_t 设备信息节点关联的分层标识
符。
ldi_ident_from_stream(9F) 分配和检索与流关联的分层标识符。
ldi_ident_release(9F) 释放使用ldi_ident_from_dev(9F)、ldi_ident_from_dip(9F)
或ldi_ident_from_stream(9F) 分配的分层标识符。
内核接口
236 编写设备驱动程序• 2006 年11 月
分层驱动程序句柄-目标设备
内核设备消费方必须使用分层驱动程序句柄(ldi_handle_t) 来通过LDI 接口访问目标设
备。ldi_handle_t 类型仅对LDI 接口有效。当LDI 成功打开某个设备时,将分配并返回此
句柄。然后,内核设备消费方可使用此句柄通过LDI 接口访问目标设备。LDI 在关闭设备时
会取消分配该句柄。有关示例,请参见第240 页中的“LDI 内核接口示例”。
本节讨论内核设备消费方如何访问目标设备并检索不同类型的信息。要了解内核设备消费
方如何打开和关闭目标设备,请参见第237 页中的“打开和关闭目标设备”。要了解内核
设备消费方如何对目标设备执行read、write、strategy 和ioctl 之类的操作,请参见第237
页中的“访问目标设备”。第238 页中的“检索目标设备信息”介绍了用于检索目标设
备信息(如设备打开类型和设备次要名称)的接口。第239 页中的“检索目标设备属性值
”介绍了用于检索目标设备属性的值和地址的接口。要了解内核设备消费方如何接收来自
目标设备的事件通知,请参见第239 页中的“接收异步设备事件通知”。
打开和关闭目标设备
本节介绍用于打开和关闭目标设备的LDI 内核接口。打开接口采用指向分层驱动程序句柄
的指针。打开接口会尝试打开由设备编号、设备ID 或路径名指定的目标设备。如果打开操
作成功,则打开接口将分配并返回可用于访问目标设备的分层驱动程序句柄。关闭接口用
于关闭与指定分层驱动程序句柄关联的目标设备,然后释放该分层驱动程序句柄。
ldi_handle_t 用于访问目标设备的分层驱动程序句柄。一种成功打开设备时返
回的不透明数据结构。
ldi_open_by_dev(9F) 打开由dev_t 设备编号参数指定的设备。
ldi_open_by_devid(9F) 打开由ddi_devid_t 设备ID 参数指定的设备。另外,还必须指
定要打开的次要节点名称。
ldi_open_by_name(9F) 根据路径名打开设备。路径名是内核地址空间中以NULL结尾的
字符串。路径名必须是以正斜杠字符(/) 开头的绝对路径。
ldi_close(9F) 关闭使用ldi_open_by_dev(9F)、ldi_open_by_devid(9F) 或
ldi_open_by_name(9F) 打开的设备。在ldi_close(9F) 返回之后,
已关闭的设备的分层驱动程序句柄不再有效。
访问目标设备
本节介绍用于访问目标设备的LDI 内核接口。通过这些接口,内核设备消费方可以对由分
层驱动程序句柄指定的目标设备执行操作。内核设备消费方可以对目标设备执行read、
write、strategy 和ioctl 之类的操作。
ldi_handle_t 用于访问目标设备的分层驱动程序句柄。属于不透明数据结构。
ldi_read(9F) 将读取请求传递到目标设备的设备入口点。块设备、字符设备和
STREAMS 设备支持此操作。
阅读(452) | 评论(0) | 转发(0) |