第 1 部分针对 Oracle Solaris 平台设计设备驱动程序
9. 直接内存访问 (Direct Memory Access, DMA)
示例 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" 附加到本机数据结构名称,就形成了阴影数据结构的名称。为方便起见,将阴影结构的定义放置到与本机结构相同的文件中,以降低将来的维护成本。
这些宏可以采用下列参数:
数据结构的本机形式的结构名称,即在 struct 关键字后输入的内容。
包含用户数据模型(例如 FILP32 或 FLP64)的标志字,从 ioctl(9E) 的模式参数中提取。
此名称用于引用这些宏所处理的结构的特定实例。
结构内部的字段的名称。
宏使您能够仅对数据项的字段进行适当地引用。宏不提供采用基于数据模型的单独代码路径的方法。如果数据结构中的字段数量很大,则应避免使用宏。如果对这些字段的引用非常频繁,也应避免使用宏。
在实现宏的过程中,宏隐藏了数据模型之间的很多差异。因此,使用此接口编写的代码通常比较容易理解。如果驱动程序以 32 位方式编译,则生成的代码较为简洁,并且无需冗长的 #ifdefs,但仍保留了类型检查。
可以使用 STRUCT_DECL(9F) 和 STRUCT_INIT(9F) 声明和初始化句柄及空间,以便在栈中对 ioctl 进行解码。STRUCT_HANDLE(9F) 和 STRUCT_SET_HANDLE(9F) 可以声明和初始化句柄,但不在栈中分配空间。如果结构非常大,或者包含在其他某个数据结构中,则后面的宏比较有用。
用于声明和初始化结构的宏如下所示:
声明为 structname 数据结构调用了 handle 的结构句柄。STRUCT_DECL 按其本机形式在栈中分配空间。假定本机形式大于或等于结构的 ILP32 形式。
将 handle 的数据模型初始化为 umodel。在对使用 STRUCT_DECL(9F) 声明的结构句柄进行任何访问之前,必须调用此宏。
声明调用了 handle 的结构句柄。它与 STRUCT_DECL(9F) 相对。
将 handle 的数据模型初始化为 umodel,然后将 addr 设置为用于后续处理的缓冲区。在访问使用 STRUCT_DECL(9F) 声明的结构句柄之前,请调用此宏。
用于在结构上执行操作的宏如下所示:
返回 handle 所引用的结构的大小(取决于该结构的嵌入式数据模型)。
返回 handle 所引用的数据结构中的指定字段。此字段为非指针类型。
返回 handle 所引用的数据结构中的指定字段。此字段为指针类型。
将 handle 所引用的数据结构中的指定字段设置为值 val。val 的类型应与 fieldname 的类型相匹配。此字段为非指针类型。
将 handle 所引用的数据结构中的指定字段设置为值 val。此字段为指针类型。
返回 handle 所引用的数据结构中的指定字段的地址。
返回指向 handle 所描述的本机结构的指针。
其他一些结构宏如下所示:
返回 struct_name 的大小(取决于给定的数据模型)。
根据给定的数据模型,返回指针的大小。