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

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

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

static struct pci_device_id pci_ids[] = {{ PCI_DEVICE(MANF_ID, MODEL_CODE) },{ 0 }
};

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;
}

现在分析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);

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
};

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;   /* 驱动程序私有数据 */
};

3. 部分结构定义

driver_private 驱动私有结构

struct driver_private {struct kobject kobj; // 根kobjstruct klist klist_devices; // 设备列表struct klist_node knode_bus; // 设备节点struct module_kobject *mkobj; // 模块kobjstruct device_driver *driver; // 设备驱动结构
};

dev_pin_info 设备的引脚状态容器

struct dev_pin_info {struct pinctrl *p; 引脚控制包含设备的句柄struct pinctrl_state *default_state; // 句柄的默认状态(如果存在)struct pinctrl_state *init_state; // 探测时的状态(如果存在)
#ifdef CONFIG_PMstruct pinctrl_state *sleep_state; // 挂起时的状态(如果存在)struct pinctrl_state *idle_state; // 空闲(运行时挂起)时的状态(如果存在)
#endif
};

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

devres_node

devres_node 设备资源节点

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

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; // 引用计数
};

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); // 驱动注册到总线

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_workreturn ret;
}
EXPORT_SYMBOL_GPL(driver_register);

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文件等等)

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;}

driver_attach

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

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) { // falseerror = 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;
}

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

__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;
}

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

__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->mutexdrv = dev->p->async_driver; dev->p->async_driver = NULL;ret = driver_probe_device(drv, dev); // 驱动探测设备及延迟处理(如果需要)

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); // 减少设备引用计数
}

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); // 驱动探测设备

__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;
}

__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_debugret = really_probe_debug(dev, drv); // 执行探测处理函数,加debug信息elseret = really_probe(dev, drv); // 探测处理函数

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;
}

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; // falseint ret, link_ret;link_ret = device_links_check_suppliers(dev); // 检查是否存在厂商驱动程序...
re_probe:dev->driver = drv;/* 如果使用pinctrl,请在探测之前立即绑定引脚 */ret = pinctrl_bind_pins(dev); // 设备设置引脚相关

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属性文件

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;
}

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链表

dev_pin_info
devres

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

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/** 如果启用了电源管理,我们还将查找可选的睡眠和空闲引脚状态* 语义如<linux/pinchtrl/pincttrlstate.h>中所定义*/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"
#endifreturn 0;...
}

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;
}

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;
}

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_devicesdevice_links_driver_bound(dev); // 设备分为延迟或同步两种状态类型,分别放入不同的列表

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); // 用户事件通知// 绑定通知
}

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_listif (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函数// 在已排队等待的所有设备上调用
}

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

__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,
};

目录预览

<<linux设备模型:sysfs(kobject)解析>>
<<linux设备模型:kset及设备驱动抽象类(class)分析>>
<<linux设备模型:bus概念及pci_bus分析>>
<<linux设备模型:devtmpfs虚拟文件系统分析>>
<<linux设备模型:设备及设备节点创建过程分析>>
<<linux设备模型:固件设备及efi固件(平台)设备节点创建过程分析>>

