第 1 部分针对 Oracle Solaris 平台设计设备驱动程序
9. 直接内存访问 (Direct Memory Access, DMA)
14. 分层驱动程序接口 (Layered Driver Interface, LDI)
为了支持自动配置,驱动程序需要静态初始化以下数据结构:
驱动程序依赖于图 5-1 中的数据结构。必须提供并正确初始化这些数据结构。没有这些数据结构,可能无法正确装入驱动程序。结果导致可能无法装入必需的例程。如果驱动程序不支持某个操作,则 nodev(9F) 例程的地址可以用作占位符。在某些情况下,驱动程序支持入口点,并且仅需要返回成功信息或失败信息。在这种情况下,可以使用例程 nulldev(9F) 的地址。
注 - 应该在编译时对这些结构进行初始化。在任何其他时间,驱动程序都不应访问或更改这些结构。
static struct modlinkage xxmodlinkage = { MODREV_1, /* ml_rev */ &xxmodldrv, /* ml_linkage[] */ NULL /* NULL termination */ };
第一个字段是装入子系统的模块的版本号。该字段应为 MODREV_1。第二个字段指向接下来定义的驱动程序的 modldrv 结构。该结构的最后一个元素应始终为 NULL。
static struct modldrv xxmodldrv = { &mod_driverops, /* drv_modops */ "generic driver v1.1", /* drv_linkinfo */ &xx_dev_ops /* drv_dev_ops */ };
该结构更加详细地描述模块。第一个字段提供有关模块安装的信息。对于驱动程序模块,该字段应设置为 &mod_driverops。第二个字段是将由 modinfo(1M) 显示的字符串。第二个字段应包含足够的信息,以便确定生成驱动程序二进制文件的源代码版本。最后一个字段指向下节所定义的驱动程序的 dev_ops 结构。
static struct dev_ops xx_dev_ops = { DEVO_REV, /* devo_rev */ 0, /* devo_refcnt */ xxgetinfo, /* devo_getinfo: getinfo(9E) */ nulldev, /* devo_identify: identify(9E) */ xxprobe, /* devo_probe: probe(9E) */ xxattach, /* devo_attach: attach(9E) */ xxdetach, /* devo_detach: detach(9E) */ nodev, /* devo_reset: see devo_quiesce */ &xx_cb_ops, /* devo_cb_ops */ NULL, /* devo_bus_ops */ &xxpower, /* devo_power: power(9E) */ ddi_quiesce_not_needed, /* devo_quiesce: quiesce(9E) */ };
使用 dev_ops(9S) 结构,内核可以找到设备驱动程序的自动配置入口点。devo_rev 字段标识结构的修订号。该字段必须设置为 DEVO_REV。devo_refcnt 字段必须初始化为零。应使用相应驱动程序的入口点地址填充函数地址字段,但以下情况除外:
将 devo_identify 字段设置为 nulldev(9F)。identify() 入口点已过时。
如果不需要 probe(9E) 例程,应将 devo_probe 字段设置为 nulldev(9F)。
将 devo_reset 字段设置为 nodev(9F)。nodev() 函数返回 ENXIO。请参见 devo_quiesce。
如果不需要 power() 例程,应将 devo_power 字段设置为 NULL。提供电源管理功能的设备的驱动程序必须具有 power(9E) 入口点。请参见第 12 章。
如果驱动程序不需要实现停止,请将 devo_quiesce 字段设置为 ddi_quiesce_not_needed()。对设备进行管理的驱动程序必须提供一个 quiesce(9E) 入口点。
devo_cb_ops 成员应包含 cb_ops(9S) 结构的地址。devo_bus_ops 字段必须设置为 NULL。
static struct cb_ops xx_cb_ops = { xxopen, /* open(9E) */ xxclose, /* close(9E) */ xxstrategy, /* strategy(9E) */ xxprint, /* print(9E) */ xxdump, /* dump(9E) */ xxread, /* read(9E) */ xxwrite, /* write(9E) */ xxioctl, /* ioctl(9E) */ xxdevmap, /* devmap(9E) */ nodev, /* mmap(9E) */ xxsegmap, /* segmap(9E) */ xxchpoll, /* chpoll(9E) */ xxprop_op, /* prop_op(9E) */ NULL, /* streamtab(9S) */ D_MP | D_64BIT, /* cb_flag */ CB_REV, /* cb_rev */ xxaread, /* aread(9E) */ xxawrite /* awrite(9E) */ };
cb_ops(9S) 结构包含设备驱动程序的字符操作和块操作的入口点。驱动程序不支持的所有入口点应初始化为 nodev(9F)。例如,字符设备驱动程序应该将所有块字段(例如 cb_stategy)设置为 nodev(9F)。请注意,保留 mmap(9E) 入口点是为了兼容早期发行版。驱动程序应使用 devmap(9E) 入口点来进行设备内存映射。如果支持 devmap(9E),应将 mmap(9E) 设置为 nodev(9F)。
streamtab 字段表明驱动程序是否基于 STREAMS。只有第 19 章中讨论的网络设备驱动程序基于 STREAMS。所有不基于 STREAMS 的驱动程序必须将 streamtab 字段设置为 NULL。
cb_flag 成员包含以下标志:
D_MP 标志表示驱动程序对于多线程是安全的。Oracle Solaris OS 仅支持线程安全的驱动程序,因此必须设置 D_MP。
D_64BIT 标志导致驱动程序使用 uio(9S) 结构的 uio_loffset 字段。驱动程序应在 cb_flag 字段中设置 D_64BIT 标志,以便正确处理 64 位偏移。
D_DEVMAP 标志支持 devmap(9E) 入口点。有关 devmap(9E) 的信息,请参见第 10 章。
cb_rev 是 cb_ops 结构修订号。该字段必须设置为 CB_REV。