一、先看屏驱动里面
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相关推荐

  1. [驱动注册]platform_driver_register()与platform_device_register()

    [驱动注册]platform_driver_register()与platform_device_register()      设备与驱动的两种绑定方式:在设备注册时进行绑定及在驱动注册时进行绑定. ...

  2. MTK平台闪光灯驱动分析

    MTK平台闪光灯驱动分析   以前没写过博客,总想写着来着,把之前学到的做过的东西都记录下来,但是一直没有时间也没那么大的决心.这次趁着刚换工作,正在学习熟悉平台不是太忙的机会,把自己总结的文档写下来 ...

  3. LCD驱动分析(一)

    LCD驱动分析 转载自http://blog.chinaunix.net/uid-26021340-id-3011787.html S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 ...

  4. (linux)MMC 卡驱动分析

    最近花时间研究了一下 MMC 卡驱动程序,开始在网上找了很多关于 MMC 卡驱动的分析文章,但大都是在描述各个层,这对于初学者来讲帮助并不大,所以我就打算把自己的理解写下来,希望对大家有用.个人觉得理 ...

  5. 【分析笔记】全志 i2c-sunxi.c 控制器驱动分析

    分析平台:全志 A64 内核版本:Linux 4.9 数据手册:Allwinner_A64_User_Manual_V1.1.pdf (whycan.com) 驱动框架 I2C 设备驱动 作为方案应用 ...

  6. 《Linux驱动:s3c2440 lcd 驱动分析》

    文章目录 一,前言 二,LCD原理和硬件分析 2.1 LCD原理解析 2.2 硬件电路 2.2.1 LCD背光电路 2.2.2 LCD屏 2.2.3 S3c2440主控 三,LCD应用平台总线-设备- ...

  7. linux串口驱动分析

    linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作 ...

  8. linux 串口驱动 atmel_set_mctrl何时调用,linux uart serial使用驱动分析

    uart tty serial 驱动分析 内核版本3.14.23 以atmel为例: 起点: static int __init atmel_serial_init(void) { int ret; ...

  9. s5k4ba摄像头驱动分析

    s5k4ba摄像头驱动分析 注释: 本驱动是基于S5PV310的,但是全天下的摄像头驱动都是采用V4L2,因此驱动框架流程基本差不多.其中fimc_init_camera()函数会回调.init函数, ...

最新文章

  1. 用matlab怎么画视电阻率拟断面图,在MATLAB平台上实现可控源音频大地电磁反演数据三维可视化显示...
  2. 华为harmonyos 2.0,华为王成录博士:HarmonyOS 2.0给消费者不一样的体验
  3. 网站收录的提升离不开“方法”和“坚持”
  4. 美团一面:你既然写过Mybatis插件,能给我说说它底层是怎么加载一个自定义插件的吗?...
  5. html请求接口_python接口自动化测试 - 2.Django开发接口
  6. 中国程序员 VS 美国程序员,差距就在这五点
  7. Uva 1625 - Color Length(DP)
  8. kafka内存不断增加_为什么 Kafka 能这么快的 6 个原因
  9. Eclipse 常用快捷键,实战经典
  10. 我从机器人先生那里了解到了有关InfoSec的全部信息
  11. java的关键字和保留字_Java关键字和保留字及其含义
  12. 数据结构vl怎么求_数据结构
  13. K-means 聚类算法的图像区域分割
  14. Linux系统下卸载jdk的步骤
  15. SCSI子系统基础学习笔记 - 3. SCSI设备探测
  16. 21届本科大数据菜鸡:我是怎么在互联网寒冬拿到腾讯、华为、京东、美团、快手等大厂offer的?
  17. 修仙第一步:凌晨打坐
  18. c#调用企业微信服务端API发送消息和图片
  19. 中小学直播活动如何选择编码器还是直播机?
  20. PASCAL VOC2012类型的数据集生成train.txt\val.txt等文件

热门文章

  1. python导出微信群成员信息_python 使用wxpy实现获取微信好友列表 头像 群成员
  2. 第1章第18节:如何利用批注功能向作者提出幻灯片的修改意见 [PowerPoint精美幻灯片实战教程]
  3. PHP四种序列化方案
  4. linux哪些程序需要指定终端,Linux程序员福利 - 追女友神奇(Linux终端运行炫酷程序)...
  5. Xilinx公司原语的使用方法
  6. [置顶][原创]差分放大器阻抗匹配计算+阻抗计算小工具
  7. 5分钟集成桌面摄像头或高拍仪到Web程序
  8. jxl.read.biff.BiffException: Unable to recognize OLE stream
  9. 山药蛋团队每日站立会议
  10. SSRF漏洞的利用与攻击内网应用