个人对于驱动模型的理解概括上去就是一句话:借助面向对象编程思想,实现设备分层管理软件体系结构。
注:代码剖析基于linux-5.4.31
为何要驱动模型
随着系统结构演变越来越复杂,Linux内核对设备描述衍生出通常性的具象描述linux设备驱动程序 视频,产生一个分层体系结构,进而引入了设备驱动模型。这样描述还是不够让人理解,来看一下这种需求就好理解些:
传统的驱动模型为它们所控制的设备实现了某种类似于树的结构(有时只是一个列表)。不同类型的总线之间没有任何一致性。
驱动模型具象了啥
当前驱动程序模型为描述总线和总线下可能出现的设备提供了一个通用的、统一的模型。统一总线模型包括一组所有总线都具有的公共属性和一组公共反弹linux定时关机命令,如总线侦测期间的设备发觉、总线关掉、总线电源管理等。
通用的设备和桥接插口反映了现代计算机的目标:即执行无缝设备“即插即用”,电源管理和热拔插的能力。非常是,英特尔和谷歌规定的模型(即ACPI)可确保与x86兼容的系统上几乎任何总线上的几乎所有设备都可以在此范式下工作。其实,尽管大多数总线都支持其中大多数操作,但并不是每条总线都还能支持所有这种操作。
这么什么通用需求被具象下来了呢?
用户空间访问
因为具有系统中所有设备的完整分层视图linux 软件,因而将完整的分层视图导入到用户空间显得相对容易。这是通过实现名为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”文件。
特定于设备的驱动程序也可以在其目录中导入文件linux设备驱动程序 视频,以曝露特定于设备的数据或可用插口。
驱动模型实现
先来梳理一下内部几个主要与驱动模型相关的数据结构:
./include/linux/Device.h定义设备驱动主要数据结构
./include/linux/Kobject.h中定义了隐藏在后台的类似于子类的数据结构:
bus_type
bus_type用以驱动总线,具体的驱动USB/I2C/PCI/MMC等:
/*i2c-core-base.c*/
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
static int __init i2c_init(void)
{
int retval;
retval = of_alias_get_highest_id("i2c");
down_write(&__i2c_board_lock);
if (retval >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = retval + 1;
up_write(&__i2c_board_lock);
/*注册I2C总线*/
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
is_registered = true;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
if (IS_ENABLED(CONFIG_ACPI))
WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));
return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
is_registered = false;
/*错误时删除总线*/
bus_unregister(&i2c_bus_type);
return retval;
}
struct dev_pm_ops {
int (*prepare)(struct device *dev);
void (*complete)(struct device *dev);
int (*suspend)(struct device *dev);
int (*resume)(struct device *dev);
int (*freeze)(struct device *dev);
int (*thaw)(struct device *dev);
int (*poweroff)(struct device *dev);
int (*restore)(struct device *dev);
int (*suspend_late)(struct device *dev);
int (*resume_early)(struct device *dev);
int (*freeze_late)(struct device *dev);
int (*thaw_early)(struct device *dev);
int (*poweroff_late)(struct device *dev);
int (*restore_early)(struct device *dev);
int (*suspend_noirq)(struct device *dev);
int (*resume_noirq)(struct device *dev);
int (*freeze_noirq)(struct device *dev);
int (*thaw_noirq)(struct device *dev);
int (*poweroff_noirq)(struct device *dev);
int (*restore_noirq)(struct device *dev);
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
};
/*在总线上注册的驱动程序会在总线的驱动程序目录中获得一个目录*/
/sys/bus/pci/
|-- devices
`-- drivers
|-- Intel ICH
|-- Intel ICH Joystick
|-- agpgart
`-- e100
/*在该类型的总线上发现的每个设备都会在总线的设备目录中获得到物理层次结构中该设备目录的符号链接*/
/sys/bus/pci/
|-- devices
| |-- 00:00.0 -> ../../../root/pci0/00:00.0
| |-- 00:01.0 -> ../../../root/pci0/00:01.0
| `-- 00:02.0 -> ../../../root/pci0/00:02.0
`-- drivers
device
int device_register(struct device * dev);
device_driver
int driver_register(struct device_driver *drv);
int driver_create_file(struct device_driver *, const struct driver_attribute *);
void driver_remove_file(struct device_driver *, const struct driver_attribute *);
classkobject/kset总体上而言:
通过里面一些关键数据结构关系剖析,总线设备驱动模型最终目的是实现如下这样一个分层驱动模型。