编写设备驱动程序

第 4 章 属性

属性是用户定义的名称-值对结构,该结构使用 DDI/DKI 接口进行管理。本章介绍有关以下主题的信息:

设备属性

设备特性 (attribute) 信息可由称为属性 (property) 的名称-值对表示法表示。

例如,设备寄存器和板载内存可由 reg 属性表示。reg 属性是描述设备硬件寄存器的软件抽象术语。reg 属性的值对设备寄存器地址位置和大小进行编码。驱动程序使用 reg 属性访问设备寄存器。

另外一个示例是 interrupt 属性。interrupt 属性表示设备中断。interrupt 属性的值对设备中断 PIN 进行编码。

可以为属性指定五种类型的值:

没有值的属性被视为布尔属性。对于布尔属性,如果存在,则值为 True;如果不存在,则值为 False。

设备属性名称

严格地说,DDI/DKI 软件属性名称没有限制。但建议使用某些限制。IEEE 1275-1994 Standard for Boot Firmware 引导固件标准按如下方法定义属性:

属性是由 1 到 31 个可列显字符组成的人工可读文本字符串。属性名称不能包含大写字符或字符 "/"、"\"、":"、 "["、"]" 和 "@"。以字符 "+" 开头的属性名称保留供 IEEE 1275-1994 的将来修订使用。

根据约定,属性名称中不能使用下划线。可使用连字符 (-)。根据约定,以问号字符 (?) 结尾的属性名称包含字符串值(通常为 TRUE 或 FALSE),例如 auto-boot?

IEEE 1275 工作组的出版物中列出了预定义的属性名称。有关如何获取这些出版物的信息,请访问 http://playground.sun.com/1275/。有关在驱动程序配置文件中添加属性的讨论,请参见 driver.conf(4) 手册页。pm(9P)pm-components(9P) 手册页说明了如何在电源管理中使用属性。有关应该如何在设备驱动程序中记录属性的信息,请阅读 sd(7D) 手册页。

创建和更新属性

要为驱动程序创建属性,或者更新现有属性,请将 DDI 驱动程序更新接口(如 ddi_prop_update_int(9F)ddi_prop_update_string(9F))中的一个接口与相应的属性类型一起使用。有关可用属性接口的列表,请参见表 4–1。这些接口通常从驱动程序的 attach(9E) 入口点调用。在以下示例中,ddi_prop_update_string() 创建一个名为 pm-hardware-state 且值为 needs-suspend-resume 的字符串属性。

     /* The following code is to tell cpr that this device
     * needs to be suspended and resumed.
     */
    (void) ddi_prop_update_string(device, dip,
         "pm-hardware-state", "needs-suspend-resume");

在大多数情况下,使用 ddi_prop_update() 例程即可满足更新属性的要求。但是,有时更新经常更改的属性值的系统开销可能会导致性能问题。有关使用属性值的本地实例以避免使用 ddi_prop_update() 的说明,请参见prop_op() 入口点

查找属性

驱动程序可以请求其父级的属性,而后者又可以请求其父级。驱动程序可以控制是否将请求传递到其父级以上。

例如,以下示例中的 esp 驱动程序为每个目标维护一个名为 targetx-sync-speed 的整数属性。targetx-sync-speed 中的 x 表示目标编号。prtconf(1M) 命令以详细模式显示驱动程序属性。以下示例列出了 esp 驱动程序的部分内容。


% prtconf -v
...
       esp, instance #0
            Driver software properties:
                name <target2-sync-speed> length <4>
                    value <0x00000fa0>.
...

下表汇总了属性接口。

表 4–1 属性接口用法

系列 

属性接口 

说明 

ddi_prop_lookup

ddi_prop_exists(9F)

查找属性,如果该属性存在,则成功返回。如果该属性不存在,则失败。 

 

ddi_prop_get_int(9F)

查找并返回整数属性 

 

ddi_prop_get_int64(9F)

查找并返回 64 位整数属性 

 

ddi_prop_lookup_int_array(9F)

查找并返回整数数组属性 

 

ddi_prop_lookup_int64_array(9F)

查找并返回 64 位整数数组属性 

 

