驱动程序和节点

在 Fuchsia 中,每个驱动程序都绑定到一个节点。节点是驱动程序框架的主要构建块 驱动程序框架。节点可以被视为硬件或虚拟设备,也可以是硬件设备的一部分。 例如,GPIO 节点可以表示连接到 GPIO 控制器的单个 GPIO 引脚,而 RAM 磁盘节点可以表示虚拟磁盘,而不是真正的硬件设备。

驱动程序在绑定到节点后,可以创建子节点。因此,节点会形成一个有向无环图,表示 Fuchsia 系统中的所有已知硬件和虚拟设备。

节点拓扑图,显示了从根节点到特定 SPI 设备节点的层次结构,以及绑定到节点的驱动程序。

图 1. 节点拓扑,其中绿色圆圈是表示设备的节点,蓝色框是驱动程序。

节点属性

节点具有以下属性:

  • 节点属性:描述 哪些驱动程序可以绑定到节点的键值对。键可以是整数或字符串类型。值可以是整数、字符串、布尔值或枚举类型。
  • 功能:驱动程序 绑定到节点时提供给驱动程序的 FIDL 功能。这些功能可以是进程内功能,也可以是进程外功能。
  • 符号:键值对,其中键是字符串, 值是虚拟地址。驱动程序使用这些符号将指针 传递给子驱动程序,以进行进程内通信。只有当驱动程序与其父级位于同一 驱动程序宿主中时,才会向该 驱动程序提供 符号,否则驱动程序会使用 FIDL 调用进行通信。

创建子节点的驱动程序可以为新节点分配节点属性、功能和符号。

节点功能

节点表示一组资源,而这些资源又表示 Fuchsia 系统中的硬件或虚拟设备。当父级驱动程序创建新节点时,父级驱动程序会指定与该节点关联的功能。 当子驱动程序绑定到该节点时,驱动程序框架会将与该节点关联的功能路由到子驱动程序的传入命名空间。

不过,驱动程序的传入命名空间中的并非所有功能都来自父级驱动程序。某些功能可能来自系统中的非驱动程序组件。

节点拓扑

Fuchsia 的 驱动程序管理器 维护一个节点拓扑,用于 描述 Fuchsia 系统中节点(表示设备)之间的父子关系 。从根节点开始,绑定到根节点的驱动程序会创建子节点 - 节点可以拥有的子节点数量没有限制。绑定到这些子节点的驱动程序通常会创建自己的子节点。因此,这些节点会形成一个以有向无环图表示的单个节点拓扑,用于描述在 Fuchsia 系统中发现的所有硬件和虚拟设备。

节点拓扑图,显示了具有子设备的 USB 总线节点,突出显示了绑定到设备节点的 USB 键盘驱动程序。

图 2. USB 总线拓扑示例。

在上面的示例中,USB 总线驱动程序 (usb-bus-driver) 绑定到表示 USB 总线的节点 (usb-bus)。然后,该驱动程序会为系统中发现的每个新 USB 设备创建一个子节点。根据节点的属性,每个 USB 设备节点都可以绑定一个特定的 USB 驱动程序。 例如,USB 键盘驱动程序 (usb-keyboard-driver) 绑定到其中一个 USB 设备节点。由此,我们可以猜测 usb-device-2 节点可能表示通过 USB 端口连接到系统的键盘设备。

与组件拓扑的比较

与驱动程序框架类似,组件框架也有自己的 拓扑,组件可以在其中声明子组件。不过,节点拓扑和组件拓扑是分开的。第一个原因是,并非所有节点都可能绑定了驱动程序。这意味着,这些未绑定的节点与组件拓扑中显示的任何特定组件都不关联。事实上,在系统运行时,节点拓扑中的许多节点通常处于未绑定状态(即与驱动程序不匹配)。其次, 在节点拓扑中,一个节点可能具有多个父节点(请参阅 复合节点),但在组件 拓扑中不允许这样做。

从组件框架的角度来看,驱动程序的拓扑似乎是扁平的。驱动程序框架将驱动程序保留在三个扁平的组件集合中:启动集合、软件包集合和宇宙软件包集合。在组件拓扑中,所有驱动程序组件在其父组件(即驱动程序管理器)下都显示为彼此的同级。

组件拓扑图,显示了在 driver_manager.cm 下组织成集合的驱动程序和驱动程序主机。

图 3:组件拓扑,显示了三个集合中的驱动程序组件

