编写设备驱动程序

第 2 章 Solaris 内核和设备树

设备驱动程序需要作为操作系统的组成部分透明地工作。理解内核工作方式是了解设备驱动程序的前提条件。本章概述了 Solaris 内核和设备树。有关设备驱动程序工作方式的概述,请参见第 1 章

本章介绍有关以下主题的信息:

什么是内核?

Solaris 内核是用于管理系统资源的程序。内核将应用程序与系统硬件隔离,并为它们提供基本系统服务,如输入/输出 (input/output, I/O) 管理、虚拟内存和调度。内核由需要时动态装入内存的对象模块组成。

Solaris 内核在逻辑上可分为两个部分: 第一部分称为内核,用于管理文件系统、调度和虚拟内存。第二部分称为 I/O 子系统,用于管理物理组件。

内核提供了一组接口,供可通过系统调用访问的应用程序使用。Reference Manual Collection 的第 2 部分对系统调用进行了介绍(请参见 Intro(2))。某些系统调用用于调用设备驱动程序以执行 I/O 操作。设备驱动程序是可装入的内核模块,用于管理数据传输,同时将内核的其余部分与设备硬件隔离。为了与操作系统兼容,设备驱动程序需要能够提供多线程、虚拟内存寻址以及 32 位和 64 位操作之类的功能。

下图解释了内核的工作机制。内核模块用于处理来自应用程序的系统调用。I/O 模块用于与硬件通信。

图 2–1 Solaris 内核

图中显示了用户级应用程序对特定内核级模块的调用,以及驱动程序与设备其他模块之间的调用。

内核通过以下功能提供对设备驱动程序的访问:

多线程执行环境

Solaris 内核是多线程的。在多处理器计算机上,多个内核线程可以运行内核代码并且可以并发运行。内核线程也可能随时被其他内核线程抢先。

内核的多线程特征对设备驱动程序强加了某些附加限制。有关多线程注意事项的更多信息,请参见第 3 章。必须对设备驱动程序进行编码,使其在许多不同线程请求时按需运行。对于每个线程,驱动程序必须处理重叠的 I/O 请求的争用问题。

虚拟内存

Solaris 虚拟内存系统的完整概述超出本书范围,但在讨论设备驱动程序时使用了两个特别重要的虚拟内存术语: 虚拟地址和地址空间。

作为特殊文件的设备

设备在文件系统中表示为特殊文件。在 Solaris OS 中, 这些文件驻留在 /devices 目录分层结构中。

特殊文件的类型可以为,也可以为字符。该类型表示了设备驱动程序的种类。驱动程序可以实现这两种类型。例如,磁盘驱动程序导出字符接口以供 fsck(1)mkfs(1) 实用程序使用,导出块接口以供文件系统使用。

每个特殊文件都与一个设备编号 (dev_t) 关联。设备编号由主设备号次要设备号组成。设备号标识与特殊文件关联的设备驱动程序。次要设备号由设备驱动程序创建,供其用来进一步标识特殊文件。通常,次要设备号是一种编码,用于标识驱动程序应访问的设备实例以及应执行的访问类型。例如,次要设备号可以标识用于备份的磁带设备,并可指定完成备份操作后需要将磁带反绕。

DDI/DKI 接口

在 System V Release 4 (SVR4) 中,设备驱动程序与 UNIX 内核其余部分之间的接口被标准化为 DDI/DKI。DDI/DKI 在 Reference Manual Collection 的第 9 部分中进行介绍。第 9E 节介绍驱动程序入口点,第 9F 节介绍驱动程序可调用的函数,而第 9S 节介绍设备驱动程序使用的内核数据结构。请参见 Intro(9E)Intro(9F)Intro(9S)

DDI/DKI 旨在对设备驱动程序与内核其余部分之间的所有接口进 行标准化并进行说明。此外,无论处理器体系结构是 SPARC 还是 x86,DDI/DKI 都允许任何运行 Solaris OS 的计算机的驱动程序的源代码和二进制代码保持兼容。仅使用 DDI/DKI 中包含的内核功能的驱动程序称为与 DDI/DKI 兼容的设备驱动程序

DDI/DKI 允许您为运行 Solaris OS 的任何计算机编写与平台无关的设备驱动程序。通过这些二进制代码兼容的驱动程序,您可以更方便地将第三方硬件和软件集成到运行 Solaris OS 的任何计算机中。DDI/DKI 与体系结构无关,从而允许同一驱动程序在一组不同的计算机体系结构中工作。

平台无关性是通过在以下方面设计 DDI 实现的:

设备树概述

Solaris OS 中的设备表示为互连的设备信息节点树。设备树描述特定计算机的已装入设备的配置。

设备树组件

系统将会生成树结构,其中包含有关引导时连接到计算机的设备的信息。此外,系统正常运行时也可以动态重新配置设备树。设备树从表示平台的根设备节点开始。

