Linux驱动(1)概述

本文概述了Linux驱动从module_init到probe函数的流程,详细介绍了Linux驱动的关键结构体,包括struct bus_type、struct device和struct device_driver。在Linux内核中,设备和驱动分开注册并通过总线匹配,成功的匹配会触发驱动的probe函数。同时,设备、驱动和总线在sysfs中以kobject表示,各attribute对应sysfs文件。
摘要由CSDN通过智能技术生成

Linux驱动过程总览

在这里插入图片描述

  以上是Linux驱动从入口函数到驱动probe函数的整个流程。

  module_init(xxx_init_module):驱动入口函数,一般在驱动代码的最下方。其参数即为驱动初始化函数。

module_init(xxx_init_module)//这就入口
module_exit(xxx_exit_module)//这就设备退掉的时候调的,不是出口啊,一般来说不要...

  xxx_init_module:驱动初始化函数,当一个设备接入后,它的路就从这里开始了。
  xxx_register_driver:这通常是一个宏控,其本体为__xxx_register_driver。

#define xxx_register_driver(drv) \
			__xxx_register_driver(struct xxx_driver *,struct module *)
//这个xxx_driver还挺关键,随后再议。

  __xxx_register_driver:这个函数中包含了很多对驱动结构体中函数的重定向,这个之后再说,先关注一下它的返回值。

int __xxx_register_driver(struct xxx_driver *drv ,struct module *owner)
{
	...
	return driver_register(drv->driver);
}

  driver_register:这个函数的运行结果就是__xxx_register_driver函数的返回值。

int driver_register(struct device_driver *drv){}//device_driver它出现了...0.0

  bus_add_driver:在函数driver_register中运行,它的结果将决定总线是否可完成设备和驱动的匹配。参数和上面那个函数是一样的。

  driver_attach:它来了,它来了,他们又开始套娃了 …可能是我太菜了,不能理解这个函数是干嘛的…

int driver_attach(struct device_driver *drv)
{
	return bus_of_each_dev(drv->bus,NULL,drv,__driver_attach);
}

  bus_for_each_dev:driver_attach函数只做了一件事,就是返回这个函数的结果。(所以为什么不直接返回这个函数的结果???)这个函数有四个参数,我就不打出来了…费劲…

  fn(dev,data):bus_for_each_dev函数的第四个参数是__driver_attach函数,fn事实上就是指向__driver_attach的函数指针。所以这里事实上运行的是__driver_attach

  driver_match_device:这个函数是__driver_attach函数中的一个函数,主要目的就是为了匹配设备和驱动。它将会返回这么个玩意:别问我这干啥的,我也不知道…嘿嘿嘿… 不过假如没匹配上,那这个驱动就GG了。

static inline int driver_match_device(struct device_driver *drv,struct device *dev)//device它出现了..0.0
{
	return drv->bus->match ? drv->bus->match(dev,drv) : 1 ;
}

  driver_probe_device:这个函数建立在设备与驱动已经匹配上的情况下,它在函数__driver_attach中,走到这它还会提示一下驱动设备已经匹配了哦嘿嘿嘿 我自己加的printk,不知道你们加没加

int driver_probe_device(struct device_driver *drv,struct device *dev)
{
	...
	printk("bus:mmm is matching dev:yyy and drv:zzz OK!!");//大概就这样吧,log里就会打出来已经匹配成功了。
	ret = really_probe(dev,drv);
	...
}

  really_probe:如函数名所示,really_probe。就是真正的probe函数从这里开始,在函数中会调用drv->probe,从而开始驱动代码中的probe函数。

  (以上观点仅代表个人看法,欢迎指正。)

Linux驱动关键结构体

1.struct bus_type

struct bus_type {
	const char *name;
	const char *dev_name;
	struct device *dev_root;
	struct device_attribute *dev_attrs;
	const struct attribute_group **bus_groups;
	const struct attribute_group **dev_groups;
	const struct attribute_group **drv_groups;
	
