第 14 章 Newbus

特别感谢 Matthew N. Dodd、Warner Losh、Bill Paul、Doug Rabson、Mike Smith、Peter Wemm 和 Scott Long.

本章详细解释了 Newbus 设备框架。

14.1. 设备驱动程序

14.1.1. 设备驱动程序的目的

设备驱动程序是一个软件组件,它提供内核对外设(例如磁盘、网络适配器)的通用视图与外设的实际实现之间的接口。设备驱动程序接口 (DDI) 是内核与设备驱动程序组件之间定义的接口。

14.1.2. 设备驱动程序的类型

在 UNIX® 以及 FreeBSD 的早期,定义了四种类型的设备

  • 块设备驱动程序

  • 字符设备驱动程序

  • 网络设备驱动程序

  • 伪设备驱动程序

块设备 以使用固定大小的块 [数据] 的方式执行。这种类型的驱动程序依赖于所谓的缓冲区缓存,缓冲区缓存已将访问的数据块缓存在内存的专用部分。通常,此缓冲区缓存基于写后刷新,这意味着当内存中的数据被修改时,只要系统执行其定期磁盘刷新,它就会同步到磁盘,从而优化写入操作。

14.1.3. 字符设备

但是,在 FreeBSD 4.0 及更高版本中,块设备和字符设备之间的区别变得不再存在。

14.2. Newbus 概述

Newbus 是基于抽象层的新型总线架构的实现,它是在 FreeBSD 3.0 引入 Alpha 端口到源代码树时引入的。直到 4.0 版本才成为设备驱动程序使用的默认系统。其目标是提供一种更面向对象的方式来互连主机系统提供的各种总线和设备到操作系统

其主要功能包括:

  • 动态连接

  • 轻松模块化驱动程序

  • 伪总线

最显著的变化之一是从扁平的临时系统迁移到设备树布局。

在顶层驻留着“根” 设备,所有其他设备都挂载在其上。对于每个体系结构,通常都有一个“根”的单个子节点,其上挂载了诸如主机到 PCI 桥等内容。对于 x86,此“根”设备是“nexus” 设备。对于 Alpha,各种不同的 Alpha 模型具有不同的顶层设备,对应于不同的硬件芯片组,包括lcaapecsciatsunami

在 Newbus 上下文中,设备表示系统中的单个硬件实体。例如,每个 PCI 设备都由一个 Newbus 设备表示。系统中的任何设备都可以有子节点;具有子节点的设备通常称为“总线”。系统中常见总线的示例是 ISA 和 PCI,它们分别管理连接到 ISA 和 PCI 总线上的设备列表。

通常,不同类型总线之间的连接由“桥” 设备表示,该设备通常有一个子节点用于连接的总线。一个例子是PCI 到 PCI 桥,它由父 PCI 总线上的设备pcibN 表示,并有一个子节点pciN 用于连接的总线。此布局简化了 PCI 总线树的实现,允许对顶级和桥接总线使用通用代码。

Newbus 架构中的每个设备都要求其父设备映射其资源。然后,父设备要求其自己的父设备,直到到达 nexus。因此,基本上,nexus 是 Newbus 系统中唯一了解所有资源的部分。

ISA 设备可能希望将其 IO 端口映射到0x230,因此它请求其父设备,在本例中为 ISA 总线。ISA 总线将其传递给 PCI 到 ISA 桥,然后 PCI 到 ISA 桥请求 PCI 总线,PCI 总线到达主机到 PCI 桥,最后到达 nexus。这种向上过渡的优势在于,有空间来转换请求。例如,0x230 IO 端口请求可能会在 MIPS 盒子上由 PCI 桥以0xb0000230 的方式进行内存映射。

资源分配可以在设备树的任何位置控制。例如,在许多 Alpha 平台上,ISA 中断与 PCI 中断分开管理,ISA 中断的资源分配由 Alpha 的 ISA 总线设备管理。在 IA-32 上,ISA 和 PCI 中断都由顶层 nexus 设备管理。对于这两个端口,内存和端口地址空间都由单个实体管理——IA-32 的 nexus 和 Alpha 上的相关芯片组驱动程序(例如 CIA 或 tsunami)。

为了规范对内存和端口映射资源的访问,Newbus 集成了来自 NetBSD 的bus_space API。这些 API 提供了一个单一的 API 来替换 inb/outb 和直接内存读/写。这样做的好处是单个驱动程序可以轻松地使用内存映射寄存器或端口映射寄存器(某些硬件同时支持两者)。

此支持已集成到资源分配机制中。分配资源时,驱动程序可以从资源中检索关联的bus_space_tag_tbus_space_handle_t

