Platform device and platform driver

Platform device and platform driverPlatformdevice是专门给嵌入式系统设计的设备类型,一般在移植内核到自己的开发板时,基本上注册的所有的设备的类型全是platformdevice。实际上,platform在Linux内核中是以一条总线的身份登场的,要想让这样的总线和设备一起完美的工作,必须首先在系统

大家好,又见面了,我是你们的朋友全栈君。 

Platform device是专门给嵌入式系统设计的设备类型,一般在移植内核到自己的开发板时,基本上注册的所有的设备的类型全是platform device。实际上,platform在Linux内核中是以一条总线的身份登场的,要想让这样的总线和设备一起完美的工作,必须首先在系统初始化的比较早的阶段声明并注册平台设备,注册时的设备名作为设备的唯一标识,在随后的驱动加载阶段,和驱动的驱动名进行匹配,如果这两个字符串相同,那么即宣告设备找到驱动,或是驱动找到设备,接着才会进一步调用platform driver的probe成员函数进行设备的初始化并注册对应的字符、块或是网络设备。这也就是我们阅读驱动代码时,通常在代码中都有一个名为XXX_probe的函数,而且特别长的原因。

定义平台设备,只需声明一个静态的类型为struct platform_device的全局变量就行了,struct platform_device定义如下:

struct platform_device {

       const char       * name;

       int          id;

       struct device   dev;

       u32         num_resources;

       struct resource * resource;

};

成员介绍

name:平台设备名,是这个设备与设备驱动可以相认的关键保证,所以在这个嵌入式平台上它一定要唯一(除非大家都适用同一驱动),而且要和它的驱动名同名。

id:   一般初始化为-1,此时设备名沿用初值,如果是其他值,那设备名会被格式化为name.id的形式。相关代码见函数platform_device_add()。

dev: 内嵌struct device结构体,在内核驱动模型里面代表一个设备,基本上接下来很多关系都靠这个结构体来打通。这个结构体还有些成员可以稍作初始化,来向驱动传递更多有关设备的信息。

num_resources与resource:这个设备所占用的系统资源,这种资源一般有IO、内存、中断、DMA等,有多少资源,就定义多少个struct resource类型的数组成员,同时把资源个数放在num_resources就行了。

 

下面以mini2440开发板的LCD设备为例介绍platform_device的前世今生。

在mini2440开发板上,他的LCD设备定义变量初始化如下

struct platform_device s3c_device_lcd = {

       .name               = “s3c2410-lcd”,

       .id            = -1,

       .num_resources        = ARRAY_SIZE(s3c_lcd_resource),

       .resource   = s3c_lcd_resource,

       .dev              = {

              .dma_mask            = &s3c_device_lcd_dmamask,

              .coherent_dma_mask     = 0xffffffffUL

       }

};

 

 

由于2440的LCD控制器与2410的在同一地址上且诸控制寄存器也相同,故沿用的是2410的设置。s3c_lcd_resource代码如下:

static struct resource s3c_lcd_resource[] = {

       [0] = {

              .start = S3C24XX_PA_LCD,

              .end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD – 1,

              .flags = IORESOURCE_MEM,

       },

       [1] = {

              .start = IRQ_LCD,

              .end   = IRQ_LCD,

              .flags = IORESOURCE_IRQ,

       }

 

};

在init_machine里面还调用了函数s3c24xx_fb_set_platdata()来设置平台设备内嵌dev成员的platform_data 成员,代码如下:

void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)

{

       struct s3c2410fb_mach_info *npd;

 

       npd = kmalloc(sizeof(*npd), GFP_KERNEL);

       if (npd) {

              memcpy(npd, pd, sizeof(*npd));

              s3c_device_lcd.dev.platform_data = npd;

       } else {

              printk(KERN_ERR “no memory for LCD platform data\n”);

       }

}

 

同时在函数init_machine()比较末尾的地方注册系统中的设备,当这个函数返回时,系统这就存在注册的平台设备了,但是现在还不能用,因为设备只是形式上声明了自己的存在,并没有可用的驱动和它关联,所以内核的移植工作到这里还没有完成!

要让设备工作,必须要加载设备的驱动,平台设备的驱动一般都存在于$kernel dir$/drivers下面,驱动的起点基本上都是以module_init()开始的。在这个函数中放入一个init函数,init函数里面使用已经初始化好的平台驱动结构体并调用platform_driver_register()注册平台驱动,此函数代码如下所示:

int platform_driver_register(struct platform_driver *drv)