linux设备模型:pci驱动程序注册过程相关推荐

  1. Linux设备驱动程序学习-Linux设备模型(总线、设备、驱动程序和类)

    文章的例子和实验使用<LDD3>所配的lddbus模块(稍作修改). 总线 总线是处理器和一个或多个设备之间的通道,在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟" ...

  2. linux lddbus设备,Linux设备驱动程序学习(14)-Linux设备模型(各环节的整合)

    Linux设备驱动程序学习(14) -Linux设备模型(各环节的整合) 通过一个设备在内核中生命周期的各个阶段,可以更好地理解Linux设备模型.我将通过分析lddbus和sculld的源码来了解L ...

  3. linux设备驱动程序架构的研究,Linux设备驱动程序学习(12)-Linux设备模型(底层原理简介)...

    Linux设备驱动程序学习(12) -Linux设备模型(底层原理简介) 以<LDD3>的说法:Linux设备模型这部分内容可以认为是高级教材,对于多数程序作者来说是不必要的.但是我个人认 ...

  4. 《linux设备驱动程序》——Linux设备模型

    一.概论 1.2.6版内核对系统结构的一般性抽象描述.现在内核使用了该抽象支持了多种不同的任务,其中包括: 1).电源管理和系统关机. 2).与用户控件通信. 3).热插拔设备. 4).设备类型. 5 ...

  5. 学习笔记——《LINUX设备驱动程序(第三版)》Linux设备模型:内核添加、删除设备、驱动程序

    文章目录 1. 前言 2. 准备工作 2.1. 概念 2.2. 具体总线.设备.驱动结构体说明 2.3. 注册总线 3. 添加设备 3.1. STEP1 --发现设备并创建设备结构 struct XX ...

  6. linux 统一设备模型 pci,linux设备模型____宏观印象

    linux设备模型____宏观印象 最近一个机会需要研究一个marvell芯片的设备的驱动,涉及驱动和一些用户态相关部分,正好学习一下驱动和sysfs,本文先是原理,后面的文章是详细描述.本文依托的是 ...

  7. Linux设备模型——设备驱动模型和sysfs文件系统解读笔记

    Linux设备模型--设备驱动模型和sysfs文件系统解读笔记 原文:https://blog.csdn.net/yj4231/article/details/7799245 将对Linux系统中的s ...

  8. Linux设备模型(总结)

    转:http://www.360doc.com/content/11/1219/16/1299815_173418267.shtml 看了一段时间的驱动编程,从LDD3的hello wrod到后来的字 ...

  9. Linux 文件系统与设备文件系统 (二)—— sysfs 文件系统与Linux设备模型

    提到 sysfs 文件系统 ,必须先需要了解的是Linux设备模型,什么是Linux设备模型呢? 一.Linux 设备模型 1.设备模型概述 从2.6版本开始,Linux开发团队便为内核建立起一个统一 ...

最新文章

  1. Spring Cloud 第十一篇:docker部署spring cloud项目
  2. python爬虫数据提取,Python 信息提取-爬虫,爬虫提取数据, import re
  3. 教你如何 构建基本的用户控件
  4. Hash散列算法解析
  5. 计算机网络多媒体图像矢量图,13多媒体信息处理——图像处理(一)
  6. 设置下载安装 桌面_小妖精美化app最新版下载-小妖精美化V5.3.9.800下载安装
  7. 《研磨设计模式》chap18 状态模式state(2)模式介绍
  8. DNS抓包分析--wireshark
  9. scrapy设置代理的方法
  10. Linux JDK升级
  11. 十个模块_专栏 | ABAQUS Part模块的十个小技巧
  12. 前端架构设计1:代码核心
  13. thinkphp连接oracle
  14. vue中注意watch的执行顺序
  15. HTML5响应式手机模板:h5手机抽奖游戏活动页面集合模板 HTML+CSS+JavaScript
  16. getch方法_C++中getch函数使用时注意事项
  17. 如何用甘特图进行项目进度管理
  18. 7-79 约分最简分式
  19. linux Fedora安装桌面,在Fedora Linux上安装Elementary OS桌面的方法
  20. iOS App thinning【( 通过 LinkMap、mach-o寻找优化点)】1、段迁移rename_section减小__TEXT 段大小(需关闭 Bitcode)2、查无用方法/类/宏/图

热门文章

  1. Python C# 通信2种路子
  2. 医疗行业数据泄露惊人,提高医疗信息安全刻不容缓
  3. 第23讲:多表查询之笛卡尔积的概念
  4. 成都顾连康复中心专业吗
  5. luch-request
  6. [转帖]一个关于国密SM4的故事
  7. Android真机测试使用百度地图定位到非洲附近的的问题
  8. matlab实时动态绘图clc,Matlab绘制动态 .gif 图
  9. mysql neq什么意思_ISO/EDIS 15876-1-2002 NEQ这个标准中EDIS和NEQ是什么意思?
  10. 输入您的密码来连接到_【对讲机的那点事】如何为您的数字对讲机热点配置安全密码...