编写设备驱动程序

32 位和 64 位数据结构宏

示例 15–15 中的方法适用于许多驱动程序。另一种方案是使用 <sys/model.h> 中提供的数据结构宏在应用程序和内核之间移动数据。从功能角度看,这些宏减少了代码混乱问题,并使代码的表现形式完全相同。


示例 15–16 使用数据结构宏移动数据

int
    xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
        cred_t *cr, int *rval_p)
    {    
        STRUCT_DECL(opdata, op);

        if (cmd != OPONE)
            return (ENOTTY);

        STRUCT_INIT(op, mode);

        if (copyin((void *)arg,
            STRUCT_BUF(op), STRUCT_SIZE(op)))
                return (EFAULT);

        if (STRUCT_FGET(op, flag) != XXACTIVE ||     
            STRUCT_FGET(op, size) > XXSIZE)
                return (EINVAL);
        xxdowork(device_state, STRUCT_FGET(op, size));
        return (0);
}

结构宏如何工作?

在 64 位设备驱动程序中,结构宏使得两种处理能力的数据结构可以使用相同的内核内存片段。内存缓冲区保存数据结构的本机形式的内容,即 LP64 和 ILP32 形式。每种结构的访问是通过条件表达式实现的。如果驱动程序以 32 位方式编译,则仅支持一种数据模型(本机形式)。不使用条件表达式。

64 位版本的宏依赖于数据结构阴影版本的定义。阴影版本描述了使用固定宽度类型的 32 位接口。将 "32" 附加到本机数据结构名称,就形成了阴影数据结构的名称。为方便起见,将阴影结构的定义放置到与本机结构相同的文件中,以降低将来的维护成本。

这些宏可以采用下列参数:

structname

数据结构的本机形式的结构名称,即在 struct 关键字后输入的内容。

umodel

包含用户数据模型(例如 FILP32FLP64)的标志字,从 ioctl(9E) 的模式参数中提取。

handle

此名称用于引用这些宏所处理的结构的特定实例。

fieldname

结构内部的字段的名称。

何时使用结构宏

宏使您能够仅对数据项的字段进行适当地引用。宏不提供采用基于数据模型的单独代码路径的方法。如果数据结构中的字段数量很大,则应避免使用宏。如果对这些字段的引用非常频繁,也应避免使用宏。

在实现宏的过程中,宏隐藏了数据模型之间的很多差异。因此,使用此接口编写的代码通常比较容易理解。如果驱动程序以 32 位方式编译,则生成的代码较为简洁,并且无需冗长的 #ifdefs,但仍保留了类型检查。

声明并初始化结构句柄

可以使用 STRUCT_DECL(9F)STRUCT_INIT(9F) 声明和初始化句柄及空间,以便在栈中对 ioctl 进行解码。STRUCT_HANDLE(9F)STRUCT_SET_HANDLE(9F) 可以声明和初始化句柄,但不在栈中分配空间。如果结构非常大,或者包含在其他某个数据结构中,则后面的宏比较有用。


注 –

因为 STRUCT_DECL(9F)STRUCT_HANDLE(9F) 宏扩展为数据结构声明,所以这些宏在 C 代码中应该使用这些声明进行分组。


用于声明和初始化结构的宏如下所示:

STRUCT_DECL(structname, handle)

声明为 structname 数据结构调用了 handle结构句柄STRUCT_DECL 按其本机形式在栈中分配空间。假定本机形式大于或等于结构的 ILP32 形式。

STRUCT_INIT(handle, umodel)

handle 的数据模型初始化为 umodel。在对使用 STRUCT_DECL(9F) 声明的结构句柄进行任何访问之前,必须调用此宏。

STRUCT_HANDLE(structname, handle)

声明调用了 handle结构句柄。它与 STRUCT_DECL(9F) 相对。

STRUCT_SET_HANDLE(handle , umodel, addr )

handle 的数据模型初始化为 umodel,然后将 addr 设置为用于后续处理的缓冲区。在访问使用 STRUCT_DECL(9F) 声明的结构句柄之前,请调用此宏。

结构句柄的操作

用于在结构上执行操作的宏如下所示:

size_t STRUCT_SIZE(handle )

返回 handle 所引用的结构的大小(取决于该结构的嵌入式数据模型)。

typeof fieldname STRUCT_FGET(handle, fieldname)

返回 handle 所引用的数据结构中的指定字段。此字段为非指针类型。

typeof fieldname STRUCT_FGETP(handle, fieldname)

返回 handle 所引用的数据结构中的指定字段。此字段为指针类型。

STRUCT_FSET(handle , fieldname, val)

handle 所引用的数据结构中的指定字段设置为值 valval 的类型应与 fieldname 的类型相匹配。此字段为非指针类型。

STRUCT_FSETP(handle , fieldname, val)

handle 所引用的数据结构中的指定字段设置为值 val。此字段为指针类型。

typeof fieldname *STRUCT_FADDR(handle, fieldname)

返回 handle 所引用的数据结构中的指定字段的地址。

struct structname *STRUCT_BUF(handle)

返回指向 handle 所描述的本机结构的指针。

其他操作

其他一些结构宏如下所示:

size_t SIZEOF_STRUCT(struct_name, datamodel)

返回 struct_name 的大小(取决于给定的数据模型)。

size_t SIZEOF_PTR(datamodel )

根据给定的数据模型,返回指针的大小。