linux 驱动probe 被调用流程分析
前言: 对于linux platform device 和driver,一个driver可对应多个device,通过名字进行匹配,调用驱动里边实现的probe函数,本文以一个i2c设备为例,从驱动的i2c_add_driver()开始看源码以及用比较笨的打log的方式分析如何一步一步调用的probe()函数。
分析的代码基于linux kernel msm-4.9。
/****************************************/
从module_init()开始看,
定义位置:kernel/msm-4.9/include/linux/module.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/msm-4.9/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 被调用流程分析相关推荐
- linux 驱动probe 被调用流程分析 理解1
原文链接:https://blog.csdn.net/goodnight1994/article/details/82082089 修改个别错误呈现 前言: 对于linux platform devi ...
- 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)-远程过程调用,是一个计算机通信协议.该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序无需额外的为这个交互作 ...
- linux pinctl 分析,Linux驱动架构之pinctrl子系统分析(一)
1.前言 在嵌入式系统中,许多SoC的内部都包含了pin控制器,通过芯片内部的pin控制器,我们可以配置一个或者一组引脚的状态和功能特性,Linux内核为了统一各SoC厂商的引脚管理,提供了pinct ...
- Linux 驱动 tty终端 调用过程分析
内核版本: 2.6.32.2 仍在修改中 2018.12.21 需进一步了解内核启动过程中start_kernel以及module_init调用 module_init(serial8250_init ...
最新文章
- mysql 密码加密windows_Windows下mysql数据库的安装及8.0以上版本设置密码的命令(简易教学)...
- 音频处理七:(极坐标转换)
- Boost.Flyweight 键值测试的类
- java获取数据库数据保存到本地txt文件中
- 机器学习(十五)——loss function比较、独立成分分析
- oracle 结果缓存,Result cache结果缓存
- java中FileInputStream和OutputStream解析
- STM32(四)--------按键控制LED灯
- 阿里研究院:解读互联网经济十大议题
- 移动web-使一个盒子水平垂直居中的六种方法
- Android组件间的数据传输
- 3. 安装 phpmyadmin
- 现代多媒体通信技术【问答、分析、论述】
- 三星s8清除html查看器,三星Galaxy S8/S8+双清教程_怎么清理数据缓存和恢复出厂设置...
- 黑客穷追不舍攻击“谷姐” [转]
- java操作mysql视图_MySQL使用视图
- gatk过滤_GATK使用方法详解(原始数据的处理)
- 【软件下载】常用安装包下载链接
- centos系统使用pptpd搭建在windows客户端的vpn服务器
- 【外文翻译】外国友人写得很不错的Java Lambda表达式入门教程,我终于翻译好给大家啦!!!...
热门文章
- https、ssl证书基本信息、证书链
- 计算机新生必看:10道逻辑思维训练题,做完让你成为大佬!!(附带答案)
- php xdebug 解密,php xdebug 完整篇
- 工业应用中如何选取合适的损失函数(MAE、MSE、Huber)-Pytorch版
- 嘉立创元器件及封装导入AD20
- datagrid可编辑表格
- java ip段 匹配_IP段匹配
- java包装类和基本类型_你知道Java中基本类型和包装类的区别吗
- 蓝桥杯:X星球的迷宫————Python题解
- multisim收敛助手有啥用_登记注册小助手“减肥”了……