半半路路网

linux设备驱动——bus、device、driver加载顺序与匹配流程

linux设备驱动——bus、device、driver加载顺序与匹配流程

文章目录

  • 1. 前言
  • 2. 概念
    • 2.1. 数据结构
    • 2.2. probe函数
  • 3. bus、设顺序device、备驱driver加载顺序
    • 3.1. 加载方式
    • 3.2. 加载顺序
  • 4. device、加载driver匹配流程
    • 4.1. 加载driver
    • 4.2. 加载device
  • 5. Reference

1. 前言

最近回看了下Linux设备驱动相关知识,匹配做了个总结。流程有些话需要说在前面:

  • 文中有些内容为个人理解(上标H所标识内容),设顺序未必准确,备驱有误请评论指正。加载
  • 4.2节的匹配内容主要目的是为了搞清楚driver和device在加载的过程中是如何通过bus相互匹配。

本文源码源自4.10.17版本linux内核。流程

2. 概念

Linux设备驱动有三个基本概念:总线、设顺序驱动以及设备。备驱 三者之间关 系 H 三者之间关系^H 三者之间关系H简单描述如下:

  1. 总线为外设或片内模组与核心处理器之间的加载通信总线,例如SPI、匹配I2C、流程SDIO、USB等。
  2. 每个驱动、设备都需要挂载到一种总线上;
  3. 挂载到相同总线的驱动和设备可以通过该总线进行匹配,实现设备与对应的驱动之间的绑定。

2.1. 数据结构

以下为总线、驱动及设备在linux内核中对应的核心数据结构

// 总线数据结构 -- include/linux/device.hstruct bus_type { 	const char		*name;	const char		*dev_name;	struct device		*dev_root;	struct device_attribute	*dev_attrs;	/* use dev_groups instead */	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);	int (*suspend)(struct device *dev, pm_message_t state);	int (*resume)(struct device *dev);	const struct dev_pm_ops *pm;	const struct iommu_ops *iommu_ops;	struct subsys_private *p;	// 如下	struct lock_class_key lock_key;};// drivers/base/base.hstruct subsys_private { 	struct kset subsys;			// the struct kset that defines this subsystem	struct kset *devices_kset;	// the subsystem's 'devices' directory	struct list_head interfaces;	struct mutex mutex;	struct kset *drivers_kset;	// the list of drivers associated	struct klist klist_devices;	struct klist klist_drivers;	struct blocking_notifier_head bus_notifier;	unsigned int drivers_autoprobe:1;	struct bus_type *bus;	struct kset glue_dirs;	struct class *class;};
// 驱动数据结构 -- include/linux/device.hstruct device_driver { 	const char		*name;	struct bus_type		*bus;	// 指向本驱动挂载的bus	struct module		*owner;	const char		*mod_name;	/* used for built-in modules */	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */	enum probe_type probe_type;	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;	// 如下};// drivers/base/base.hstruct driver_private { 	struct kobject kobj;	struct klist klist_devices;	struct klist_node knode_bus;	struct module_kobject *mkobj;	struct device_driver *driver;};
// 设备数据结构 -- include/linux/device.hstruct device { 	struct device		*parent;	struct device_private	*p;	struct kobject kobj;	const char		*init_name; /* initial name of the device */	const struct device_type *type;	struct mutex		mutex;	/* mutex to synchronize calls to					 * its driver.					 */	struct bus_type	*bus;		/* type of bus device is on */	struct device_driver *driver;	/* which driver has allocated this					   device */	void		*platform_data;	/* Platform specific data, device					   core doesn't touch it */	void		*driver_data;	/* Driver data, set and get with					   dev_set/get_drvdata */	struct dev_links_info	links;	struct dev_pm_info	power;	struct dev_pm_domain	*pm_domain;#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN	struct irq_domain	*msi_domain;#endif#ifdef CONFIG_PINCTRL	struct dev_pin_info	*pins;#endif#ifdef CONFIG_GENERIC_MSI_IRQ	struct list_head	msi_list;#endif#ifdef CONFIG_NUMA	int		numa_node;	/* NUMA node this device is close to */#endif	u64		*dma_mask;	/* dma mask (if dma'able device) */	u64		coherent_dma_mask;/* Like dma_mask, but for					     alloc_coherent mappings as					     not all hardware supports					     64 bit addresses for consistent					     allocations such descriptors. */	unsigned long	dma_pfn_offset;	struct device_dma_parameters *dma_parms;	struct list_head	dma_pools;	/* dma pools (if dma'ble) */	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem					     override */#ifdef CONFIG_DMA_CMA	struct cma *cma_area;		/* contiguous memory area for dma					   allocations */#endif	/* arch specific additions */	struct dev_archdata	archdata;	struct device_node	*of_node; /* associated device tree node */	struct fwnode_handle	*fwnode; /* firmware device node */	dev_t			devt;	/* dev_t, creates the sysfs "dev" */	u32			id;	/* device instance */	spinlock_t		devres_lock;	struct list_head	devres_head;	struct klist_node	knode_class;	struct class		*class;	const struct attribute_group **groups;	/* optional groups */	void	(*release)(struct device *dev);	struct iommu_group	*iommu_group;	struct iommu_fwspec	*iommu_fwspec;	bool			offline_disabled:1;	bool			offline:1;};

2.2. probe函数

bus_type和device_driver中都包含probe()函数,两者关系可追踪到函数really_probe()中(该函数在后续的调用加载流程中会描述具体执行的位置,可先跳过,后续查看加载流程时返回此处查看),如下:

static int really_probe(struct device *dev, struct device_driver *drv){ 	int ret = -EPROBE_DEFER;	int local_trigger_count = atomic_read(&deferred_trigger_count);	bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&			   !drv->suppress_bind_attrs;	...	atomic_inc(&probe_count);	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",		 drv->bus->name, __func__, drv->name, dev_name(dev));	WARN_ON(!list_empty(&dev->devres_head));re_probe:	dev->driver = drv;	/* If using pinctrl, bind pins now before probing */	ret = pinctrl_bind_pins(dev);	if (ret)		goto pinctrl_bind_failed;	if (driver_sysfs_add(dev)) { 		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",			__func__, dev_name(dev));		goto probe_failed;	}	if (dev->pm_domain && dev->pm_domain->activate) { 		ret = dev->pm_domain->activate(dev);		if (ret)			goto probe_failed;	}	/*	 * Ensure devices are listed in devices_kset in correct order	 * It's important to move Dev to the end of devices_kset before	 * calling .probe, because it could be recursive and parent Dev	 * should always go first	 */	devices_kset_move_last(dev);	/*****  以下为重点

未经允许不得转载:半半路路网 » linux设备驱动——bus、device、driver加载顺序与匹配流程