第 1 部分针对 Oracle Solaris 平台设计设备驱动程序
9. 直接内存访问 (Direct Memory Access, DMA)
系统会为支持程控 I/O 的设备指定一个或多个总线地址空间区域,这些区域映射到设备的可寻址区域。这些映射在与设备相关的 reg 属性中描述为值对。每个值对描述一段总线地址。
驱动程序通过指定寄存器编号(即 regspec,设备的 reg 属性的索引)来标识特定的总线地址映射。reg 属性标识设备的 busaddr 和 size。驱动程序在调用 DDI 函数(如 ddi_regs_map_setup(9F))时传递寄存器编号。驱动程序通过调用 ddi_dev_nregs(9F) 可以确定已为设备指定的可映射区域数。
主机的数据格式可以与设备的数据格式具有不同的字节序特征。在这种情况下,主机与设备间传送的数据需要进行字节交换,才能符合目标位置的数据格式要求。与主机具有相同字节序特征的设备无需对数据进行字节交换。
驱动程序通过在传递给 ddi_regs_map_setup(9F) 的 ddi_device_acc_attr(9S) 结构中设置相应的标志来指定设备的字节序特征。然后,DDI 框架在驱动程序调用 ddi_getX 例程(如 ddi_get8(9F))或 ddi_putX 例程(如 ddi_put16(9F))来读/写设备内存时,执行任何所需的字节交换。
平台可以重新排列数据的负载和存储,以优化平台的性能。由于某些设备可能不允许重新排列,因此驱动程序在设置到设备的映射时需要指定设备的排序要求。
此结构描述了设备的字节序和数据顺序要求。驱动程序需要对此结构进行初始化并将其作为一个参数传递给 ddi_regs_map_setup(9F)。
typedef struct ddi_device_acc_attr { ushort_t devacc_attr_version; uchar_t devacc_attr_endian_flags; uchar_t devacc_attr_dataorder; } ddi_device_acc_attr_t;
指定 DDI_DEVICE_ATTR_V0
描述设备的字节序特征。指定为一个位值,其可能值包括:
DDI_NEVERSWAP_ACC-从不交换数据
DDI_STRUCTURE_BE_ACC-设备数据格式为大尾数法
DDI_STRUCTURE_LE_ACC-设备数据格式为小尾数法
描述 CPU 根据设备的要求引用数据时必须遵循的顺序。指定为一个枚举值,其中数据访问限制的排列顺序为最严格到最不严格。
DDI_STRICTORDER_ACC-主机必须按程序员指定的顺序发出引用。此标志为缺省行为。
DDI_UNORDERED_OK_ACC-允许主机重新排列到设备内存的负载和存储。
DDI_MERGING_OK_ACC-允许主机将单个存储合并到连续位置。此设置还表明需要重新排列。
DDI_LOADCACHING_OK_ACC-允许主机从设备读取数据,直到发生存储。
DDI_STORECACHING_OK_ACC-允许主机对写入设备的数据进行高速缓存。然后,主机可以延迟将数据写入设备,直到将来某一时间。
注 - 系统对数据的访问可能会比驱动程序在 devacc_attr_dataorder 中所做指定更严格。就数据访问而言,由从必须遵循严格的数据排序到可以执行高速缓存存储操作,驱动程序对主机的限制依次降低。
驱动程序通常会在执行 attach(9E) 期间映射设备的所有区域。驱动程序通过调用 ddi_regs_map_setup(9F)、指定要映射的区域寄存器编号、区域的设备访问属性以及偏移和大小来映射设备内存区域。DDI 框架为设备区域设置映射并将一个不透明句柄返回给驱动程序。在从设备区域读取数据或向其中写入数据时,此数据访问句柄将作为一个参数传递给 ddi_get8(9F) 或 ddi_put8(9F) 系列例程。
驱动程序通过检查设备导出的映射数来验证设备映射的形式与驱动程序预期的形式是否匹配。驱动程序调用 ddi_dev_nregs(9F),然后调用 ddi_dev_regsize(9F) 来验证每个映射的大小。
下面的简单示例说明了 DDI 数据访问接口。此驱动程序用于虚构的小端字节序设备,该设备每次接受一个字符并在准备好接受另一个字符时生成中断。此设备实现两个寄存器集:第一个是 8 位 CSR 寄存器,第二个是 8 位数据寄存器。
示例 7-1 映射设置
#define CSR_REG 0 #define DATA_REG 1 /* * Initialize the device access attributes for the register * mapping */ dev_acc_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; dev_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; dev_acc_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; /* * Map in the csr register (register 0) */ if (ddi_regs_map_setup(dip, CSR_REG, (caddr_t *)&(pio_p->csr), 0, sizeof (Pio_csr), &dev_acc_attr, &pio_p->csr_handle) != DDI_SUCCESS) { mutex_destroy(&pio_p->mutex); ddi_soft_state_free(pio_softstate, instance); return (DDI_FAILURE); } /* * Map in the data register (register 1) */ if (ddi_regs_map_setup(dip, DATA_REG, (caddr_t *)&(pio_p->data), 0, sizeof (uchar_t), &dev_acc_attr, &pio_p->data_handle) \ != DDI_SUCCESS) { mutex_destroy(&pio_p->mutex); ddi_regs_map_free(&pio_p->csr_handle); ddi_soft_state_free(pio_softstate, instance); return (DDI_FAILURE); }