• linux设备模型:pci驱动程序注册过程


    一个具有正常探测功能的pci驱动程序应具有基本的pci_driver结构实现,如:

    static struct pci_driver driver_ops = {
        .name = "driver_name", // 驱动名称
        .id_table = pci_ids, // 驱动ids结构
        .probe = pci_probe, // 驱动探测函数
        .remove = pci_remove, // 驱动移除函数
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其中,驱动ids结构可以按照以下方式实现:

    static struct pci_device_id pci_ids[] = {
        { PCI_DEVICE(MANF_ID, MODEL_CODE) },
        { 0 }
    };
    
    • 1
    • 2
    • 3
    • 4

    MANF_ID 表示厂商ID 是一个数字ID,如0xfead 或 PCI_ANY_ID(表示通用),如果有真实硬件应与硬件ID一致
    MODEL_CODE 表示厂商设备 是一个数字ID,如0x1234 或 PCI_ANY_ID(表示通用),如上
    默认情况下,子系统的厂商ID和厂商设备ID不用设置,它们采用PCI_ANY_ID(表示通用),class、class_mask等成员默认也不需要指定,在设备创建后,创建相关对象关联设备节点即可(或创建时指定父级)

    pci_probe:
       驱动探测函数,可在函数内部实现bar映射等设备相关的功能设置等等

    pci_remove:
       驱动移除函数,可在函数内部实现驱动的移除等等,同样也可以实现设备取消bar映射等相关的功能设置

    准备工作完成后,我们开始驱动的执行逻辑分析:

    通常情况下,我们通过定义module_init(函数名称),作为驱动程序入口函数执行,如pci_init_module:

    static int pci_init_module(void)
    {
    	...
    	如字符、块设备注册,class创建,class设备节点关联等等
    	驱动其他的一些定义,如设置变量标志,输出注册相关新等等
    	...
    	dri_register = pci_register_driver(&driver_ops);
    	if (dri_register) {
            printk(KERN_ERR  ": pci driver registration failed !\n");
            goto fail;
        }
        ...
        return dri_register;
    	fail: 
    		if(DBUG_PRINT) printk(KERN_ALERT "pci_init_module :failed !\n");
    	return dri_register;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    现在分析pci_register_driver函数 :

    pci_register_driver(driver) \
       __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

       THIS_MODULE 如果驱动编译选项为m或手动编译驱动,在编译时会生成__this_module结构对象,参考 __this_module。如果为y时,则通过"内置模块条目 /sys/modules"查找,参考 module_add_driver

       KBUILD_MODNAME 模块名称,编译时通过如obj-m:=pci_test.o中获取pci_test

       driver 注册的driver_ops对象

    __pci_register_driver函数内部赋值完成后,执行driver_register(&driver_ops->driver); 驱动分配、探测设备,并注册到总线等等

    在arm等架构下,pci_register_driver还包括设备树相关的概念流程,如DMA设置,通过开启CONFIG_OF相关编译选项,启动设备树相关功能,用于动态加载dts目录下的配置文件信息


    目录


    1. 函数分析

    1.1 pci_register_driver

    2. 源码结构

    3. 部分结构定义

    4. 扩展函数/变量

    目录预览


    1. 函数分析

    1.1 pci_register_driver

      驱动分配、探测并注册到总线

    #define pci_register_driver(driver)             \
            __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
     ||
     \/ 
     int __pci_register_driver(struct pci_driver *drv, struct module *owner,
                              const char *mod_name)
    {
            /* 初始化通用驱动程序字段 */
            drv->driver.name = drv->name; // 驱动名称
            drv->driver.bus = &pci_bus_type; // 驱动的设备所属的总线
            drv->driver.owner = owner; // 所属者
            drv->driver.mod_name = mod_name; // 用于内置模块
            drv->driver.groups = drv->groups; // 驱动核心自动创建的默认属性
            drv->driver.dev_groups = drv->dev_groups; // 设备实例绑定到驱动程序后的附加属性
    
            spin_lock_init(&drv->dynids.lock); // 初始化动态id锁
            INIT_LIST_HEAD(&drv->dynids.list); // 初始化动态id链表
    
            /* 注册到核心 */
            return driver_register(&drv->driver); // 驱动分配、探测并注册到总线
    }
    EXPORT_SYMBOL(__pci_register_driver);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    pci_driver
    driver_register


    2. 源码结构

      pci_driver pci驱动结构

    struct pci_driver {
    	struct list_head node; // 节点
    	const char *name; // 名称
    	const struct pci_device_id *id_table;	// pci设备id结构
    	/* 必须为非NULL,才能调用探测 */
    	
    	int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);	/* 探测函数 */
    	// 已插入设备(硬件),并且pci设备id结构 描述的厂商ID等信息正确,可触发此函数执行
    	
    	void (*remove) (struct pci_dev *dev);	/* 设备移除(如果不是热插拔驱动则为NULL) */
    	int  (*suspend) (struct pci_dev *dev, pm_message_t state);	/* 设备挂起 */
    	int  (*suspend_late) (struct pci_dev *dev, pm_message_t state);
    	int  (*resume_early) (struct pci_dev *dev);
    	int  (*resume) (struct pci_dev *dev);	                /* 唤醒设备 */
    	void (*shutdown) (struct pci_dev *dev);
    	int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* PF pdev */
    	const struct pci_error_handlers *err_handler; // PCI总线错误事件回调结构
    	struct device_driver	driver; /* 设备驱动结构 */
    	struct pci_dynids dynids; // 动态id,用于驱动与设备的匹配
    	// 如果没有找到,再查询静态id
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    pci_device_id

      pci_device_id pci设备id结构

    struct pci_device_id {
    	__u32 vendor, device;		/* 厂商和设备ID or PCI_ANY_ID*/
    	__u32 subvendor, subdevice;	/* 子系统ID or PCI_ANY_ID */
    	__u32 class, class_mask;	/* (class,subclass,prog-if) triplet */
    	kernel_ulong_t driver_data;	/* 驱动程序私有数据 */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3. 部分结构定义

      driver_private 驱动私有结构

    struct driver_private {
    	struct kobject kobj; // 根kobj
    	struct klist klist_devices; // 设备列表
    	struct klist_node knode_bus; // 设备节点
    	struct module_kobject *mkobj; // 模块kobj
    	struct device_driver *driver; // 设备驱动结构
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

      dev_pin_info 设备的引脚状态容器

    struct dev_pin_info {
    	struct pinctrl *p; 引脚控制包含设备的句柄
    	struct pinctrl_state *default_state; // 句柄的默认状态(如果存在)
    	struct pinctrl_state *init_state; // 探测时的状态(如果存在)
    #ifdef CONFIG_PM
    	struct pinctrl_state *sleep_state; // 挂起时的状态(如果存在)
    	struct pinctrl_state *idle_state; // 空闲(运行时挂起)时的状态(如果存在)
    #endif
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      devres 设备资源

    struct devres {
    	struct devres_node		node; // 设备资源节点
    	/*
    	 * 一些架构希望对kmalloc缓存执行DMA,并且需要比64位整数的对齐更大的保证对齐
    	 * 因此,我们在这里使用ARCH_KMALLOC_MINALIGN
    	 * 并获得与普通KMALLOC()分配的缓冲区完全相同的对齐方式
    	 */
    	u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];
    	// x86_64 按照 __alignof__(unsigned long long)
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    devres_node

      devres_node 设备资源节点

    struct devres_node {
    	struct list_head		entry; // 列表(链表)
    	dr_release_t			release; // 释放函数
    	const char			*name; // 名称
    	size_t				size; // 大小
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      pinctrl 引脚控制(设备引脚控制状态保持器)

    struct pinctrl {
    	struct list_head node; // 全局列表节点
    	struct device *dev; // 使用此引脚控制的设备
    	struct list_head states; // 此设备的状态列表
    	struct pinctrl_state *state; // 当前状态
    	struct list_head dt_maps; // 从设备树动态解析的映射表块(部分架构不具备设备树概念,此概念相关结构的部分成员不使用)
    	struct kref users; // 引用计数
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4. 扩展函数/变量

      driver_register 驱动分配、探测并注册到总线

    int driver_register(struct device_driver *drv)
    {
    	int ret;
    	struct device_driver *other;
    
    	BUG_ON(!drv->bus->p); // 子系统私有结构
    
    	if ((drv->bus->probe && drv->probe) ||
    	    (drv->bus->remove && drv->remove) ||
    	    (drv->bus->shutdown && drv->shutdown)) // 这种情况应该是已注册驱动
    	    // 通常不指定设备驱动结构,由注册过程中自动完成关联
    	    
    		printk(KERN_WARNING "Driver '%s' needs updating - please use "
    			"bus_type methods\n", drv->name);
    
    	other = driver_find(drv->name, drv->bus); // 按名称查找总线上的设备驱动结构
    	if (other) { // 如果存在,应该是已注册驱动
    		printk(KERN_ERR "Error: Driver '%s' is already registered, "
    			"aborting...\n", drv->name);
    		return -EBUSY;
    	}
    
    	ret = bus_add_driver(drv); // 驱动注册到总线
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    bus_add_driver

    	ret = driver_add_groups(drv, drv->groups); // 驱动kobj分配到驱动属性组
    
    	kobject_uevent(&drv->p->kobj, KOBJ_ADD); // 通过发送uevent通知用户空间
    	deferred_probe_extend_timeout(); // 取消延迟的工作(如果存在),延迟后将工作任务放入全局工作队列
    	//	deferred_probe_timeout_work
    	
    	return ret;
    }
    EXPORT_SYMBOL_GPL(driver_register);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      bus_add_driver 驱动注册到总线

      获取总线对象
      分配驱动私有结构对象
      初始化klist 设备结构
      驱动私有结构的knode_bus 加入 bus私有结构的klist_drivers链表中
      驱动程序遍历总线中的设备是否匹配,匹配则执行探测相关函数
      驱动增加到模块
      驱动创建sysfs属性文件(用户事件)
      驱动kobj分配到总线的驱动属性组
      驱动创建sysfs属性文件(unbind和bind)

    int bus_add_driver(struct device_driver *drv)
    {
    	struct bus_type *bus;
    	struct driver_private *priv;
    	int error = 0;
    
    	bus = bus_get(drv->bus); // 获取总线对象
    
    	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
    
    	priv = kzalloc(sizeof(*priv), GFP_KERNEL); // 分配驱动私有结构对象
    
    	klist_init(&priv->klist_devices, NULL, NULL); // 初始化klist 设备结构
    	priv->driver = drv; // 关联设备驱动结构对象
    	drv->p = priv; // 关联驱动私有结构对象
    	priv->kobj.kset = bus->p->drivers_kset; // 关联驱动列表(容器)
    	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
    				     "%s", drv->name); // 私有结构对象的根kobj初始化
    	// driver_ktype 驱动动态注册的sysfs的ktype(用于释放对象,访问sysfs文件等等)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    driver_private

    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); // 驱动私有结构的knode_bus 加入 bus私有结构的klist_drivers链表中
    // 驱动结构与设备结构的创建过程(如结构的关系形式)大致相同
    
    if (drv->bus->p->drivers_autoprobe) { // 自动探测
    		error = driver_attach(drv); // 驱动程序遍历总线中的设备是否匹配,匹配则执行探测相关函数
    		// 驱动检查id列表(动态和静态)是否与设备匹配,匹配的情况下,执行驱动探测设备及延迟处理(如果需要)
    		if (error)
    			goto out_del_list;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    driver_attach

    module_add_driver(drv->owner, drv); // 驱动增加到模块
    
    • 1

    module_add_driver

    error = driver_create_file(drv, &driver_attr_uevent); // 驱动创建sysfs属性文件(用户事件)
    
    error = driver_add_groups(drv, bus->drv_groups); // 驱动kobj分配到总线的驱动属性组
    
    if (!drv->suppress_bind_attrs) { // false
    		error = add_bind_files(drv); // 驱动创建sysfs属性文件(unbind和bind)
    		if (error) {
    			/* Ditto */
    			printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
    				__func__, drv->name);
    		}
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

      driver_attach 驱动程序遍历总线中的设备是否匹配,匹配则执行探测相关函数

      驱动检查id列表(动态和静态)是否与设备匹配,匹配的情况下,执行驱动探测设备及延迟处理(如果需要)

    int driver_attach(struct device_driver *drv)
    {
    	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); // 遍历总线上的设备
    	// 驱动检查id列表(动态和静态)是否与设备匹配,匹配的情况下,执行驱动探测设备及延迟处理(如果需要)
    }
    EXPORT_SYMBOL_GPL(driver_attach);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    __driver_attach
    bus_for_each_dev

      __driver_attach 驱动检查id列表(动态和静态)是否与设备匹配,匹配的情况下,执行驱动探测设备及延迟处理(如果需要)

      检查驱动关联id列表(动态和静态)是否与设备匹配,匹配则返回1
      默认情况下,驱动名称是否匹配保存异步探测驱动程序的名称,或模块异步探测驱动有效
      执行驱动探测设备及延迟处理(如果需要)

    static int __driver_attach(struct device *dev, void *data)
    {
    	struct device_driver *drv = data;
    	bool async = false;
    	int ret;
    
    	/*
    	 * 锁定设备并尝试绑定到它
    	 * 我们在此处删除错误,并始终返回0
    	 * 因为我们需要继续尝试绑定到设备
    	 * 如果某些驱动程序不支持该设备,则会返回错误
    	 *
    	 * 如果出现错误,driver_be_device()将发出警告
    	 */
    
    	ret = driver_match_device(drv, dev); // 检查驱动关联id列表(动态和静态)是否与设备匹配,匹配则返回1
    	// 如果总线实现了match函数,执行match
    	// pci总线实现函数 pci_bus_match 判断一个PCI设备结构是否具有匹配的PCI设备id结构
    
    	... 
    	else if (ret == -EPROBE_DEFER) { // pci不支持此选项
    		dev_dbg(dev, "Device match requests probe deferral\n");
    		dev->can_match = true;
    		driver_deferred_probe_add(dev); // 延迟匹配
    		// 设备私有结构的deferred_probe,放入deferred_probe_pending_list链表
    		// driver_deferred_probe_trigger 启动重新探测延迟设备函数时
    		// 唤醒工作队列,为deferred_probe_pending_list链表中的每一个deferred_probe关联的设备执行探测函数
    		/*
    		 * 驱动程序无法与设备匹配,但可能与总线上的其他设备匹配
    		 */
    		return 0;
    	}
    	...
    
    	if (driver_allows_async_probing(drv)) { // 默认情况下,驱动名称是否匹配保存异步探测驱动程序的名称,或模块异步探测驱动有效
    	// #define ASYNC_DRV_NAMES_MAX_LEN	256
    	// static char async_probe_drv_names[ASYNC_DRV_NAMES_MAX_LEN];  内核命令行保存异步探测驱动程序的名称
    	// static bool async_probe_default; // 异步探测驱动程序是否启动
    	// module->async_probe_requested (drv->owner <-> module) 模块异步探测驱动
    		/*
    		 * 我们将异步探测设备,而不是同步探测设备,以允许更多的并行性
    		 *
    		 * 我们在这里只使用设备锁,以确保dev->driver和async_driver字段受到保护
    		 */
    		dev_dbg(dev, "probing driver %s asynchronously\n", drv->name);
    		device_lock(dev);
    		if (!dev->driver && !dev->p->async_driver) {
    			get_device(dev);
    			dev->p->async_driver = drv; // 设备私有结构的异步驱动成员关联驱动
    			async = true;
    		}
    		device_unlock(dev);
    		if (async)
    			async_schedule_dev(__driver_attach_async_helper, dev); // 异步执行__driver_attach_async_helper函数
    			// 驱动探测设备及延迟处理(如果需要)
    		return 0;
    	}
    
    	__device_driver_lock(dev, dev->parent); // 设备加锁
    	// 设备父级存在,父级加锁,否则设备加锁
    	
    	driver_probe_device(drv, dev); // 驱动探测设备及延迟处理(如果需要)
    	__device_driver_unlock(dev, dev->parent); // 设备释放锁
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    __driver_attach_async_helper

      bus_for_each_dev 遍历总线上的设备

    int bus_for_each_dev(struct bus_type *bus, struct device *start,
    		     void *data, int (*fn)(struct device *, void *))
    {
    	struct klist_iter i;
    	struct device *dev;
    	int error = 0;
    
    	if (!bus || !bus->p)
    		return -EINVAL;
    
    	klist_iter_init_node(&bus->p->klist_devices, &i,
    			     (start ? &start->p->knode_bus : NULL)); // 初始化klist 设备迭代器结构
    	while (!error && (dev = next_device(&i)))
    		error = fn(dev, data); // 如果当前设备节点存在,执行__driver_attach函数
    	klist_iter_exit(&i);
    	return error;
    }
    EXPORT_SYMBOL_GPL(bus_for_each_dev);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

      __driver_attach_async_helper 驱动探测设备及延迟处理(如果需要)

    static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie)
    {
    	struct device *dev = _dev;
    	struct device_driver *drv;
    	int ret;
    
    	__device_driver_lock(dev, dev->parent); // 设备加锁
    	// 设备父级存在,父级加锁,否则设备加锁
    	// dev->mutex
    	
    	drv = dev->p->async_driver; 
    	dev->p->async_driver = NULL;
    	ret = driver_probe_device(drv, dev); // 驱动探测设备及延迟处理(如果需要)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    driver_probe_device

    __device_driver_unlock(dev, dev->parent); // 设备释放锁
    
    	dev_dbg(dev, "driver %s async attach completed: %d\n", drv->name, ret);
    
    	put_device(dev); // 减少设备引用计数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      driver_probe_device 驱动探测设备及延迟处理(如果需要)

      驱动探测设备
      如果返回EPROBE_DEFER相关状态,驱动请求探测重试
      放入延迟探测列表
      探测过程中发生了触发器,启动重新探测延迟的设备
      唤醒probe_waitqueue等待队列

    static int driver_probe_device(struct device_driver *drv, struct device *dev)
    {
    	int trigger_count = atomic_read(&deferred_trigger_count); // 读取原子计数
    	// 原子 deferred_trigger_count 用于确定在探测驱动程序过程中是否发生了成功的触发
    	// 如果在探测过程中触发计数发生变化,则应再次触发延迟处理
    	
    	int ret;
    
    	atomic_inc(&probe_count); // 探测计数自增
    	ret = __driver_probe_device(drv, dev); // 驱动探测设备
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    __driver_probe_device

    if (ret == -EPROBE_DEFER || ret == EPROBE_DEFER) {
    #define EPROBE_DEFER	517	/* 驱动请求探测重试 */
    
    	driver_deferred_probe_add(dev);  // 放入延迟探测列表
    
    	/*
    		 * 探测过程中是否发生了触发器?如果是,需要重新触发
    		 */
    		if (trigger_count != atomic_read(&deferred_trigger_count) &&
    		    !defer_all_probes)
    			driver_deferred_probe_trigger(); // 启动重新探测延迟的设备
    	}
    
    	atomic_dec(&probe_count); // 探测计数自减
    	wake_up_all(&probe_waitqueue); // 唤醒probe_waitqueue等待队列
    	return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

      __driver_probe_device 驱动探测设备

      如果驱动允许匹配
      恢复(唤醒)厂商设备并修改引用计数
      刷新挂起的请求并等待完成
      执行探测处理函数
      为设备排队执行“空闲检查”
      删除对厂商设备的引用

    static int __driver_probe_device(struct device_driver *drv, struct device *dev)
    {
    	int ret = 0;
    	...
    	dev->can_match = true; // 可以匹配
    	...
    	pm_runtime_get_suppliers(dev); // 恢复(唤醒)厂商设备并修改引用计数
    	// static DEFINE_MUTEX(device_links_lock); 设备链接 写相关的锁
    	// DEFINE_STATIC_SRCU(device_links_srcu); // 设备链接 读相关的锁
    	// SRCU 可休眠读拷贝更新(Sleepable Read-copy update) 
    	
    	if (dev->parent)
    		pm_runtime_get_sync(dev->parent); // 启动设备的使用计数器并恢复
    
    	pm_runtime_barrier(dev); // 刷新挂起的请求并等待完成
    
    	if (initcall_debug) // /sys/module/kernel/parameters/initcall_debug
    		ret = really_probe_debug(dev, drv); // 执行探测处理函数,加debug信息
    	else
    		ret = really_probe(dev, drv); // 探测处理函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    really_probe

    pm_request_idle(dev); // 为设备排队执行“空闲检查”
    // 对一个工作项进行排队,以异步方式为设备运行相当于pm_runtime_idle()的函数
    
    if (dev->parent)
    		pm_runtime_put(dev->parent); // 丢弃设备使用计数器,如果为0则排队“空闲检查”
    		// 减少设备的运行时PM使用计数器,如果它等于0,就像pm_request_idle()一样为设备排队一个工作项
    
    	pm_runtime_put_suppliers(dev); // 删除对厂商设备的引用
    	return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

      really_probe 探测处理函数

      检查是否存在厂商驱动程序
      设备设置引脚相关内容
      设置DMA配置(如果开启了CONFIG_OF,设备树相关)
      驱动通知、链接,及创建sysfs属性文件
      驱动探测
      设备根kobj关联/分配到设备属性组
      驱动探测已完成,更新引脚控制状态
      驱动绑定设备

    static int really_probe(struct device *dev, struct device_driver *drv)
    {
    	bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
    			   !drv->suppress_bind_attrs; // false
    	int ret, link_ret;
    
    	link_ret = device_links_check_suppliers(dev); // 检查是否存在厂商驱动程序
    
    	...
    re_probe:
    	dev->driver = drv;
    
    	/* 如果使用pinctrl,请在探测之前立即绑定引脚 */
    	ret = pinctrl_bind_pins(dev); // 设备设置引脚相关
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    pinctrl_bind_pins

    	if (dev->bus->dma_configure) { // 设置DMA配置
    		ret = dev->bus->dma_configure(dev); // 函数更新PCI设备的DMA配置
    		// 使用相同的信息从主机桥的父节点的OF节点或ACPI节点(如果有)
    		if (ret)
    			goto pinctrl_bind_failed;
    	}
    
    	ret = driver_sysfs_add(dev); // 驱动通知、链接,及创建sysfs属性文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    driver_sysfs_add

    if (dev->pm_domain && dev->pm_domain->activate) {
    		ret = dev->pm_domain->activate(dev); // 设备恢复
    		if (ret)
    			goto probe_failed;
    	}
    
    ret = call_driver_probe(dev, drv); // 驱动探测
    // 执行pci_device_probe函数
    // 通过主桥设备对象找到pin值(引脚号)
    // 计算出卡槽位置(槽号),然后通过槽号找到对应的中断号
    // 写入到中断线(行)并关联到pci设备(由pci驱动使用)
    // 最后在连接设备的cpu上执行驱动程序初始化,执行驱动的探测函数(用户注册的驱动)
    
    ret = device_add_groups(dev, drv->dev_groups); // 设备根kobj关联/分配到设备属性组
    // 获取kobj对象的sysfs所有权数据
    // 创建kernfs_node节点及命名空间 (父级kn,如目录)
    // 然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件)
    // 包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree
    // 更新哈希值及时间戳,并激活这个节点(属性列表中的节点)
    
    if (dev_has_sync_state(dev)) { // 如果设备驱动 或 总线的同步状态函数存在
    		ret = device_create_file(dev, &dev_attr_state_synced); // // 为设备创建sysfs属性文件
    }
    
    pinctrl_init_done(dev); // 驱动探测已完成,更新引脚控制状态
    
    if (dev->pm_domain && dev->pm_domain->sync)
    		dev->pm_domain->sync(dev);
    
    driver_bound(dev); // 驱动绑定设备
    
    pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
    		 drv->bus->name, __func__, dev_name(dev), drv->name);
    	goto done;
    	...
    	done:
    	return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    driver_bound

      pinctrl_bind_pins 设备设置引脚相关

      如果设备节点与父级设备共享引脚,不执行以下过程

      分配设备的引脚状态容器对象
      创建两级引脚控制对象指针,二级指针关联到设备
      查找/分配默认的引脚控制状态结构
      查找/分配初始的引脚控制状态结构
      选择/激活/编程一个引脚控制状态到硬件
      查找/分配睡眠引脚控制状态结构
      查找/分配空闲引脚控制状态结构

    int pinctrl_bind_pins(struct device *dev)
    {
    	int ret;
    
    	if (dev->of_node_reused) // 如果设备(树)节点与上级设备共享
    		return 0;
    
    	dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL); // 分配设备的引脚状态容器对象
    	// 分配设备资源对象,设备资源节点的列表(链表,node->entry)放入dev->devres_head链表
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    dev_pin_info
    devres

    dev->pins->p = devm_pinctrl_get(dev); // 创建两级引脚控制对象指针,二级指针关联到设备
    // 一级指针引脚控制放入pinctrl_list(引脚控制链表)
    // 二级指针引脚控制作为设备资源对象的data成员 关联到设备 (node->entry 放入 dev->devres_head链表)
    
    • 1
    • 2
    • 3

    pinctrl
    create_pinctrl

    dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
    					PINCTRL_STATE_DEFAULT); // 查找/分配默认的引脚控制状态结构
    // #define PINCTRL_STATE_DEFAULT "default"
    // 初始化settings链表
    // node链表放入引脚控制的状态链表(p->states)
    
    dev->pins->init_state = pinctrl_lookup_state(dev->pins->p,
    					PINCTRL_STATE_INIT); // 查找/分配初始的引脚控制状态结构
    // #define PINCTRL_STATE_INIT "init"
    
    ...
    ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state); // 选择/激活/编程一个引脚控制状态到硬件
    
    #ifdef CONFIG_PM
    	/*
    	 * 如果启用了电源管理,我们还将查找可选的睡眠和空闲引脚状态
    	 * 语义如中所定义
    	 */
    	dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
    					PINCTRL_STATE_SLEEP); // 查找/分配睡眠引脚控制状态结构
    	// #define PINCTRL_STATE_SLEEP "sleep"
    
    	dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
    					PINCTRL_STATE_IDLE); // 查找/分配空闲引脚控制状态结构
    	// #define PINCTRL_STATE_IDLE "idle"
    #endif
    
    	return 0;
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

      create_pinctrl 创建引脚控制对象

    static struct pinctrl *create_pinctrl(struct device *dev,
    				      struct pinctrl_dev *pctldev)
    {
    	struct pinctrl *p;
    	const char *devname;
    	struct pinctrl_maps *maps_node;
    	int i;
    	const struct pinctrl_map *map;
    	int ret;
    
    	/*
    	 * 为每个映射创建状态cookie持有者结构pinctrl
    	 * 这是消费者在使用pinctrl_get()请求pin控制句柄时会得到的
    	 */
    	p = kzalloc(sizeof(*p), GFP_KERNEL); // 分配引脚控制对象
    
    	p->dev = dev; // 关联设备
    	INIT_LIST_HEAD(&p->states); // 初始化状态链表
    	INIT_LIST_HEAD(&p->dt_maps); // 初始化设备树链表(部分架构不支持设备树概念,此概念相关结构的部分成员不使用)
    
    	ret = pinctrl_dt_to_map(p, pctldev); // 直接返回0
    	// x86_64默认没有启动 CONFIG_OF 选项,不使用设备树相关内容
    	
    	devname = dev_name(dev); // 设备名称
    
    	...
    	pinctrl_maps这部分跳过
    	...
    
    	kref_init(&p->users); // 初始化引用计数器
    	// 专门用于引用计数的atomic-t的变体
    
    	/* 将引脚控制添加到全局列表 */
    	mutex_lock(&pinctrl_list_mutex);
    	list_add_tail(&p->node, &pinctrl_list); // 引脚控制放入pinctrl_list(引脚控制链表)
    	
    	mutex_unlock(&pinctrl_list_mutex);
    
    	return p;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

      driver_sysfs_add 驱动通知、链接,及创建sysfs属性文件

      执行通知链函数,驱动即将被绑定
      驱动私有结构 链接到 设备
      设备 链接到 驱动私有结构,链接名称"driver"
      为设备创建sysfs属性文件

    static int driver_sysfs_add(struct device *dev)
    {
    	int ret;
    
    	if (dev->bus)
    		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
    					     BUS_NOTIFY_BIND_DRIVER, dev); // 执行通知链(注册的)函数
    		//	#define BUS_NOTIFY_BIND_DRIVER		0x00000004 /* 驱动即将被绑定 */
    
    	ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
    				kobject_name(&dev->kobj)); // 在两个对象之间创建符号链接,驱动私有结构 链接到 设备
    
    	ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
    				"driver"); // 设备 链接到 驱动私有结构,链接名称"driver"
    
    	ret = device_create_file(dev, &dev_attr_coredump); // 为设备创建sysfs属性文件
    
    	if (!ret)
    		return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

      driver_bound 驱动绑定设备

      设备私有结构的knode_driver 放入 设备驱动私有结构的klist_devices
      设备分为延迟或同步两种状态类型,分别放入不同的列表

      检查设备电源函数是否可以使用
      确保设备不再在一个延迟列表中,并开始重新尝试所有挂起的设备
      执行通知链(注册的)函数,驱动程序绑定到设备
      用户事件通知(绑定通知)

    static void driver_bound(struct device *dev)
    {
    	...
    	klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
    	// 设备私有结构的knode_driver 放入 设备驱动私有结构的klist_devices
    
    	device_links_driver_bound(dev); // 设备分为延迟或同步两种状态类型,分别放入不同的列表
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    device_links_driver_bound

    device_pm_check_callbacks(dev); // 检查设备电源函数是否可以使用
    
    /*
     * 确保设备不再在一个延迟列表中,并开始重新尝试所有挂起的设备
     * /
    driver_deferred_probe_del(dev); // 移除延迟列表
    driver_deferred_probe_trigger();  // 启动重新探测延迟的设备
    // *该函数将所有设备从挂起列表移动到活动列表
    // 并调度延迟探测工作队列来处理它们
    // 它应该在驱动程序成功绑定到设备时调用
    // queue_work(system_unbound_wq, &deferred_probe_work);
    
    if (dev->bus)
    		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
    					     BUS_NOTIFY_BOUND_DRIVER, dev); // // 执行通知链(注册的)函数
    		// #define BUS_NOTIFY_BOUND_DRIVER		0x00000005 /* 驱动程序绑定到设备 */
    
    	kobject_uevent(&dev->kobj, KOBJ_BIND); // 用户事件通知
    	// 绑定通知
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

      device_links_driver_bound 设备分为延迟或同步两种状态类型,分别放入不同的列表

    void device_links_driver_bound(struct device *dev)
    {
    	struct device_link *link, *ln;
    	LIST_HEAD(sync_list); // 定义、初始化sync_list链表对象
    	
    	/*
    	 * 如果一个设备绑定成功,那么它应该已经创建了它需要的所有设备链接
    	 * 或者根据需要创建了新的设备链接
    	 * 因此,fw_devlink不再需要创建到设备的任何厂商的设备链接
    	 *
    	 * 另外,如果这个绑定设备的子固件节点到现在还没有被添加为设备
    	 *  那么假定它永远不会被添加,并确保其他设备不会通过等待这样的子设备无限期地延迟探测
    	 */
    	if (dev->fwnode && dev->fwnode->dev == dev) {
    		struct fwnode_handle *child;
    		fwnode_links_purge_suppliers(dev->fwnode); // 删除fwnode_handle的所有厂商链接
    		fwnode_for_each_available_child_node(dev->fwnode, child)
    			fw_devlink_purge_absent_suppliers(child);
    	}
    
    	device_remove_file(dev, &dev_attr_waiting_for_supplier); // 移除sysfs属性文件
    
    	...
    	if (link->flags & DL_FLAG_AUTOPROBE_CONSUMER)
    			driver_deferred_probe_add(link->consumer);  // 放入延迟探测列表
    			// deferred_probe -> deferred_probe_pending_list
    
    	if (defer_sync_state_count)
    		__device_links_supplier_defer_sync(dev); // 设备放入延迟探测列表(deferred_sync)
    	else
    		__device_links_queue_sync_state(dev, &sync_list); // 设备放入同步状态列表
    	
    	...
    	device_link_drop_managed(link); // 移除device_link相关的链表节点等
    	...
    
    	dev->links.status = DL_DEV_DRIVER_BOUND;
    	// DL_DEV_DRIVER_BOUND: 驱动已绑定到设备
    	...
    	
    	device_links_flush_sync_list(&sync_list, dev); // 对设备列表调用sync_state函数
    	// 在已排队等待的所有设备上调用
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

      module_add_driver 驱动增加到模块

    void module_add_driver(struct module *mod, struct device_driver *drv)
    {
    	char *driver_name;
    	int no_warn;
    	struct module_kobject *mk = NULL;
    
    	if (mod) // 指定了THIS_MODULE,并且编译选项为m或手动编译驱动,在编译时会生成__this_module结构对象
    		mk = &mod->mkobj;
    	else if (drv->mod_name) { // 编译选项为y时
    		struct kobject *mkobj;
    
    		/* 查找中的内置模块条目 /sys/modules */
    		mkobj = kset_find_obj(module_kset, drv->mod_name);
    		if (mkobj) {
    			mk = container_of(mkobj, struct module_kobject, kobj);
    			/* 记住我们的模块结构 */
    			drv->p->mkobj = mk;
    			/* kset_find_obj took a reference */
    			kobject_put(mkobj);
    		}
    	}
    
    	/* 不要检查返回代码;这些调用是幂等的 */
    	no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module");
    	// drv->p->kobj 链接到 mk->kobj  "module"
    	driver_name = make_driver_name(drv);
    	if (driver_name) {
    		module_create_drivers_dir(mk);
    		no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj,
    					    driver_name);
    		// mk->drivers_dir 链接到 drv->p->kobj 驱动名称
    		kfree(driver_name);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    __this_module

      __this_module 编译选项为m或手动编译驱动,在编译时会生成__this_module结构对象

    __visible struct module __this_module
    __section(".gnu.linkonce.this_module") = {
            .name = KBUILD_MODNAME,
            .init = init_module,
    #ifdef CONFIG_MODULE_UNLOAD
            .exit = cleanup_module,
    #endif
            .arch = MODULE_ARCH_INIT,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    目录预览


    <>
    <>
    <>
    <>
    <>
    <>

  • 相关阅读:
    #力扣:1. 两数之和@FDDLC
    阿里巴巴全球数学竞赛报名条件
    pico+unity3d运行测试方法
    第十三章《集合》第3节:Set集合
    mongodb导出聚合查询的数据
    nginx的正向代理、反向代理、负载均衡
    优秀的网络工程师,需要具备什么?
    差分信号的末端并联电容到底有什么作用?
    标签类目体系(面向业务的数据资产设计方法论)-读书笔记5
    如何做好持续交付中的多环境配置管理?
  • 原文地址:https://blog.csdn.net/a29562268/article/details/128045281