[导读] Linux设备林林总总,嵌入式开发一个绕不开的话题就是设备驱动开发,在做具体设备驱动开发之前,有必要对Linux设驱动模型有一个相对清晰的认识,将会帮助驱动开发,明白具体驱动接口操作符相应都做些什么。
个人对于驱动模型的理解概括起来就是一句话:利用面向对象编程思想,实现设备分层管理软件体系结构。
注:代码分析基于linux-5.4.31
为啥要驱动模型随着系统结构演化越来越复杂,Linux内核对设备描述衍生出一般性的抽象描述,形成一个分层体系结构,从而引入了设备驱动模型。这样描述还是不够让人理解,来看一下这些需求就好理解些:
Linux内核可以在各种体系结构和硬件平台上运行,因此需要最大限度地提高代码在平台之间的可重用性。
分层实现也实现了软件工程的高内聚-低耦合的设计思想。低耦合体现在对外提供统一的抽象访问接口,高内聚将相关度紧密的集中抽象实现。
Linux内核驱动程序模型是先前在内核中使用的所有不同驱动程序模型的统一。 它旨在通过将一组数据和操作整合到全局可访问的数据结构中,来扩展基于基础总线来桥接设备驱动程序。
传统的驱动模型为它们所控制的设备实现了某种类似于树的结构(有时只是一个列表)。不同类型的总线之间没有任何一致性。
驱动模型抽象了啥当前驱动程序模型为描述总线和总线下可能出现的设备提供了一个通用的、统一的模型。统一总线模型包括一组所有总线都具有的公共属性和一组公共回调,如总线探测期间的设备发现、总线关闭、总线电源管理等。
通用的设备和桥接接口反映了现代计算机的目标:即执行无缝设备“即插即用”,电源管理和热插拔的能力。 特别是,英特尔和微软规定的模型(即ACPI)可确保与x86兼容的系统上几乎任何总线上的几乎所有设备都可以在此范式下工作。 当然,虽然大多数总线都支持其中大多数操作,但并不是每条总线都能够支持所有此类操作。
那么哪些通用需求被抽象出来了呢?
电源系统和系统关机,对于电源管理与系统关机对于设备相关的操作进行抽象实现。关机为什么要被抽象出来管理,比如设备操作正在进行此时系统收到关机指令,那么在设备模型层就会遍历系统设备硬件,确保系统正确关机。
用户空间访问:sysfs虚拟文件系统实现与设备模型对外的访问抽象,这也是为什么说Linux 设备也是文件的由来。实际从软件架构层面看,这其实是一个软件桥接模块,抽象出统一用户访问接口,桥接了设备驱动。
热插拔管理:热插拔管理机制定义统一的抽象接口操作符kset_hotplug_ops,不同设备利用操作符实现差异化。
设备类型:设备分类机制,从高层级抽象描述设备类型,具体可以在sysfs下面体现。
用户空间访问由于具有系统中所有设备的完整分层视图,因此将完整的分层视图导出到用户空间变得相对容易。 这是通过实现名为sysfs虚拟文件系统来完成的。
sysfs的自动挂载通常是通过/etc/fstab文件中的以下条目来完成的:
none /sys sysfs defaults 0 0对于Debian系统而言,可能在/lib/init/fstab采用下面的形式挂载:
none /sys sysfs nodev,noexec,nosuid 0 0当然也可以采用手动方式挂载:
# mount -t sysfs sysfs /sys当将设备插入树中时,都会为其创建一个目录。该目录可以填充在发现的每个层(全局层,总线层或设备层)中。
全局层当前创建两个文件-'name'和'power'。 前者报告设备名称。 后者报告设备的当前电源状态。 它还将用于设置当前电源状态。
总线层为探测总线时发现的设备创建文件。 例如,PCI层当前为每个PCI设备创建“ irq”和“resource”文件。
特定于设备的驱动程序也可以在其目录中导出文件,以暴露特定于设备的数据或可用接口。
驱动模型实现先来梳理一下内部几个主要与驱动模型相关的数据结构:
./include/linux/Device.h 定义设备驱动主要数据结构
bus_type:抽象描述总线类型,如USB/PCI/I2C/MMC等
device_driver:实现具体连接在总线上的设备驱动。
device:描述连接在总线上的设备
./include/linux/Kobject.h中定义了隐藏在后台的类似于基类的数据结构:
kset:可以认为是kobject的顶层容器类。每个kset内部都包含了自己的kobject.
kobject:在 sysfs 中出现的每个对象都对应一个 kobject, 它和内核交互来创建它的可见表述,每一个 kobject 对应 文件系统 /sys 里的一个 目录,目录的名字就是结构体中的 name