从RDA平台屏驱动分析platform_driver_register、platform_device_register
一、先看屏驱动里面
static struct platform_driver rda_fb_panel_HX8357D_mcu_driver = {
.probe = rda_fb_panel_HX8357D_mcu_probe,
.remove = rda_fb_panel_HX8357D_mcu_remove,
.driver = {
.name = HX8357D_MCU_PANEL_NAME}
};
static int __init rda_fb_panel_HX8357D_mcu_init(void)
{
rda_fb_probe_panel(&HX8357D_mcu_info, &rda_fb_panel_HX8357D_mcu_driver);
return platform_driver_register(&rda_fb_panel_HX8357D_mcu_driver);
}
module_init(rda_fb_panel_HX8357D_mcu_init);
如果要使platform_driver 里的probe函数正常执行,那么platform_driver_register的时候必须要match相同name的platform_device。
我们来找下相同name的platform_device。由于module_init()后执行了rda_fb_probe_panel(),这个函数做了自动检测不同型号LCD的操作,同时执行以下代码
printk("lcd %s is selected\n", p_info->name);
if (p_info->lcd.lcd_interface == GOUDA_LCD_IF_DBI) {
struct platform_driver *plat_drv = (struct platform_driver *)p_driver;
plat_drv->driver.name = RDA_DBI_PANEL_DRV_NAME;
}
所以platform_driver name变为了RDA_DBI_PANEL_DRV_NAME
通过跟踪代码发现具有相同name的platform_device位于
Z:\rdadroid-4.2.2_r1-rel2.8\src\kernel\arch\arm\mach-rda\devices.c
static struct platform_device rda_fb_panel = {
.name = RDA_DBI_PANEL_DRV_NAME,
.id = -1,
.dev = {
.platform_data = NULL,
},
};
二、系统是先注册的设备还是先注册的驱动?
1、Kernel启动
Z:\rdadroid-4.2.2_r1-rel2.8\src\kernel\init\main.c
start_kernel(void)->
rest_init();->
kernel_thread(kernel_init,…….);->
do_basic_setup()->
do_initcalls(void)->
do_initcall_level(int level)->
这里就会去调用不同级别的initcall
do_initcalls()函数
那些入口函数的调用由do_initcalls函数来完成。
do_initcall 函数通过for循环,由__initcall_start开始,直到__initcall_end结束,依次调用识别到的初始化函数。而位于 __initcall_start和__initcall_end之间的区域组成了.initcall.init节,其中保存了由 xxx_initcall形式的宏标记的函数地址,do_initcall函数可以很轻松的取得函数地址并执行其指向的函数。
.initcall.init节所保存的函数地址有一定的优先级,越前面的函数优先级越高,也会比位于后面的函数先被调用。
各initcall函数调用优先级别定义在
Z:\rdadroid-4.2.2_r1-rel2.8\src\kernel\include\linux\init.h
*/
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
#define __initcall(fn) device_initcall(fn)
#define module_init(x) __initcall(x);
2、RDA平台设备初始化
Z:\rdadroid-4.2.2_r1-rel2.8\src\kernel\arch\arm\mach-rda\devices.c
找到rda_init_devices()->
platform_add_devices(devices, ARRAY_SIZE(devices))->
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
static struct platform_device *devices[] __initdata = {
&rda_fb_panel,
}
static struct platform_device rda_fb_panel = {
.name = RDA_DBI_PANEL_DRV_NAME,// "rda-dbi-panel"
.id = -1,
.dev = {
.platform_data = NULL,
},
};
3、针对RDA_DBI_PANEL_DRV_NAME这个设备,是先注册的设备还是先注册的驱动?
通过跟踪代码发现rda_init_devices()调用流程如下
kernel/arch/arm/kernel/setup.c
static int __init customize_machine(void)
{
/* customizes platform devices, or adds new ones */
if (machine_desc->init_machine)
machine_desc->init_machine();
return 0;
}
arch_initcall(customize_machine);
kernel/arch/arm/mach-rda/board-rda8810.c
.init_machine = rda8810_init,
rda8810_init()->
rda_init_devices()->
arch_initcall()调用优先级高于module_init(),所以系统会先去platform_device_register将设备添加到设备链表
三、驱动是如何probe到设备的,以及设备是如何probe到驱动的?
由二知道系统优先执行了arch_initcall(customize_machine)–>platform_device_register将设备登记到设备链表
随后module_init(rda_fb_panel_HX8357D_mcu_init);->platform_driver_register()
int platform_driver_register(struct platform_driver *drv)
{
return driver_register(&drv->driver);
}
int driver_register(struct device_driver *drv)
{
ret = bus_add_driver(drv);
}
int bus_add_driver(struct device_driver *drv)
{
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
}
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
//到这里遍历总线上的设备并掉用__driver_attach
}
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (!driver_match_device(drv, dev))
return 0;
//调用的驱动的总线上的match函数。如果返回1,则可以继续,否则就Done了。
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);//
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
ret = really_probe(dev, drv);
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);//如果probe非空则调用probe
if (ret)
goto probe_failed;
}
}
rda_fb_panel_HX8357D_mcu_driver注册成功并探测匹配到设备后执行probe函数
Probe函数执行会去登记"rda-fb"的设备,然后设备去探测名为rda-fb的驱动,匹配成功后执行rda_fb_probe函数。
为什么说是设备去找驱动?因为注册设备之前rda-fb的驱动已添加到driver链表
设备probe驱动分析如下:
.probe = rda_fb_panel_HX8357D_mcu_probe,
static int rda_fb_panel_HX8357D_mcu_probe(struct platform_device *pdev)
{
rda_fb_register_panel(&HX8357D_mcu_info);
dev_info(&pdev->dev, "rda panel HX8357D_mcu registered\n");
return 0;
}
int platform_device_register(struct platform_device *pdev)
{
return platform_device_add(pdev);
}
int platform_device_add(struct platform_device *pdev)
{
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
}
int device_add(struct device *dev)
{
error = bus_add_device(dev);
if (error)
goto BusError;
bus_probe_device(dev);
}
void bus_probe_device(struct device *dev)
{
if (bus->p->drivers_autoprobe) {
ret = device_attach(dev);
WARN_ON(ret < 0);
}
}
int device_attach(struct device *dev)
{
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
if (!driver_match_device(drv, dev))
return 0;
//调用的驱动的总线上的match函数。如果返回1,则可以继续,否则就Done了。
return driver_probe_device(drv, dev);
}
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
ret = really_probe(dev, drv);
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);//如果probe非空则被调用
if (ret)
goto probe_failed;
}
}
疑问:
if (drv->probe) 的drv类型为struct device_driver * 为何最终调用到了struct platform_driver里的probe?
rda_fb_panel_HX8357D_mcu_probe指定的参数类型为struct platform_device *,而最终传递给drv->probe()的dev类型为struct device *?
因为注册驱动前程序给drv->driver.probe赋值了platform_drv_probe;的函数指针
int platform_driver_register(struct platform_driver *drv)
{
if (drv->probe)
drv->driver.probe = platform_drv_probe;
}
所以每当跑到relly_probe()执行ret = drv->probe(dev)的时候就会实际调用到platform_drv_probe(struct device *_dev)
函数原型如下:
static int platform_drv_probe(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
return drv->probe(dev);
}
#define to_platform_driver(drv) (container_of((drv), struct platform_driver, driver))
#define to_platform_device(x) container_of((x), struct platform_device, dev)
通过struct device *_dev找到所在结构体的首地址,最终调用到platform_driver结构体里面probe所指向的函数
从RDA平台屏驱动分析platform_driver_register、platform_device_register相关推荐
- [驱动注册]platform_driver_register()与platform_device_register()
[驱动注册]platform_driver_register()与platform_device_register() 设备与驱动的两种绑定方式:在设备注册时进行绑定及在驱动注册时进行绑定. ...
- MTK平台闪光灯驱动分析
MTK平台闪光灯驱动分析 以前没写过博客,总想写着来着,把之前学到的做过的东西都记录下来,但是一直没有时间也没那么大的决心.这次趁着刚换工作,正在学习熟悉平台不是太忙的机会,把自己总结的文档写下来 ...
- LCD驱动分析(一)
LCD驱动分析 转载自http://blog.chinaunix.net/uid-26021340-id-3011787.html S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 ...
- (linux)MMC 卡驱动分析
最近花时间研究了一下 MMC 卡驱动程序,开始在网上找了很多关于 MMC 卡驱动的分析文章,但大都是在描述各个层,这对于初学者来讲帮助并不大,所以我就打算把自己的理解写下来,希望对大家有用.个人觉得理 ...
- 【分析笔记】全志 i2c-sunxi.c 控制器驱动分析
分析平台:全志 A64 内核版本:Linux 4.9 数据手册:Allwinner_A64_User_Manual_V1.1.pdf (whycan.com) 驱动框架 I2C 设备驱动 作为方案应用 ...
- 《Linux驱动:s3c2440 lcd 驱动分析》
文章目录 一,前言 二,LCD原理和硬件分析 2.1 LCD原理解析 2.2 硬件电路 2.2.1 LCD背光电路 2.2.2 LCD屏 2.2.3 S3c2440主控 三,LCD应用平台总线-设备- ...
- linux串口驱动分析
linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作 ...
- linux 串口驱动 atmel_set_mctrl何时调用,linux uart serial使用驱动分析
uart tty serial 驱动分析 内核版本3.14.23 以atmel为例: 起点: static int __init atmel_serial_init(void) { int ret; ...
- s5k4ba摄像头驱动分析
s5k4ba摄像头驱动分析 注释: 本驱动是基于S5PV310的,但是全天下的摄像头驱动都是采用V4L2,因此驱动框架流程基本差不多.其中fimc_init_camera()函数会回调.init函数, ...
最新文章
- 用matlab怎么画视电阻率拟断面图,在MATLAB平台上实现可控源音频大地电磁反演数据三维可视化显示...
- 华为harmonyos 2.0,华为王成录博士:HarmonyOS 2.0给消费者不一样的体验
- 网站收录的提升离不开“方法”和“坚持”
- 美团一面:你既然写过Mybatis插件,能给我说说它底层是怎么加载一个自定义插件的吗?...
- html请求接口_python接口自动化测试 - 2.Django开发接口
- 中国程序员 VS 美国程序员,差距就在这五点
- Uva 1625 - Color Length(DP)
- kafka内存不断增加_为什么 Kafka 能这么快的 6 个原因
- Eclipse 常用快捷键,实战经典
- 我从机器人先生那里了解到了有关InfoSec的全部信息
- java的关键字和保留字_Java关键字和保留字及其含义
- 数据结构vl怎么求_数据结构
- K-means 聚类算法的图像区域分割
- Linux系统下卸载jdk的步骤
- SCSI子系统基础学习笔记 - 3. SCSI设备探测
- 21届本科大数据菜鸡:我是怎么在互联网寒冬拿到腾讯、华为、京东、美团、快手等大厂offer的?
- 修仙第一步:凌晨打坐
- c#调用企业微信服务端API发送消息和图片
- 中小学直播活动如何选择编码器还是直播机?
- PASCAL VOC2012类型的数据集生成train.txt\val.txt等文件
热门文章
- python导出微信群成员信息_python 使用wxpy实现获取微信好友列表 头像 群成员
- 第1章第18节:如何利用批注功能向作者提出幻灯片的修改意见 [PowerPoint精美幻灯片实战教程]
- PHP四种序列化方案
- linux哪些程序需要指定终端,Linux程序员福利 - 追女友神奇(Linux终端运行炫酷程序)...
- Xilinx公司原语的使用方法
- [置顶][原创]差分放大器阻抗匹配计算+阻抗计算小工具
- 5分钟集成桌面摄像头或高拍仪到Web程序
- jxl.read.biff.BiffException: Unable to recognize OLE stream
- 山药蛋团队每日站立会议
- SSRF漏洞的利用与攻击内网应用