{

       drv->driver.bus = &platform_bus_type;

       if (drv->probe)

              drv->driver.probe = platform_drv_probe;

       if (drv->remove)

              drv->driver.remove = platform_drv_remove;

       if (drv->shutdown)

              drv->driver.shutdown = platform_drv_shutdown;

       if (drv->suspend)

              drv->driver.suspend = platform_drv_suspend;

       if (drv->resume)

              drv->driver.resume = platform_drv_resume;

       return driver_register(&drv->driver);

}

具体实例:

int __init s3c2410fb_init(void)

{

       int ret = platform_driver_register(&s3c2410fb_driver);

 

       if (ret == 0)

              ret = platform_driver_register(&s3c2412fb_driver);;

 

       return ret;

}

module_init(s3c2410fb_init);

调用流程为:

platform_driver_register()àdrv->driver.probe = platform_drv_probe;à driver_register()àbus_add_driver()àdriver_attach()à__driver_attach()àdrv->bus->match()àdriver_probe_device()àreally_probe()àdrv->probe(dev)(即platform_drv_probe())àdrv->probe(dev);此时调用的才是自己实现的probe函数,够波折吧。

以上调用流程中比较重要环节是drv->bus->match(),如果这里调用失败,那么说明设备和驱动不匹配,此驱动不适用这个设备,所以给一个设备注册驱动时,关键是要让这个函数的调用返回非零!要如何才能使这个函数调用成功呢?先来看看它的代码再说:

static int platform_match(struct device *dev, struct device_driver *drv)

{

       struct platform_device *pdev;

 

       pdev = container_of(dev, struct platform_device, dev);

       return (strcmp(pdev->name, drv->name) == 0);

}

注意到上面以红色展示的代码语句了吗,这是在比较驱动名与设备名啊,这个就是上面我提到的,平台设备的名字是他和驱动相认的关键!如果这两个名字一样了,那么注册的平台设备驱动的probe被调用就成了既定的事实了,最后这个驱动是否确实适合这个设备,或是相关额外的考虑,就要看此probe函数的返回值了,如果返回0,那么万事大吉,驱动和设备门当户对,如果非零,那可能在probe阶段驱动和设备出现了点小矛盾,谈不拢,最后还是分开了。

至此,该说的也说得差不多了,就此打住吧~~~

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/163815.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • QStringList 去除重复项算法赏析

    QStringList 去除重复项算法赏析测试用例:QStringListlist;list<<"2222"<<"xxx"<<"1111"<<"2222"<<"xxxx"<<"1111";intn=list.removeDuplica

    2022年6月6日
    390
  • 办公小贴士之:Office 2010截图有妙招

    办公小贴士之:Office 2010截图有妙招

    2022年3月11日
    39
  • 模拟电子线路复习笔记( 六) —— 集成运算放大器原理及其运用「建议收藏」

    模拟电子线路复习笔记( 六) —— 集成运算放大器原理及其运用「建议收藏」模拟电子线路复习笔记(六)——集成运算放大器原理及其运用本文是对模电的第六章的集成运算放大器原理及其运用知识点的笔记总结。全文手写,附有例题解析,帮助加深理解。1.知识点总结2.习题解析…

    2022年6月8日
    35
  • 前端VSCode常用插件及安装

    前端VSCode常用插件及安装新手前端VSCode常用插件及其安装方法

    2022年7月25日
    8
  • 前端开发技术(vscode怎么下载)

    前言   在前端开发中,有一个非常好用的工具,VisualStudioCode,简称VScode。   都不用我安利VScode,大家就会乖乖的去用,无数个大言不惭的攻城狮,都被VScode比德芙还丝滑的强大功能所折服。   我是来给大家安利插件的,想做个比较全面的插件集合给大家。网上的我也看过一些,但是都比较零散,时间也久了一些,我结合最近的情况,总结一下,造福大家,才是我…

    2022年4月10日
    52
  • 1192啥意思_有向图的拓扑排序算法

    1192啥意思_有向图的拓扑排序算法由于无敌的凡凡在2005年世界英俊帅气男总决选中胜出,Yali Company总经理Mr.Z心情好,决定给每位员工发奖金。公司决定以每个人本年在公司的贡献为标准来计算他们得到奖金的多少。于是Mr.Z下令召开 m 方会谈。每位参加会谈的代表提出了自己的意见:“我认为员工 a 的奖金应该比 b 高!”Mr.Z决定要找出一种奖金方案,满足各位代表的意见,且同时使得总奖金数最少。每位员工奖金最少为100元,且必须是整数。输入格式第一行包含整数 n,m,分别表示公司内员工数以及参会代表数。接下来 m

    2022年8月9日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号