ddi_prop_lookup_string(9F)

查找并返回字符串属性 

 

ddi_prop_lookup_string_array(9F)

查找并返回字符串数组属性 

 

ddi_prop_lookup_byte_array(9F)

查找并返回字节数组属性 

ddi_prop_update

ddi_prop_update_int(9F)

更新或创建整数属性 

 

ddi_prop_update_int64(9F)

更新或创建单个 64 位整数属性 

 

ddi_prop_update_int_array(9F)

更新或创建整数数组属性 

 

ddi_prop_update_string(9F)

更新或创建字符串属性 

 

ddi_prop_update_string_array(9F)

更新或创建字符串数组属性 

 

ddi_prop_update_int64_array(9F)

更新或创建 64 位整数数组属性 

 

ddi_prop_update_byte_array(9F)

更新或创建字节数组属性 

ddi_prop_remove

ddi_prop_remove(9F)

删除单个属性 

 

ddi_prop_remove_all(9F)

删除与设备关联的所有属性 

尽可能使用 64 位版本的 int 属性接口(如 ddi_prop_update_int64(9F)),而不要使用 32 位版本(如 ddi_prop_update_int(9F))。

prop_op() 入口点

向系统报告设备属性或驱动程序属性通常需要使用 prop_op(9E) 入口点。如果驱动程序无需创建或管理其自己的属性,则 ddi_prop_op(9F) 函数可用于此入口点。

如果在驱动程序的 cb_ops(9S) 结构中定义了 ddi_prop_op(),则 ddi_prop_op(9F) 可用作设备驱动程序的 prop_op(9E) 入口点。ddi_prop_op() 使叶设备可在设备的属性列表中搜索并获取属性值。

如果驱动程序需要维护其值经常更改的属性,则应在 cb_ops() 结构中定义特定于驱动程序的 prop_op 例程,而不是调用 ddi_prop_op()。此方法可避免由于重复使用 ddi_prop_update() 而造成的效率低下。然后,驱动程序应在其软状态结构或驱动程序变量中维护属性值的副本。

prop_op(9E) 入口点向系统报告特定驱动程序属性的值和设备属性的值。在许多情况下,ddi_prop_op(9F) 例程在 cb_ops(9S) 结构中可用作驱动程序的 prop_op() 入口点。ddi_prop_op() 会执行所有必需的处理过程。对于处理设备属性请求时不需要进行特殊处理的驱动程序,ddi_prop_op() 即可满足要求。

但是,有时驱动程序必须提供 prop_op() 入口点。例如,如果驱动程序维护其值经常更改的属性,则针对每次更改使用 ddi_prop_update(9F) 更新属性便不能满足要求。相反,驱动程序应在实例的软状态下维护属性的阴影副本。然后,驱动程序可在值发生变化时更新阴影副本,而无需使用任何 ddi_prop_update() 例程。prop_op() 入口点必须拦截此属性的请求,并使用 ddi_prop_update() 例程之一更新属性的值,然后将请求传递到 ddi_prop_op() 以处理属性请求。

在以下示例中,prop_op() 拦截 temperature 属性的请求。属性发生变化时,驱动程序将更新状态结构中的变量。但是,仅当发出请求时才会更新该属性。然后,驱动程序使用 ddi_prop_op() 处理该属性请求。如果属性请求不特定于某个设备,则驱动程序不会拦截该请求。dev 参数的值等于 DDI_DEV_T_ANY(通配符设备编号)时即是这种情况。


示例 4–1 prop_op() 例程

static int
xx_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
    int flags, char *name, caddr_t valuep, int *lengthp)
{
        minor_t instance;
        struct xxstate *xsp;
        if (dev != DDI_DEV_T_ANY) {
                return (ddi_prop_op(dev, dip, prop_op, flags, name,
                    valuep, lengthp));
        }

        instance = getminor(dev);
        xsp = ddi_get_soft_state(statep, instance);
        if (xsp == NULL)
                return (DDI_PROP_NOTFOUND);
        if (strcmp(name, "temperature") == 0) {
                ddi_prop_update_int(dev, dip, name, temperature);
        }

        /* other cases */    
}