Fuchsia 驱动程序是在驱动程序主机中动态加载的共享库 进程。驱动程序的加载过程由 驱动程序管理器。请参阅 设备型号 详细了解驱动程序主机、驱动程序管理器以及驱动程序和设备 生命周期
目录结构
驱动程序可以在源代码树的 driver
子目录下找到
部分指定的
源代码布局文档。大多数人
Fuchsia 驱动程序位于 //src/devices/ 下。它们会按组
进行扩缩。驱动程序协议在
ddk/include/lib/ddk/protodefs.h.对于
一个 USB 以太网驱动程序
//src/connectivity/ethernet/drivers/
而不是//src/devices/usb/drivers/
实现了以太网协议不过,实现 USB 堆栈的驱动程序
位于 //src/devices/usb/drivers/ 中,因为它们
实现 USB 协议。
在驱动程序的 BUILD.gn
中,应该有一个 fuchsia_driver_component
目标。
为了让驾驶员显示在 /boot/driver
下,应包含它
如位于相关板文件中的 board_bootfs_labels
列表下,
//boards.要让它显示在 /system/driver
中,它应该
添加到带有 driver_package
构建目标的系统软件包中,
然后被 //boards
下的相关板级文件引用。驱动程序管理器
首先在 /boot/driver
中查找可加载驱动程序,然后在 /system/driver/
中查找可加载的驱动程序。
创建新的驱动程序
您可以使用 创建工具。只需运行以下命令:
fx create driver --path <PATH> --lang cpp
这将创建包含空驱动程序的目录 <PATH>
,其中
<PATH>
的最后一部分是驱动程序名称和 GN 目标名称。在此之后
命令,则需要执行以下步骤:
- 在该文件中添加
fuchsia_driver_component
或driver_package
构建目标 将司机加入系统的正确位置。 - 对于打包的驱动程序,应将
driver_package
构建目标添加到 将//boards
或//vendor/<foo>/boards
中的相关开发板文件复制到xx_package_labels
GN 参数。 - 对于启动驱动程序,应添加
fuchsia_driver_component
构建目标 到//boards
或//vendor/<foo>/boards
中的相关开发板文件,board_bootfs_labels
GN 参数。 - 在
<PATH>:tests
build 目标中添加tests
build 目标以获取 包含在 CQ 中的测试。 - 在
meta/<NAME>.bind
中添加适当的绑定规则。 - 在
meta/<NAME>-info.json
中添加驾驶员信息。该文件必须包含short_description
和areas
至少与以下列出的其中一个区域匹配://build/drivers/areas.txt
。 - 为驱动程序编写功能。
声明驱动程序
驱动程序至少应包含驱动程序声明,并实现
bind()
驱动程序操作。
驱动程序管理器成功加载驱动程序并将其绑定到设备
为设备寻找匹配的驱动程序。驱动程序声明设备
通过绑定规则兼容,该规则应放在 .bind
文件中
旁边。绑定编译器会编译这些规则,并创建
驱动程序声明宏,该宏将这些规则包含在 C 头文件中。通过
以下绑定规则声明了
AHCI 驱动程序:
using fuchsia.pci;
using fuchsia.pci.massstorage;
fuchsia.BIND_PROTOCOL == fuchsia.pci.BIND_PROTOCOL.DEVICE;
fuchsia.BIND_PCI_CLASS == fuchsia.pci.BIND_PCI_CLASS.MASS_STORAGE;
fuchsia.BIND_PCI_SUBCLASS == fuchsia.pci.massstorage.BIND_PCI_SUBCLASS_SATA;
fuchsia.BIND_PCI_INTERFACE == 0x01;
fuchsia.BIND_COMPOSITE == 1;
这些绑定规则规定,驱动程序通过 BIND_PROTOCOL
绑定到设备
属性,与 pci
命名空间中的 DEVICE
和给定 PCI 匹配
类/子类/接口。pci
命名空间是从 fucnsia.pci
导入的
。如需了解详情,请参阅绑定
文档。
要生成包含这些绑定规则的驱动程序声明宏,
是相应的 bind_rules
构建目标。这应该会声明依赖项
这对应于“使用”语句。
driver_bind_rules("bind") {
rules = "meta/ahci.bind"
bind_output = "ahci.bindbc"
deps = [
"//src/devices/bind/fuchsia.pci",
"//src/devices/bind/fuchsia.pci.massstorage",
]
}
驱动程序现在可以包含生成的标头,并使用
后续宏。"zircon"
是供应商 ID,"0.1"
是驱动程序版本。
#include <lib/ddk/binding_driver.h>
...
ZIRCON_DRIVER(ahci, ahci_driver_ops, "zircon", "0.1");
PCI 驱动程序会发布匹配的 具有以下属性的设备:
zx_device_str_prop_t pci_device_props[] = {
ddk::MakeStrProperty(bind_fuchsia::PCI_VID,
static_cast<uint32_t>(device.info.vendor_id)),
ddk::MakeStrProperty(bind_fuchsia::PCI_DID,
static_cast<uint32_t>(device.info.device_id)),
ddk::MakeStrProperty(bind_fuchsia::PCI_CLASS,
static_cast<uint32_t>(device.info.base_class)),
ddk::MakeStrProperty(bind_fuchsia::PCI_SUBCLASS,
static_cast<uint32_t>(device.info.sub_class)),
ddk::MakeStrProperty(bind_fuchsia::PCI_INTERFACE,
static_cast<uint32_t>(device.info.program_interface)),
ddk::MakeStrProperty(bind_fuchsia::PCI_REVISION,
static_cast<uint32_t>(device.info.revision_id)),
ddk::MakeStrProperty(bind_fuchsia::PCI_TOPO, pci_bind_topo),
};
目前,绑定变量和宏是在
lib/ddk/binding.h.在不久的将来,
所有节点属性都将由绑定库定义,例如 fuchsia.pci
库。如果您要引入新的设备类别,则可能需要
为绑定标头以及
bind 库。
节点属性是 32 位值。如果您的变量值要求大于
一个 32 位值,将它们拆分为多个 32 位变量。以 ACPI 为例
HID 值,长度为 8 个字符(64 位)。它分为
BIND_ACPI_HID_0_3
和BIND_ACPI_HID_4_7
。完成迁移后
库之后,您就可以使用其他数据类型,例如字符串、
更大的数字和布尔值。
您可以在 bind_rules
构建规则中指定 disable_autobind = true
,以
停用自动绑定行为。在这种情况下,可以将驱动程序绑定到
设备使用 fuchsia.device.Controller/Bind
FIDL 调用。
驱动程序绑定
当驱动程序与设备匹配时,系统会调用驱动程序的 bind()
函数。一般
驱动程序将初始化设备所需的任何数据结构
初始化硬件。它不应执行任何耗时的
因为它是从驱动程序主机的
RPC 线程,并且在此期间它将无法处理其他请求。
相反,它应该生成一个新线程来执行冗长的任务。
驱动程序不应对硬件状态做任何假设,
bind()
。它可能需要重置硬件或以其他方式确保采用
状态。因为系统会重新生成驱动程序,从驾驶员崩溃中恢复
主机,则在调用 bind()
时,硬件可能处于未知状态。
bind()
通常会有四种结果:
驱动程序确定设备受支持,无需采取任何操作 因此使用 C 或 C 版本发布具有
device_add()
的新设备ddk::Device::DdkAdd()
在 DDKTL C++ 封装容器库,并返回 `ZX_OK.驱动程序确定,即使绑定规则匹配,设备 (可能是因为检查了硬件版本位或其他原因) 返回一个错误。
驱动程序需要在设备准备就绪之前进行进一步初始化,或者 它确定自己能支持它,因此发布了一个隐形设备, 实现
init()
钩子,以及 启动一个线程以继续工作,同时将ZX_OK
返回到bind()
。 该线程最终会调用device_init_reply()
或 C 语言ddk::InitTxn::Reply()
(在 DDKTL C++ 封装容器库。在 收到回复。状态表明ZX_OK
是否能够 已成功初始化设备,并且该设备应处于可见状态;或者 错误,指示应移除设备。驱动程序表示具有 0 个子项的总线或控制器, 动态地出现或消失。在本示例中,它应该发布 然后动态发布 子项(下游驱动程序将绑定到的子项),表示 这辆公共汽车。示例:AHCI/SATA、USB 等
在设备被添加并被系统设为可见后,该设备就可供 以及通过兼容的驱动程序进行绑定。
Banjo 协议
驱动程序用于为设备提供一组设备操作和可选的协议操作。 设备操作实现设备生命周期方法和外部接口, 由其他用户空间应用和服务调用的设备。 协议操作实现设备的进程内协议,由 将其他驱动程序加载到同一驱动程序主机中。
您可以在 device_add_args_t
中为设备传递一组协议操作。如果
设备支持多种协议,请实现 get_protocol()
设备操作。答
设备只能有一个协议 ID。协议 ID 对应类
设备发布在 devfs 下。
驱动程序操作
驱动程序通常通过处理来自子级驱动程序的客户端请求来运行 或其他进程满足这些要求 与硬件连接(例如,通过 MMIO)或与其父项通信 设备(例如,将 USB 事务加入队列)。
来自驱动程序主机外部进程的外部客户端请求由 儿童驾驶员通常在同一进程中,由班卓琴扮演 与设备类对应的协议。驾驶员之间的请求应 使用班卓琴协议而不是设备操作。
设备可通过调用
device_get_protocol()
。
设备中断
设备中断通过中断对象来实现,中断对象是一种
内核对象。驱动程序从
通过设备协议方法指定父设备返回的句柄将绑定到
相应设备中断(如父驱动程序所定义)。对于
例如,PCI 协议会为 PCI 子级实现 map_interrupt()
。答
驱动程序应生成一个线程来等待中断句柄。
内核将根据中断情况自动遮盖和取消遮盖中断,
具体取决于中断是边缘触发还是
。对于音频级触发的硬件中断,
zx_interrupt_wait()
将进行遮盖
在返回之前中断该中断,并在再次调用时取消屏蔽中断
。对于边缘触发的中断,中断保持未屏蔽状态。
中断线程不应执行任何长时间运行的任务。对于 执行冗长的任务,请使用工作线程。
您可以使用以下代码发出中断句柄
zx_interrupt_trigger()
已开启
槽 ZX_INTERRUPT_SLOT_USER
从 zx_interrupt_wait()
返回。这是
需要在驱动程序清理期间关闭中断线程。
FIDL 消息
非驱动程序进程
在
FIDL 语言。每台设备
实现零个或多个 FIDL 协议,
客户端。驱动程序可通过
message()
钩子。只有非驱动程序组件才能访问这些内容
DevFs 的方法。
其他进程中的驱动程序
如果驱动程序需要在单独的进程中与驱动程序通信, 它必须改为托管传出目录,类似于 组件,这些组件应托管子驱动程序将访问的所有 FIDL 协议 。
协议操作与 FIDL 消息
协议操作定义设备的进程内 API。FIDL 消息定义 用于进程外通信的 API。如果函数是 同一进程中的其他驱动程序会调用这些函数。司机应拨打 协议操作,以利用这些函数。
隔离设备
使用 DEVICE_ADD_MUST_ISOLATE
添加的设备将生成新的驱动程序主机。
设备必须具有用于托管 FIDL 的相应外发目录
协议绑定到设备的驱动程序将加载到新的
驱动程序主机,并提供了将
父级驱动程序提供的传出目录。
驾驶员权利
虽然驱动程序在用户空间进程中运行,但它们对 权限。不允许驱动程序访问文件系统 包括 devfs这意味着驱动程序无法与任意设备交互。如果 您的驱动程序需要执行此操作,不妨考虑改为编写服务组件。对于 虚拟控制台由 virtcon 组件。
特权操作,例如 zx_vmo_create_contiguous()
和
zx_interrupt_create
需要
根资源句柄。除了
系统驱动程序(x86 系统上的 ACPI 和
platform)。设备应
请求其父级为其执行此类操作。请与
父驱动程序的协议未处理此用例的情况。
同样,不允许驱动程序请求任意 MMIO 范围、中断 或 GPIOPCI 和平台等总线驱动程序只会返回资源 与子设备相关联。
高级主题和提示
初始化需要很长时间
如果您的设备初始化时间很长,该怎么办?我们讨论
null_bind()
函数,我们表示,如果返回成功,
驱动程序管理器,告知该驱动程序现在已与设备关联。我们不能支出
在绑定函数中会耗费大量时间;我们需要先完成
然后发布它,就大功告成了
但是,您的设备可能需要执行冗长的初始化操作, 以:
- 枚举硬件点
- 加载固件
- 协商协议
依此类推,这可能需要很长时间
您可以将设备发布为“隐形”方法是实现设备 init()
钩子。在通过 device_add()
添加设备后,系统会运行 init()
钩子,
可用于安全访问设备状态和生成工作线程。
该设备将保持不可见状态,并保证在
系统会调用 device_init_reply()
,此操作可通过任何线程完成。这符合
绑定函数的要求,但任何人都无法使用您的设备
(因为还没有人知道它,因为它还没有显示。)现在,您的设备
可以在后台线程上执行长操作。
当您的设备准备好处理客户端请求时,请调用 device_init_reply()
这会使其出现在路径名空间中。
节能
您的设备有两条宣传信息,suspend()
和resume()
可供使用
以支持省电或其他资源节约功能
两者都接受设备上下文指针和 flag 参数,但 flag 参数 只在挂起情况下使用。
标志 | 含义 |
---|---|
DEVICE_SUSPEND_FLAG_REBOOT |
驱动程序应自行关闭,以便为重新启动或关闭机器做好准备 |
DEVICE_SUSPEND_FLAG_REBOOT_BOOTLOADER |
? |
DEVICE_SUSPEND_FLAG_REBOOT_RECOVERY |
? |
DEVICE_SUSPEND_FLAG_POWEROFF |
驾驶员应自行关闭,准备关闭电源 |
DEVICE_SUSPEND_FLAG_MEXEC |
驱动程序应自行关闭以准备软重启 |
DEVICE_SUSPEND_FLAG_SUSPEND_RAM |
驱动程序应进行适当安排,以便可以从 RAM 重新启动 |
参考文档:支持函数
本部分列出了我们为驱动程序提供的支持函数。
访问器函数
作为第一个参数传递给驱动程序协议的上下文块 函数是一种不透明的数据结构。这意味着,要访问 您需要调用一个存取器函数:
函数 | 用途 |
---|---|
device_get_name() |
检索设备名称 |
管理功能
以下函数用于管理设备:
函数 | 用途 |
---|---|
device_add() |
将设备添加到家长账号 |
device_async_remove() |
安排移除设备及其所有子项 |