不过,与组件拓扑中的其他组件不同,驱动程序框架 负责设置驱动程序组件的 moniker。驱动程序框架会根据驱动程序在节点拓扑中的位置(而不是在组件拓扑中的位置)来命名驱动程序组件的 moniker。例如,PCI 驱动程序的组件 moniker 可能类似于 /bootstrap/boot-drivers:root.sys.platform.pci.00_14_0。 此组件 moniker 表示 PCI 驱动程序的以下节点拓扑:root -> sys -> platform -> pci -> 00_14_0。组件 moniker 表明,此 PCI 驱动程序组件在组件拓扑中仅位于第 2 层,但绑定到 PCI 驱动程序的节点 (00_14_0) 在节点拓扑中位于第 5 层。

节点生命周期

驱动程序在 Fuchsia 系统中的生命周期与其绑定的节点的生命周期相关联。

驱动程序可以对其控制的 Node 对象执行以下生命周期操作:

  • 创建子节点,具有特定节点属性和 功能。
  • 删除节点,这会导致驱动程序管理器清理 节点及其在节点拓扑中的后代。

节点创建

当对现有节点调用 AddChild FIDL 方法时,系统会创建节点。拥有现有节点的驱动程序或有权访问节点对象的其他驱动程序都可以触发节点上的子节点创建。

当驱动程序创建子节点时,该驱动程序可以执行以下操作:

  • 为子节点提供确定哪些 驱动程序可以绑定到节点的属性
  • 为子节点提供稍后可供绑定到节点的驱动程序使用的功能
  • 保留子节点的 NodeController 对象,以便 父级驱动程序停止子节点。

当驱动程序创建子节点时,可以选择拥有该节点。如需拥有子节点,驱动程序会将额外的实参添加到 AddChild 调用中。如果驱动程序请求拥有子节点,驱动程序框架不会将新驱动程序绑定到该节点。不过,如果子节点不归驱动程序所有,驱动程序框架会尝试查找可以绑定到新节点的其他驱动程序。

当驱动程序创建子节点时,该驱动程序还可以保留 NodeController子节点的 对象。驱动程序 可以随时使用此对象(通过调用 Remove)停止子节点 ,这会导致停止绑定到该节点的驱动程序。当子节点绑定到驱动程序时,驱动程序 会通过此对象接收 OnBind FIDL 事件。

节点移除

当发生以下任一事件时,系统会移除节点:

  • 绑定到节点的驱动程序会删除其 Node 对象。
  • 节点(例如父节点)会对目标节点的 NodeController 对象调用 Remove 方法。
  • 组件框架会关闭驱动程序组件,因此会移除绑定到该节点的驱动程序。

在节点拓扑中,系统会先从底部移除节点。如果节点绑定到驱动程序,则必须先停止驱动程序,然后才能移除节点。如果要移除的节点位于节点拓扑的中间,驱动程序框架会确保在移除目标节点之前,所有子驱动程序都已停止,并且所有子节点都已移除。

复合节点

当驱动程序希望将自身绑定到多个父节点时,驱动程序管理器会创建一个复合节点。在这种情况下,驱动程序管理器会将复合节点作为子节点添加到每个父节点。然后,驱动程序管理器会将驱动程序绑定到复合节点。

复合节点具有复合绑定规则,这使驱动程序能够为每个父节点指定一组不同的绑定规则。复合节点的功能是从每个父节点转发的,这使驱动程序能够访问父节点的组合功能。不过,绑定属性不会从父节点转发到复合节点。在创建复合节点的情况下,驱动程序框架会响应驱动程序绑定到多个父节点的操作来创建复合节点,因此它已经知道将绑定到该节点的驱动程序。

可以创建具有复合节点的摄像头,如下所示:

示意图:显示了一个复合节点(“camera”),该节点具有两个绑定到驱动程序的父节点(“gpio-enable”和“pci-device”)。

图 4. 复合节点的表示形式。

在上面的示例中,Fuchsia 系统中发现了一个摄像头设备。此摄像头设备有一个用于开启摄像头的 GPIO 引脚和一个用于将图片数据从摄像头中传输出来的 PCI 设备。驱动程序索引 会将此摄像头设备与名为 camera-driver 的驱动程序进行匹配。此驱动程序具有复合绑定规则,表明它希望拥有两个父节点:一个用于 GPIO 设备,另一个用于 PCI 设备。驱动程序管理器会创建一个名为 camera 的复合节点,并将该节点作为子节点添加到 gpio-enable 节点和 pci-device 节点。然后,父节点将其功能转发到复合节点。最后,驱动程序管理器会将 camera-driver 驱动程序绑定到复合节点。

图示:具有多个父节点的相机控制器驱动程序的复杂绑定拓扑。

图 5. 一个真实示例,展示了摄像头控制器驱动程序的复杂绑定拓扑