linux 驱动probe 被调用流程分析 理解1
原文链接:https://blog.csdn.net/goodnight1994/article/details/82082089 修改个别错误呈现
前言: 对于linux platform device 和driver,一个driver可对应多个device,通过名字进行匹配,调用驱动里边实现的probe函数,本文以一个i2c设备为例,从驱动的i2c_add_driver()开始看源码以及用比较笨的打log的方式分析如何一步一步调用的probe()函数。
分析的代码基于linux kernel 4.15
/****************************************/
从module_init()开始看,
定义位置:kernel4.15/include/linux/init.h
源码里对该函数的说明:
/**
- module_init() - driver initialization entry point
- @x: function to be run at kernel boot time or module insertion
- module_init() will either be called during do_initcalls() (if
- builtin) or at module insertion time (if a module). There can only
- be one per module.
*/
作为每个驱动的入口函数,若代码有编译进kernel的话在开机kernel 启动(或模块被动态加载的时候)的时候被do_initcalls()调用,
从kernel如何一步一步走到do_initcalls()进而调用module_init(),调用链如下:
start_kernel();//kernel的第一个函数
rest_init();
kernel_init();
kernel_init_freeable();
do_basic_setup();
do_initcalls(); —> do_initcall_level(); —>module_init();
/*************************************/
接着通过module_init()调用你在驱动里边实现的init函数:
对于一个i2c设备来说,做法如下:
static struct i2c_driver your_driver =
{
.driver =
{
.owner = THIS_MODULE,
.name = YOUR_DRIVER_NAME_UP,
.of_match_table = your_driver_of_match,
},
.probe = your_probe_func,
.remove = your_i2c_drv_remove,
.id_table = your_i2c_drv_id_table,
//.suspend = your_i2c_drv_suspend,
//.resume = your_i2c_drv_resume,
};
static int __int your_init_func(void)
{
//…
i2c_add_driver(&your_i2c_driver);
//…
}
modul_init(your_init_func);
将 your_probe_func()地址装进你实现的struct i2c_driver的.probe 成员,
接下来,从i2c_add_driver()如何调用到你的probe函数,调用链如下:
i2c_register_driver();
driver_register();
bus_add_driver();
driver_attach();
__driver_attach (for your device);
driver_probe_device();
really_probe();
i2c_device_probe (this is what dev->bus->probe is for an i2c driver);
your_probe_func();
接下来一个一个函数分析他们到底干了什么:
先看i2c_register_driver(),
定义位置:kernel/4.15/drivers/i2c/i2c-core.c
int i2c_register_driver(struct module *owner, struct i2c_driver driver)
{
int res;
/ Can’t register until after driver model init */
if (WARN_ON(!is_registered)) {
printk(“unlikely(WARN_ON(!i2c_bus_type.p and return -EAGAIN.\n”);
return -EAGAIN;
}
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;//这边添加了bus type
INIT_LIST_HEAD(&driver->clients); /* When registration returns, the driver core * will have called probe() for all matching-but-unbound devices. */
res = driver_register(&driver->driver); //这边走进去
if (res){ printk("driver_register return res : %d\n", res); return res;
} pr_debug("driver [%s] registered\n", driver->driver.name);
printk(KERN_INFO "======>lkhlog %s\n",driver->driver.name); /* iWalk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver); return 0;
}
type定义如下:
struct bus_type i2c_bus_type = {
.name = “i2c”,
.match = i2c_device_match,
.probe = i2c_device_probe,//后面会回调这个函数,在上面的调用链里有提到
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
添加了bus_type,然后调用driver_register(),
接着看driver_register(),
定义位置:kernel/msm-4.9/drivers/base/driver.c
/**
driver_register - register driver with bus 注册总线驱动,和总线类型联系起来
@drv: driver to register
We pass off most of the work to the bus_add_driver() call,
since most of the things we have to do deal with the bus
structures.
*/
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;BUG_ON(!drv->bus->p); //…… other = driver_find(drv->name, drv->bus);//确认该驱动是否已经注册过 printk(KERN_WARNING "======>lkh driver_find, other : %d", other); //…… ret = bus_add_driver(drv);//主要从这儿走进去 printk(KERN_WARNING "======>lkh bus_add_driver, ret : %d", ret); if (ret) return ret; ret = driver_add_groups(drv, drv->groups); if (ret) { printk(KERN_WARNING "======>lkh bus_remove_driver"); bus_remove_driver(drv); return ret; } kobject_uevent(&drv->p->kobj, KOBJ_ADD); return ret;
}
接着看 bus_add_driver(),
定义位置:kernel/msm-4.9/drivers/base/bus.c
/**
bus_add_driver - Add a driver to the bus.
@drv: driver.
*/
int bus_add_driver(struct device_driver *drv)
{bus = bus_get(drv->bus);//重新获取到前边装进去的bus
if (!bus) {
printk(KERN_ERR “======>lkh return -EINVAL\n”);
return -EINVAL;
}printk(KERN_ERR “======>lkh bus_add_driver\n”);
pr_debug(“bus: ‘%s’: add driver %s\n”, bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;//driver_private 里边指针指向device_driver
drv->p = priv;
//device_driver也有指针指向driver_private,这样就可通过其中一个获取到另外一个
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
“%s”, drv->name);
if (error)
goto out_unregister;klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
if (drv->bus->p->drivers_autoprobe) {
printk(KERN_ERR “>lkh drv->bus->p->drivers_autoprobe == true, name : %s\n", drv->name);
if (driver_allows_async_probing(drv)) {
pr_debug(“bus: ‘%s’: probing driver %s asynchronously\n”,
drv->bus->name, drv->name);
printk(KERN_ERR ">lkh bus: ‘%s’: probing driver %s asynchronously\n”,
drv->bus->name, drv->name);
async_schedule(driver_attach_async, drv);
} else {
printk(KERN_ERR “>lkh enter driver_attach, name : %s\n", drv->name);
error = driver_attach(drv);//这边走进去
printk(KERN_ERR ">lkh driver_attach, error : %d\n”, error);
if (error)
goto out_unregister;
}
}
printk(KERN_ERR “======>lkh bus_add_driver 2, name : %s \n”, drv->name);
//若前边driver_attach()返回没有错误的话,
//这边会进去创建相关节点,链接
module_add_driver(drv->owner, drv);
//……
}
接着看driver_attach(),直接看函数说明就能明白了,
定义位置:kernel/msm-4.9/drivers/base/dd.c
/**driver_attach - try to bind driver to devices.
@drv: driver.
Walk the list of devices that the bus has on it and try to
match the driver with each one. If driver_probe_device()
returns 0 and the @dev->driver is set, we’ve found a
compatible pair.
/
int driver_attach(struct device_driver drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
在bus_for_each_dev()里边,将会遍历总线上的每个设备,并调用__driver_attach() 函数,如下:
定义位置:kernel/msm-4.9/drivers/base/bus.c
/bus_for_each_dev - device iterator.
@bus: bus type.
@start: device to start iterating from.
@data: data for the callback.
@fn: function to be called for each device.
Iterate over @bus’s list of devices, and call @fn for each,
passing it @data. If @start is not NULL, we use that device to
begin iterating from.
*/
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
//……
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
printk(KERN_WARNING "======>lkh while\n");
//获取每一个device,并调用__driver_attach
while ((dev = next_device(&i)) && !error){ error = fn(dev, data); //__driver_attach(dev,data) //printk(KERN_WARNING "======>lkh while enter\n"); //} klist_iter_exit(&i);
printk(KERN_WARNING "======>lkh bus_for_each_dev end \n");
return error;
}
接着走进__driver_attach()继续看,
定义位置:kernel/msm-4.9/drivers/base/dd.c
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
int ret;
//调用 i2c_device_match(),匹配设备和驱动
ret = driver_match_device(drv, dev);
//……
if (!dev->driver){ printk(KERN_DEBUG "======>lkh enter driver_probe_device \n"); driver_probe_device(drv, dev);//这边走进去
}
device_unlock(dev);
if (dev->parent) device_unlock(dev->parent); return 0;
}
先看一下driver_match_device(),
定义位置:kernel/msm-4.9/drivers/base/base.h
kernel/msm-4.9/drivers/i2c/i2c-core.c
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
/* Attempt an OF style match */ //在这儿匹配,三种匹配方式: //Compatible match has highest priority //Matching type is better than matching name //Matching name is a bit better than not if (i2c_of_match_device(drv->of_match_table, client)) return 1; /* Then ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; driver = to_i2c_driver(drv); /* Finally an I2C match */ if (i2c_match_id(driver->id_table, client)) return 1; return 0;
}
这个i2c_device_match()就是前边在函数i2c_register_driver()里边装进driver->driver.bus的:
driver->driver.bus = &i2c_bus_type;
struct bus_type i2c_bus_type = {
.name = “i2c”,
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
若匹配成功,那么就走进driver_probe_device()里边了,
定义位置:kernel/msm-4.9/drivers/base/dd.c
/**
driver_probe_device - attempt to bind device & driver together
*/
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;printk(KERN_DEBUG “======>lkh driver_probe_device enter\n”);
//检测设备是否已经注册
if (!device_is_registered(dev))
return -ENODEV;
//……
pr_debug(“bus: ‘%s’: %s: matched device %s with driver %s\n”,
drv->bus->name, func, dev_name(dev), drv->name);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);//这边走进去
//……
return ret;
}
若设备已经注册,那么调用really_probe():
定义位置:kernel/msm-4.9/drivers/base/dd.c
static int really_probe(struct device *dev, struct device_driver *drv)
{
// ……
re_probe:
//设备与驱动匹配,device里边的driver装上了其对应的driver
dev->driver = drv;/*
* 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);if (dev->bus->probe) { ret = dev->bus->probe(dev);//调用i2c_device_probe() if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev);if (ret) goto probe_failed; } //……
}
在i2c_device_probe()里边,将调用your_probe_func(),
定义位置: kernel/msm-4.9/drivers/i2c/i2c-core.c
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;if (!client)
return 0;driver = to_i2c_driver(dev->driver); // ……/* * When there are no more users of probe(), * rename probe_new to probe. */ if (driver->probe_new) status = driver->probe_new(client); else if (driver->probe)//调用your_probe_func() status = driver->probe(client, i2c_match_id(driver->id_table, client)); else status = -EINVAL; if (status) goto err_detach_pm_domain; return 0;
err_detach_pm_domain:
dev_pm_domain_detach(&client->dev, true);
err_clear_wakeup_irq:
dev_pm_clear_wake_irq(&client->dev);
device_init_wakeup(&client->dev, false);
return status;
}
至此,就调用到了你在自己驱动里边实现的your_probe_func()里边了,
关于probe函数,在里边主要做的主要工作如下:
a,给设备上电;
主要用到的函数为:regulator_get()
regulator_set_voltage()
具体上多少电,在哪一个电路上电,和你在dtsi文件的配置有关,而dtsi如何来配置,
也取决于设备在硬件上接哪路电路供电以及芯片平台对配置选项的定义。
b,初始化设备;
c,创建相关节点接口;
主要用到的函数为: sysfs_create_group();需要实现static struct attribute_group 的一个结构体以及你需要的接口,如:
static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_enable_sho
w, your_deriver_enable_store);
static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_delay_show
, your_deriver_delay_store);
static DEVICE_ATTR(debug, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_debug_show
, your_deriver_debug_store);
static DEVICE_ATTR(wake, S_IWUSR|S_IWGRP, NULL, your_d
eriver_wake_store);
static DEVICE_ATTR(rawdata, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_data_show,
NULL);
static DEVICE_ATTR(dump, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_dump_show,
NULL);
static struct attribute *your_deriver_attributes[] = {
&dev_attr_enable.attr,
&dev_attr_delay.attr,
&dev_attr_debug.attr,
&dev_attr_wake.attr,
&dev_attr_rawdata.attr,
&dev_attr_dump.attr,
NULL
};
static struct attribute_group your_driver_attribute_group = {
.attrs = your_driver_attributes
};
sysfs_create_group(&p_data->input_dev->dev.kobj, &your_driver_attribute_group);
创建完节点后,就可以在linux应用层空间通过操作节点来调用驱动里边的对应函数了。
如,对节点enable,delay的read(或cat),write(或echo),就对应到其驱动实现的
your_deriver_enable_show, your_deriver_enable_store
your_deriver_delay_show, your_deriver_delay_store
linux 驱动probe 被调用流程分析 理解1相关推荐
- linux 驱动probe 被调用流程分析
前言: 对于linux platform device 和driver,一个driver可对应多个device,通过名字进行匹配,调用驱动里边实现的probe函数,本文以一个i2c设备为例,从驱动的i ...
- NORDIC Thingy:52 蓝牙 BLE 服务 SoC 程序调用流程分析之八, 网盘分享 PPT
NORDIC Thingy:52 蓝牙 BLE 服务 SoC 程序调用流程分析之八 网盘分享 PPT https://pan.baidu.com/s/1BuhqsBwQIz0Zlwzy7u7SQw
- SDIO_WiFi驱动学习之安卓WLAN架构介绍及调用流程分析
一.引言 上一篇博客介绍了MMC子系统和SDIO相关知识及架构,这一篇博客则分析一下安卓的WLAN架构及上层如何调用WLAN驱动的流程. 虽然我工作的开发环境是安卓系统,但由于我不是专业的安卓应用/框 ...
- 08.音频系统:第003课_Linux音频驱动程序:第003节_耳麦拔插事件调用流程分析
在前面的小节中,我们编写了一个驱动程序,模拟耳机的插拔事件,其可以上报耳机的拔插事件,并且修改了android的源代码,可以根据耳机的拔插事件,在状态栏上现实或者消除耳麦的图标,这节视频我们讲解耳麦插 ...
- Dubbo comsumer 远程调用流程分析
简单代码示例: <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --><dubbo:application name="demo-consumer ...
- 8.5.3耳麦拔插事件调用流程分析
目录 回顾 input系统方法 回顾 在前面的小节中,我们编写了一个驱动程序,模拟耳机的插拔事件,其可以上报耳机的拔插事件,并且修改了android的源代码,可以根据耳机的拔插事件,在状态栏上现实或者 ...
- Java基础之《netty(30)—RPC调用流程分析》
一.RPC基本介绍 1.RPC(Remote Procedure Call)-远程过程调用,是一个计算机通信协议.该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序无需额外的为这个交互作 ...
- slimphp中间件调用流程的理解
slimphp是一款微型php框架,主要是处理http请求,并调用合适的程序处理,并返回一个http响应. 它遵循php的psr7规范,可以很方便的集成其它遵循psr7规范的php组建. 当读到中间件 ...
- linux pinctl 分析,Linux驱动架构之pinctrl子系统分析(一)
1.前言 在嵌入式系统中,许多SoC的内部都包含了pin控制器,通过芯片内部的pin控制器,我们可以配置一个或者一组引脚的状态和功能特性,Linux内核为了统一各SoC厂商的引脚管理,提供了pinct ...
最新文章
- “未来已来,共赢未来!” -- 我眼中的Citrix Summit 2017 - Part 2
- 手机app 有没有window.location.href_热议小程序使用场景越来越多,未来有没有可能替代手机APP?...
- DirectX11 With Windows SDK--22 立方体映射:静态天空盒的读取与实现
- 前端资源构建-Grunt环境搭建
- 从零开始学python数据分析-【01】从零开始学Python—数据分析与挖掘概述
- 做一个像Keras在线技术文档(Sphinx + GitHub + Read the Docs)
- 在RHEL上实现OpeenSSH
- Lfie has sweet
- TWebBrowser禁止弹出Alert对话框
- [driver]linux内核动态加载模块
- 查看UNIX/Linux资源占用的top命令
- Mybatis中SqlMapConfig.xml配置文件的使用
- 如何从rpm包中提取文件
- linux7 vi 末行 快捷键,vi 常用操作快捷键
- python自动化办公excel-自动化办公:python 操作Excel
- IIS出现 分析器错误消息: 在应用程序级别之外使用注册为 allowDefinition='MachineToApplication' 的节是错误的...
- 解决串口数据接收,实际值FF,接收却是FFFFFFFF
- 数据挖掘导论完整版pdf中文
- python-selenium 自动化弹幕
- Swift learning part 18 - 类型转换
热门文章
- 浪潮服务器sa5212m4重装阵列卡信息,浪潮英信服务器SA5212M4
- 二级计算机题库上机ppt考试,计算机二级考试MSOffice考试题库ppt操作题附答案
- 免是计算机科学与技术,西南交大计算机科学与技术2016免研课程.docx
- mysql使用新建用户的好处_入门MySQL——用户与权限
- java内存数据被篡改,初涉Java内存模型
- java面向对象设计_Java面向对象设计 构造函数设计
- 用Python一键生成微信个人专属数据报告,了解你的微信社交历史
- springboot+vue 审批工作流集成flowable(springboot实现工作流)
- centos 7(1611)安装笔记
- 华为设备二层协议透明传输命令