根节点下面是设备树的分支。分支由一个或多个总线结点设备和一个终止叶设备组成。

总线结点设备可为设备树中的从属设备提供总线映射和转换服务。PCI - PCI 网桥、PCMCIA 适配器和 SCSI HBA 都是结点设备的示例。编写结点设备驱动程序的讨论仅限于 SCSI HBA 驱动程序的开发(请参见第 18 章)。

叶设备通常为外围设备,如磁盘、磁带、网络适配器、帧缓存器等。叶设备驱动程序可以导出传统的字符驱动程序接口和块驱动程序接口。通过这些接口,用户进程可在存储设备或通信设备中读取和写入数据。

系统通过以下步骤来生成树:

  1. CPU 经过初始化后搜索固件。

  2. 主要固件(OpenBoot、基本输入/输出系统 (Basic Input/Output System, BIOS) 或 Bootconf)初始化并创建包含已知或自标识硬件的设备树。

  3. 当主要固件在设备中发现兼容固件时,主要固件将初始化该设备并检索设备属性。

  4. 该固件将查找并引导操作系统。

  5. 内核从树的根节点开始,搜索匹配的设备驱动程序并将该驱动程序绑定到设备。

  6. 如果设备是结点,则内核会查找固件尚未检测到的子设备。内核会将所有子设备都添加到树的子树节点下面。

  7. 内核从步骤 5 开始重复该过程,直到无需再创建设备节点。

每个驱动程序都会导出设备操作结构 dev_ops(9S),以定义设备驱动程序可以执行的操作。设备操作结构包含通用操作(如 attach(9E)detach(9E)getinfo(9E))的函数指针。该结构同时还包含了一组与特定总线结点驱动程序操作相关的函数指针,以及一组与特定叶结点设备驱动程序操作相关的函数指针。

树结构将在节点之间创建父子关系。此父子关系是体系结构无关性的关键。当叶驱动程序或总线结点驱动程序本质上需要依赖于体系结构的服务时,该驱动程序会请求其父级提供该服务。采用此方法,不管计算机或处理器的体系结构是什么,驱动程序都可以正常运行。下图显示了典型的设备树。

图 2–2 示例设备树

图中显示了典型的设备树中的叶和节点。

子树节点可以有一个或多个子节点。叶节点表示各个设备。

显示设备树

设备树可以采用以下三种方式显示:


注 –

/devices 仅显示将驱动程序配置到系统中的设备。prtconf(1M) 命令显示所有设备节点,而不管系统中是否存在设备驱动程序。


libdevinfo

libdevinfo 库提供用于访问所有公共设备配置数据的接口。有关接口列表,请参见 libdevinfo(3LIB) 手册页。

prtconf 命令

以下摘录的 prtconf(1M) 命令示例显示了系统中的所有设备。

System Configuration:  Sun Microsystems  sun4u
Memory size: 128 Megabytes
System Peripherals (Software Nodes):

SUNW,Ultra-5_10
    packages (driver not attached)
        terminal-emulator (driver not attached)
        deblocker (driver not attached)
        obp-tftp (driver not attached)
        disk-label (driver not attached)
        SUNW,builtin-drivers (driver not attached)
        sun-keyboard (driver not attached)
        ufs-file-system (driver not attached)
    chosen (driver not attached)
    openprom (driver not attached)
        client-services (driver not attached)
    options, instance #0
    aliases (driver not attached)
    memory (driver not attached)
    virtual-memory (driver not attached)
    pci, instance #0
        pci, instance #0
            ebus, instance #0
                auxio (driver not attached)
                power, instance #0
                SUNW,pll (driver not attached)
                se, instance #0
                su, instance #0
                su, instance #1
                ecpp (driver not attached)
                fdthree, instance #0
                eeprom (driver not attached)
                flashprom (driver not attached)
                SUNW,CS4231 (driver not attached)
            network, instance #0
            SUNW,m64B (driver not attached)
            ide, instance #0
                disk (driver not attached)
                cdrom (driver not attached)
                dad, instance #0
                sd, instance #15
        pci, instance #1
            pci, instance #0
                pci108e,1000 (driver not attached)
                SUNW,hme, instance #1
                SUNW,isptwo, instance #0
                    sd (driver not attached)
                    st (driver not attached)
                    sd, instance #0 (driver not attached)
                    sd, instance #1 (driver not attached)
                    sd, instance #2 (driver not attached)
                    ...
    SUNW,UltraSPARC-IIi (driver not attached)
    SUNW,ffb, instance #0
    pseudo, instance #0

/devices 目录

/devices 分层结构提供了表示设备树的名称空间。下面是 /devices 名称空间的缩写列表。样例输出对应于先前显示的示例设备树和 prtconf(1M) 输出。

