确定设备驱动程序后,将其加载到驱动程序主机进程中 所需的资源。绑定程序决定了它们是否加载 是对驱动程序可绑定到什么设备的描述。绑定程序 使用一种针对特定领域的小型语言定义的,这种语言会被编译成字节码, 都随驱动程序一起分发。
以下是来自 Intel 以太网驱动程序的绑定程序示例:
fuchsia.device.protocol == fuchsia.pci.protocol.PCI_DEVICE;
fuchsia.pci.vendor == fuchsia.pci.vendor.INTEL;
accept fuchsia.pci.device {
0x100E, // Qemu
0x15A3, // Broadwell
0x1570, // Skylake
0x1533, // I210 standalone
0x15b7, // Skull Canyon NUC
0x15b8, // I219
0x15d8, // Kaby Lake NUC
}
绑定编译器会使用绑定程序输出一个 C 头文件,
定义了一个宏 ZIRCON_DRIVER
。ZIRCON_DRIVER
宏包含
将绑定程序放入 ELF 备注所需的编译器指令
部分,以便设备协调器无需对其进行检查
将驱动程序完全加载到其进程中。
ZIRCON_DRIVER
的第二个参数是 zx_driver_ops_t
结构指针
(由 lib/ddk/driver.h
定义,
定义了 init、bind、create 和 release 方法。
init()
会在驱动程序加载到驱动程序主机进程时调用,并允许
任何全局初始化。通常不需要。如果 init()
方法是
但之后失败,驱动程序加载将会失败。
调用 bind()
,为驱动程序提供要绑定的设备。该设备属于
与驱动程序发布的绑定规则匹配。如果 bind()
方法执行成功,
驱动程序必须创建新设备并将其添加为传入设备的子设备
传递给 bind()
方法。如需了解详情,请参阅“设备生命周期”。
针对平台/系统总线驱动程序或代理驱动程序调用 create()
。对于
绝大多数驱动程序,则不需要使用此方法。
在驱动程序卸载前,在其可能装载的所有设备之后调用 release()
在 bind()
和其他位置创建的文件已被销毁。目前,此方法
never 调用过。驱动程序在加载后会在驱动程序主机的生命周期内保持加载
过程。
设备生命周期
在驱动程序主机进程中,设备以 zx_device_t
结构树的形式存在,
对驱动程序不透明它们是使用 device_add()
创建的,
驱动程序为其提供 zx_protocol_device_t
结构。由
此结构中的函数指针是“设备操作”。通过
device.h
中定义的各种结构和函数
device_add()
函数会创建一个新设备,并将其作为子级添加到
提供的设备该父级设备必须是已通过测试的设备
传入到设备驱动程序的 bind()
方法中,
由同一设备驱动程序创建。
device_add()
有一个附带效应,就是系统会添加新创建的设备
全局设备文件系统(由设备协调器维护)。如果
尚未实现 init()
钩子,则系统将立即
可通过在 devfs 中打开其节点进行访问。
系统会在 device_add()
之后调用 init()
钩子。这对于
不需要进行扩展的初始化或探测的驱动程序,
直到成功发布其设备(然后静默地删除
否则就会出现这种错误)。驾驶员应在其后调用 device_init_reply()
。
已完成初始化。此回复不一定要是
从 init()
钩子调用该方法。该设备将保持不可见状态,
在此之前,我们保证不会将其移除。
设备属于参考计数。当驱动程序创建
具有 device_add()
的设备,以及当设备被远程进程打开时
通过设备文件系统访问
从调用 device_init_reply()
或 device_add()
调用之时起
未实现的 init()
钩子时,可由
驱动程序主机。
在设备上调用 device_async_remove()
时,系统会安排移除
设备及其后代节点的名称。
设备的移除过程包括四个部分:运行设备的 unbind()
钩子、
从设备文件系统中移除设备,从而丢弃获取的参考
由 device_add()
运行并运行设备的 release()
钩子。
调用 unbind()
方法时,这会告知驱动程序应启动的驱动程序
关闭设备,并在完成解除绑定后调用 device_unbind_reply()
。
解除绑定也是 FIDL 交易的硬屏障。
FDF 将不允许任何新的 FIDL 交易或连接
调用“Unbind”时创建的任何关联由驾驶员负责
关闭或回复其
取消绑定钩子(如果它们处理 FIDL 消息)。
这是一个可选的钩子。如果未实现,则被视为 device_unbind_reply()
调用该方法。调用 device_unbind_reply 时,
所有 FIDL 连接都将终止。
由于子设备的 unbind()
方法
则父级设备(已完成
解除绑定)可能会继续接收设备方法调用或协议方法
来代表该子发布商进行调用。建议在完成解除绑定之前
父级设备应安排这些方法返回错误,以便
儿童在完成移除之前发出的调用不会启动更多
工作或引发意外交互。
只有在完成驱动程序创建操作后,系统才会调用 release()
方法
解除绑定,该设备的所有打开的实例都已关闭,
并且该设备的所有子节点均已解除绑定并释放。这个
是驱动程序销毁或释放任何相关资源的最后机会
与设备互动参考该设备的zx_device_t
无效
在 release()
返回后返回。调用任何设备方法或协议方法
超过此时限后,通过家长设备获取的协议是非法的,
可能会导致崩溃。
拆解序列示例
为了说明 unbind()
和 release()
在拆解过程中的工作原理,
下例说明了 USB WLAN 驱动程序通常会如何处理它。简而言之,
unbind()
调用序列自上而下,而 release()
序列自下而上。
请注意,这只是一个示例。这可能与真正的 WLAN 驱动程序并不完全一致 具体表现
假设 WLAN 设备作为 USB 设备插入,并且已配置 PHY 接口 在 USB 设备下创建除 PHY 接口外,还有 2 个 MAC 接口 已在 PHY 接口下创建。
+------------+
| USB Device | .unbind()
+------------+ .release()
|
+------------+
| WLAN PHY | .unbind()
+------------+ .release()
| |
+------------+ +------------+
| WLAN MAC 0 | | WLAN MAC 1 | .unbind()
+------------+ +------------+ .release()
现在,我们拔下这个 USB WLAN 设备的电源线。
USB XHCI 检测到移除操作并调用
device_async_remove(usb_device)
。这会导致系统调用 USB 设备的
unbind()
。 完成解除绑定后,它会调用device_unbind_reply()
。
usb_device_unbind(void* ctx) {
// Stop interrupt or anything to prevent incoming requests.
...
device_unbind_reply(usb_dev);
}
- 当 USB 设备完成解除绑定后,系统会调用 WLAN PHY 的
unbind()
。 完成解除绑定后,它会调用device_unbind_reply()
。
wlan_phy_unbind(void* ctx) {
// Stop interrupt or anything to prevent incoming requests.
...
device_unbind_reply(wlan_phy);
}
- 当 wlan_phy 完成解绑定后,系统会对其所有子级调用 unbind() (wlan_mac_0、wlan_mac_1)。
wlan_mac_unbind(void* ctx) {
// Stop accepting new requests, and notify clients that this device is offline (often just
// by returning a ZX_ERR_IO_NOT_PRESENT to any requests that happen after unbind).
...
device_unbind_reply(iface_mac_X);
}
移除设备的所有客户端并且该设备不再有子设备后 其 refcount 将达到零,并且将调用其 release() 方法。
调用 Wi-Fi MAC 0 和 1 的
release()
。
wlan_mac_release(void* ctx) {
// Release sources allocated at creation.
...
// Delete the object here.
...
}
- wlan_phy 没有打开的连接,但仍有子设备(wlan_mac_0 和 wlan_mac_1)。 在这两者都被释放后,其 refcount 最终将变为零,并且其 release() 方法。
wlan_phy_release(void* ctx) {
// Release sources allocated at creation.
...
// Delete the object here.
...
}
- 当 USB 设备现在没有子设备或打开的连接时,系统会调用其
release()
。