引言
设备驱动通常存在于单个内核子系统中。然而,有时开发人员需要处理此模块之外的功能。例如,一个暴露以太网和 RDMA 功能的网络接口卡 (NIC)。只有一个硬件,但有两个驱动用于这两个功能。这些驱动程序需要在各自的子系统内工作,但它们也必须共享对相同硬件的访问方法。当前内核中没有将这些驱动程序连接在一起的标准方法,因此开发人员发明了ad-hoc方法来处理它们之间的交互。最近,Dave Ertman 发布了一个补丁集,介绍了一种称为“auxiliary bus”的新型总线,以解决这个问题。
Complex devices
Linux 已经包含了许多用于 multi-function 设备的驱动。支持它们的方法之一是Multi-Function Devices (MFD) 子系统。它处理多个独立设备“粘合”到一个硬件块的硬件,该硬件可能包含一些共享资源。 MFD 允许直接或使用公共总线访问设备寄存器。在第二种情况下,它可以方便地多路复用地访问Inter-Integrated Circuit (I2C) 或Serial Peripheral Interface (SPI) 总线。由于 MFD sub-device 是独立的,因此 MFD 驱动程序不共享公共状态信息。
Ertman 地址的设备不太适合 MFD 模型。使用 auxiliary bus 的设备提供独立硬件设备功能的子集。它们不会为每个功能公开单独的寄存器集合;因此它们不能被devicetrees 描述或被 ACPI 发现。他们的驱动程序需要共享对硬件的访问方式。涉及所有子功能(如电源管理)的事件需要由被有驱动程序正确处理。这些设备通常有特定的处理前,处理前运行firmware 并通过消息与主机系统(和 Linux 驱动程序)通信。可见的function可能无法提前知道,因此必须在运行时发现。
auxiliary bus系列中的 documentation patch引用了许多示例。 Sound Open Firmware (SOF) 驱动程序与单个设备交互,这些设备暴露HDMI 输出、麦克风、扬声器、测试和调试挂钩等接口。同时实现以太网和 RDMA 的 NIC 可能需要一个支持通用部分功能的驱动程序,然后特定的以太网和 RDMA 驱动程序可以在此基础上实现特定的部分。
当前的内核没有通用的方法来描述这种设备的驱动程序之间的依赖关系。该问题的解决方案可能是将secondary驱动程序附加到primary 驱动程序;这正是auxiliary bus所实现的。
Auxiliary 设备和驱动
补丁集引入了两个主要概念:“auxiliary device”和“辅助驱动程序”。这些实现了主要和次要驱动程序之间的关系。主驱动程序维护设备状态,分配和管理所有共享数据。它还会在关闭时取消注册所有辅助驱动程序。相反,次要驱动程序处理与他们正在为其实现设备的特定子系统的交互。
每个主驱动可以为secondary 驱动公开许多功能(设备)。只有一个secondary驱动程序可以附加到这些功能中的一个。
主驱动创建了一个auxiliary device,用struct auxiliary_device表示:
name 和 id 的组合必须是唯一的;完整的设备名称是模块名称和这两个字段的组合,由点 (.) 连接。这会产生类似 modname.device_name.id 的结果。
开发人员将此结构嵌入到主驱动程序的device结构中,其中包含主驱动程序和secondary 要驱动程序之间通信所需的所有共享数据。他们还可以添加补充callback。
初始化主驱动程序的序列包含两个步骤。第一个是调用auxiliary_device_init():
它验证参数并在需要时返回错误代码;在这种情况下,应中止设备的初始化。如果第一步调用成功,则第二步用已经初始化的设备调用带auxiliary_device_add() 的宏;这将设置设备名称并注册设备本身。
注销过程也有两个步骤,包括对auxiliary_device_uninit() 的调用(已成功调用auxiliary_device_init() )和auxiliary_device_delete()。这些函数具有以下原型:
这种两步方法是为了响应对补丁集早期版本的评论而实现的。它允许驱动程序在auxiliary_device_init()和auxiliary_device_add()之间分配自己的数据,并有可能在发生故障时正确释放它。
将连接到主驱动程序的secondary设备由struct auxiliary_driver 表示:
该结构包括许多用于管理设备生命周期的callback,以及包含驱动可以用来绑定的设备名称的 id_table。所有回调都接收指向父级的auxiliary_device的指针,从而允许访问共享数据。
secondary 设备使用auxiliary_driver_register()设置:
此函数需要填充probe() 和id_table。成功后,它会为任何匹配的设备调用probe() 回调调用。secondary 设备可以使用 container_of() 和auxiliary_device结构访问共享数据。
注销驱动程序时,开发人员应调用auxiliary_driver_unregister():
第一批使用者
连同auxiliary bus实现,Ertman 发布了对 SOF 驱动程序的更改。修改后的驱动程序使用此infrastructure 结构来实现 test driver 和probe driver,从而允许创建新的虚拟音频设备,该设备可以接入管道并允许随时收听。
另一个使用者可以在网络子系统中找到; Leon Romanovsky 发布了对 mlx5 驱动的转换以使用auxiliary bus。新的驱动程序为一台物理设备创建网络、VDPA 和 RDMA 驱动程序。这些更改允许删除一堆自定义驱动程序代码。 Parav Pandit 随后使用此功能来实现设备子功能。
补丁集以目前的形式进行了第四次迭代,并见证了许多早期以辅助和虚拟总线的名义进行的迭代。auxiliary bus补丁集的开发需要时间,并且它在其他工作中产生了依赖性。这造成了相当大的压力,使其何入upstream,并导致了一些人对名单的推动。为了推动事情的发展,丹·威廉姆斯重新发布了补丁集,并表示“对我和其他几个利益相关者来说看起来不错”。经过 Greg Kroah-Hartman 的审查,auxiliary bus代码被合并到 5.11 内核版本的主线中。