/devices
/devices/pseudo
/devices/pci@1f,0:devctl
/devices/SUNW,ffb@1e,0:ffb0
/devices/pci@1f,0
/devices/pci@1f,0/pci@1,1
/devices/pci@1f,0/pci@1,1/SUNW,m64B@2:m640
/devices/pci@1f,0/pci@1,1/ide@3:devctl
/devices/pci@1f,0/pci@1,1/ide@3:scsi
/devices/pci@1f,0/pci@1,1/ebus@1
/devices/pci@1f,0/pci@1,1/ebus@1/power@14,724000:power_button
/devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:a
/devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:b
/devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:0,hdlc
/devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:1,hdlc
/devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:a,cu
/devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:b,cu
/devices/pci@1f,0/pci@1,1/ebus@1/ecpp@14,3043bc:ecpp0
/devices/pci@1f,0/pci@1,1/ebus@1/fdthree@14,3023f0:a
/devices/pci@1f,0/pci@1,1/ebus@1/fdthree@14,3023f0:a,raw
/devices/pci@1f,0/pci@1,1/ebus@1/SUNW,CS4231@14,200000:sound,audio
/devices/pci@1f,0/pci@1,1/ebus@1/SUNW,CS4231@14,200000:sound,audioctl
/devices/pci@1f,0/pci@1,1/ide@3
/devices/pci@1f,0/pci@1,1/ide@3/sd@2,0:a
/devices/pci@1f,0/pci@1,1/ide@3/sd@2,0:a,raw
/devices/pci@1f,0/pci@1,1/ide@3/dad@0,0:a
/devices/pci@1f,0/pci@1,1/ide@3/dad@0,0:a,raw
/devices/pci@1f,0/pci@1
/devices/pci@1f,0/pci@1/pci@2
/devices/pci@1f,0/pci@1/pci@2/SUNW,isptwo@4:devctl
/devices/pci@1f,0/pci@1/pci@2/SUNW,isptwo@4:scsi

将驱动程序绑定到设备

除了构造设备树之外,内核还可确定用于管理设备的驱动程序。

将驱动程序绑定到设备指的是系统选择用于管理特定设备的驱动程序的过程。绑定名称是将驱动程序与设备信息树连接在一起的唯一设备结点名称。对于设备树中的每个设备,系统都会尝试从已安装的驱动程序列表中选择一个驱动程序。

每个设备节点都有关联的 name 属性。可以在系统引导期间通过外部代理(如 PROM )或通过 driver.conf 配置文件指定此属性。无论在哪种情况下,name 属性都表示指定给设备树中的设备的节点名称节点名称是在 /devices 中可见并列在 prtconf(1M) 输出中的名称。

图 2–3 设备节点名称

图中显示了一个简单的设备节点名称示例。

设备节点也可以有关联的 compatible 属性。compatible 属性包含设备的一个或多个可能的驱动程序名称或驱动程序别名的有序列表。

系统使用 compatible 属性和 name 属性来为设备选择驱动程序。如果 compatible 属性存在,则系统会首先尝试将 compatible 属性的内容与系统中的驱动程序匹配。系统将从 compatible 属性列表的第一个驱动程序名称开始,尝试将该驱动程序名称与系统中的已知驱动程序匹配。系统将会处理该列表中的每一项,直到找到匹配项或者到达列表结尾。

如果 name 属性或 compatible 属性的内容与系统中的某个驱动程序匹配,则将该驱动程序绑定到设备节点。如果未找到匹配项,则不会将任何驱动程序绑定到设备节点。

通用设备名称

某些设备将通用设备名称指定为 name 属性的值。通用设备名称用于描述设备的功能,不实际标识设备的特定驱动程序。例如,SCSI 主机总线适配器可能具有通用设备名称 scsi。以太网设备可能具有通用设备名称 ethernet

通过 compatible 属性,系统可以确定具有通用设备名称的设备的备用驱动程序名称,例如,glm 对应于 scsi HBA 设备驱动程序,hme 对应于 ethernet 设备驱动程序。

具有通用设备名称的设备需要提供 compatible 属性。


注 –

有关通用设备名称的完整说明,请参见 IEEE 1275 Open Firmware Boot Standard。


下图显示了具有特定设备名称的设备节点。驱动程序绑定名称 SUNW,ffb 与设备节点名称同名。

图 2–4 特定驱动程序节点绑定

图中显示了使用特定设备名称的设备节点: SUNW, ffb。

下图显示了具有通用设备名称 display 的设备节点。驱动程序绑定名称 SUNW,ffbcompatible 属性驱动程序列表中与系统驱动程序列表中的驱动程序匹配的第一个名称。在这种情况下,display 是帧缓存器的通用设备名称。

图 2–5 通用驱动程序节点绑定

图中显示了使用通用设备名称的设备节点: display。