	int	(*match)(struct device * dev, struct device_driver * drv);
    int	(*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device * dev);
    int (*remove)(struct device * dev);
    void (*shutdown)(struct device * dev);

	int (*online)(struct device *dev);
	int	(*offline)(struct device *dev);

	const struct dev_pm_ops *pm;
	struct iommu_ops *iommu_ops;

	struct subsys_private *p;
	struct lock_class_key lock_key;
};

2.struct device

struct device{
	struct device *parent;
	
	struct device_private *p;
	
	struct kobject kobj;
	const char *init_name; //设备的初始名称
	const struct device_type *type;

	struct mutex mutex; //互斥,同步对其驱动程序的调用
	
	struct bus_type *bus; //开启总线设备类型
	struct device_driver *driver; //哪个驱动程序分配了这个设备

	void	*platform_data; //平台特定数据,设备核心不碰它

	struct dev_pm_info power;
	struct dev_pm_domain *pm_domain;
	
	#ifdef CONMFIG_PINCTRL
	struct dev_pin_info	*pins;
	#endif

	#ifdef CONFIG_NUMA
	int num_node; //NUMA节点与该设备接近
	#endif
	u64	*dma_mask; //ama掩码,如果dma设备被开启
	u64	coherent_dma_mask; /*类似于dma掩码,但是对于内存一致映射,
							并不是所有的硬件都支持64位地址来一致分配这样的描述符*/
	struct device_dma_parameters *dma_parms;
	
	struct list_head dma_pools; //dma池

	struct dma_coherent_mem	*dma_mem //内部为连贯的内存覆写

	#ifdef CONFIG_DMA_CMA
	struct cma *cma_area; //用于dma分配的连续内存区域
	#endif
	
	/*arch 主要附加*/
	struct dev_archdata archdata;

	struct device_node *of_node; //关联设备树节点
	struct acpi_dev_node acpi_node; //关联ACPI设备节点

	dev_t devt; //dev_t,创建sysfs节点dev
	u32 id; //设备实例

	spinlock_t devres_lock;
	struct list_head;

	struct klist_node knode_class;
	struct class *class;
	const struct attribute_group **groups; //可选组

	void (*release)(struct device *dev);
	struct iommu_group *iommu_group;

	bool offline_disabled:1;
	bool offline:1;
}

3.struct device_driver

struct device_driver{
	const char *name;
	struct bus_type *bus;
	
	struct module *owner;
	const char *mod_name; //用于内置模块
	
	bool suppress_bind_attrs; //通过sysfs禁用 绑定/解绑定

	const struct of_device_id *of_match_table;
	const struct acpi_device_id *acpi_match_table;

	int (*probe) (struct device *dev);
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;

	const struct dev_pm_ops *pm;

	struct driver_private *p;
}

  其实在Linux内核中,所有设备的驱动的定义,都是以struct device_driver为基类进行继承扩展的。

  device_driver和device分别表示驱动和设备,这两都依附在一种总线上,因此都包含struct bus_type指针。在Linux内核中,设备和驱动是分开注册的,注册一个设备的时候,并不需要驱动已经存在。(驱动:俺也一样。)设备和驱动各自涌向内核,然后找另一半。总线则用match函数把他们捆绑在一块。匹配成功后则执行驱动的probe函数。
  总线驱动和设备最终都会落实为sysfs这玩意我稍后再议中的一个目录,进一步追踪代码会发现,他们实际上都可是kobject的派生类,kobject可看作是所有总线、设备和驱动的抽象基类,1个kobject对应sysfs中的1个目录。
  总线、设备和驱动中的各个attribute则直接落实为sysfs中的1个文件,attribute会伴随着show()和store()这两个函数,分别用于读写该attribute对应的sysfs文件。
  (上面这三段话熟吗,熟就对了,来自于Linux设备驱动开发详解基于最新的Linux4.0第125页…目前就这样吧,等有啥改动了,回头再改…)

转载请注明出处,谢谢...0.0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值