[导读] Linux设备林林总总,嵌入式开发一个绕不开的话题就是设备驱动开发,在作具体设备驱动开发以前,有必要对Linux设驱动模型有一个相对清晰的认识,将会帮助驱动开发,明白具体驱动接口操做符相应都作些什么。node
我的对于驱动模型的理解归纳起来就是一句话:利用面向对象编程思想,实现设备分层管理软件体系结构。linux
注:代码分析基于linux-5.4.31c++
随着系统结构演化愈来愈复杂,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 定义设备驱动主要数据结构
./include/linux/Kobject.h中定义了隐藏在后台的相似于基类的数据结构:
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
做用:抽象描述具体的设备
设备注册:发现设备的总线驱动程序使用下面的函数来向内核注册设备
int device_register(struct device * dev);
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 *);
经过上面一些关键数据结构关系分析,总线设备驱动模型最终目的是实现以下这样一个分层驱动模型。
文章出自微信公众号:嵌入式客栈,更多内容,请关注本人公众号,严禁商业使用,违法必究