学Linux驱动: 应该先了解驱动模型

时间:2020-06-03 00:09:30   收藏:0   阅读:58

[导读] Linux设备林林总总,嵌入式开发一个绕不开的话题就是设备驱动开发,在做具体设备驱动开发之前,有必要对Linux设驱动模型有一个相对清晰的认识,将会帮助驱动开发,明白具体驱动接口操作符相应都做些什么。

个人对于驱动模型的理解概括起来就是一句话:利用面向对象编程思想,实现设备分层管理软件体系结构

注:代码分析基于linux-5.4.31

为啥要驱动模型

随着系统结构演化越来越复杂,Linux内核对设备描述衍生出一般性的抽象描述,形成一个分层体系结构,从而引入了设备驱动模型。这样描述还是不够让人理解,来看一下这些需求就好理解些:

传统的驱动模型为它们所控制的设备实现了某种类似于树的结构(有时只是一个列表)。不同类型的总线之间没有任何一致性。

驱动模型抽象了啥

当前驱动程序模型为描述总线和总线下可能出现的设备提供了一个通用的、统一的模型。统一总线模型包括一组所有总线都具有的公共属性和一组公共回调,如总线探测期间的设备发现、总线关闭、总线电源管理等。

通用的设备和桥接接口反映了现代计算机的目标:即执行无缝设备“即插即用”,电源管理和热插拔的能力。 特别是,英特尔和微软规定的模型(即ACPI)可确保与x86兼容的系统上几乎任何总线上的几乎所有设备都可以在此范式下工作。 当然,虽然大多数总线都支持其中大多数操作,但并不是每条总线都能够支持所有此类操作。

那么哪些通用需求被抽象出来了呢?

用户空间访问

由于具有系统中所有设备的完整分层视图,因此将完整的分层视图导出到用户空间变得相对容易。 这是通过实现名为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

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 *);

class

kobject/kset

总体上而言:

通过上面一些关键数据结构关系分析,总线设备驱动模型最终目的是实现如下这样一个分层驱动模型。
技术分享图片

文章出自微信公众号:嵌入式客栈,更多内容,请关注本人公众号,严禁商业使用,违法必究
技术分享图片

原文:https://www.cnblogs.com/embInn/p/13034226.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!