Newbus 还允许在专门用于此目的的文件中定义接口方法。这些是在src/sys 层次结构下找到的.m 文件。

Newbus系统的核心是一个可扩展的“基于对象的编程”模型。系统中的每个设备都有一张它支持的方法表。系统和其他设备使用这些方法来控制设备并请求服务。设备支持的不同方法由多个“接口”定义。“接口”只是一组相关的、可以由设备实现的方法。

在Newbus系统中,设备的方法由系统中的各种设备驱动程序提供。当设备在*自动配置*期间连接到驱动程序时,它使用驱动程序声明的方法表。设备稍后可以从其驱动程序*分离*并*重新连接*到具有新方法表的新驱动程序。这允许动态替换驱动程序,这对驱动程序开发很有用。

接口由类似于用于定义文件系统vnode操作的语言的接口定义语言描述。接口将存储在方法文件中(通常命名为foo_if.m)。

示例 1. Newbus 方法
      # Foo subsystem/driver (a comment...)

	  INTERFACE foo

	METHOD int doit {
		device_t dev;
	};

	# DEFAULT is the method that will be used, if a method was not
	# provided via: DEVMETHOD()

	METHOD void doit_to_child {
		device_t dev;
		driver_t child;
	} DEFAULT doit_generic_to_child;

编译此接口时,它会生成一个包含函数声明的头文件“foo_if.h

      int FOO_DOIT(device_t dev);
      int FOO_DOIT_TO_CHILD(device_t dev, device_t child);

还会创建一个源文件“foo_if.c”来配合自动生成的头文件;它包含这些函数的实现,这些函数查找对象方法表中相关函数的位置并调用该函数。

系统定义了两个主要接口。第一个基本接口称为“device”,其中包含与所有设备相关的方法。“device”接口中的方法包括“probe”“attach”“detach”以控制硬件检测,以及“shutdown”“suspend”“resume”用于关键事件通知。

第二个更复杂的接口是“bus”。此接口包含适合具有子设备的设备的方法,包括访问特定于总线的每个设备信息[1]、事件通知(child_detacheddriver_added)和资源管理(alloc_resourceactivate_resourcedeactivate_resourcerelease_resource)的方法。

“bus”接口中的许多方法都在为总线设备的某个子设备执行服务。这些方法通常使用前两个参数来指定提供服务的总线和请求服务的子设备。为了简化驱动程序代码,许多这些方法都有访问器函数,这些函数查找父设备并在父设备上调用方法。例如,可以使用函数bus_teardown_intr(device_t child, …​)来调用方法BUS_TEARDOWN_INTR(device_t dev, device_t child, …​)

系统中的一些总线类型定义了其他接口以提供对特定于总线的功能的访问。例如,PCI总线驱动程序定义了“pci”接口,该接口具有两种方法read_configwrite_config,用于访问PCI设备的配置寄存器。

14.3. Newbus API

由于Newbus API非常庞大,因此本节做了一些努力来记录它。更多信息将在本文档的下一版中提供。

14.3.1. 源代码层次结构中的重要位置

src/sys/[arch]/[arch] - 特定机器体系结构的内核代码位于此目录中。例如,i386体系结构或SPARC64体系结构。

src/sys/dev/[bus] - 特定[bus]的设备支持位于此目录中。

src/sys/dev/pci - PCI总线支持代码位于此目录中。

src/sys/[isa|pci] - PCI/ISA设备驱动程序位于此目录中。PCI/ISA总线支持代码过去存在于FreeBSD版本4.0的此目录中。

14.3.2. 重要的结构和类型定义

devclass_t - 这是指向struct devclass的指针的类型定义。

device_method_t - 这与kobj_method_t相同(参见src/sys/kobj.h)。

device_t - 这是指向struct device的指针的类型定义。device_t表示系统中的设备。它是一个内核对象。有关实现细节,请参见src/sys/sys/bus_private.h

driver_t - 这是一个引用struct driver的类型定义。driver结构是device内核对象的类;它还保存驱动程序专用的数据。

driver_t 实现

	  struct driver {
		KOBJ_CLASS_FIELDS;
		void	*priv;			/* driver private data */
	  };

device_state_t类型,它是一个枚举类型device_state。它包含自动配置过程之前和之后Newbus设备的可能状态。

设备状态 _device_state_t

	  /*
	   * src/sys/sys/bus.h
	   */
	  typedef enum device_state {
		DS_NOTPRESENT,	/* not probed or probe failed */
		DS_ALIVE,		/* probe succeeded */
		DS_ATTACHED,	/* attach method called */
		DS_BUSY			/* device is open */
	  } device_state_t;

上次修改时间:2024年3月9日,作者 Danilo G. Baio