第5章 平台设备驱动
到目前为止,你已经将设备驱动作为可加载的驱动模块进行了构建,该模块在系统运行期间被加载。字符设备驱动已经实现,并通过用户态应用程序进行了彻底的测试。在下一个任务中,会将字符设备驱动转换为平台设备驱动。在嵌入式系统中,设备通常并不通过总线进行连接。通过总线进行连接允许对这些设备进行枚举或者热插拔。
但是,你仍然希望所有这些设备成为设备模型的一部分。对于这些设备来说,必须对其进行静态描述,而不是对其进行动态检测:
1. 就像在一些旧的、不基于设备树的ARM平台所做的那样,通过将platform_device
数据结构直接实例化来实现。其定义在单板或SoC特定代码中完成。
2. 在某些体系结构中使用的在设备树中的硬件描述文件。硬件设备驱动与.dts文件中描述的物理设备进行匹配。当匹配过程完成后,驱动程序的probe()
函数被调用。驱动程序代码中,必须包含.of_match_table
以允许该匹配过程顺利进行。
在不可探测设备中,大量设备均直接属于片上系统,如UART控制器、以太网控制器、SPI控制器、图形或音频设备等。在Linux内核中,有一种名为平台总线的特殊总线被构建,以处理这样的设备。它支持处理平台设备的平台设备驱动。它像任何其他总线(USB、PCI)那样工作,但是设备是被静态枚举而不是被动态发现。
每个平台驱动程序负责在设备模型核心中实例化并注册platform_driver
数据结构实例。平台驱动程序遵循标准的驱动程序模型规范,其中发现/枚举过程在驱动程序之外处理,并且驱动程序提供probe()
和remove()
方法。它们支持使用标准规范的电源管理和关机通知。platform_driver
数据结构的最重要成员如下所示:
在platform_driver
数据结构中,你可以看到一个函数指针变量,该变量指向名为probe()
的函数。当总线驱动程序对设备和设备驱动程序进行匹配时,将调用probe()
函数。probe()
函数负责初始化设备,并将其注册到合适的内核框架中:
1. probe()
函数使用一个指向设备数据结构的指针作为函数参数(例如,struct pci_dev *
,struct usb_dev *
,struct platform_device *
,struct i2c_client *
)。
2. 它也初始化设备,映射I/O内存,分配缓冲区,注册中断处理程序、定时器等。
3. 它也将设备注册到特定的框架(例如网络框架、杂项框架、串口框架、输入框架、工业设备框架)中。
suspend()
/resume()
函数被设备所使用,这两个函数支持低功耗管理功能。
负责平台设备的平台驱动程序应当使用platform_driver_register(struct platform_driver * drv)
函数将驱动程序注册到平台核心。在模块init()
函数中注册平台驱动程序,并在模块exit()
函数中注销平台驱动程序,如下所示:
也可以使用module_platform_driver(my_platform_driver)
宏。对于那些在模块init()
/ exit()
函数中不用执任何特殊事情的驱动来说,这是一个可用的辅助宏。这消除了很多重复代码。每个模块只能使用该宏一次,调用该宏也会替换module_init()
和module_exit()
。