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阶段驱动和设备出现了点小矛盾,谈不拢,最后还是分开了。

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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Linux NFS配置固定端口[通俗易懂]

    Linux NFS配置固定端口[通俗易懂]需求介绍:生产上想要利用NFS实现共享,由于生产规则防火墙仅开放了22端口,此时我们需要开启NFS服务端口但是NFS启动时会随机启动多个端口并向RPC注册.为了设置安全组以及防火墙规则,此时就需要设置NFS固定端口。NFS服务需要开启mountd,nfs,nlockmgr,portmapper,rquotad这5个服务.其中nfs、portmapper的端口是固定的.另外三个服务的端口是随机分配的.所以需要给mountd,nlockmgr,rquotad设置固定的端口。1.给mo

  • notepad正则删除关键词所在行

    notepad正则删除关键词所在行

  • pytest测试框架和unittest_python列表生成式

    pytest测试框架和unittest_python列表生成式一、核心要素(1)TestCase测试用例一个.py脚本就是一个测试用例测试用例类必须继承于unittest.TestCase类中的方法名必须以test开头,否则不会被执行类的外部写main方法(自测代码)(2)TestSuite测试套件测试套件就是测试用例的集合把多个测试用例集合到一起就构成了测试套件测试套件不能直接执行(3)TextTestR…

    2022年10月14日
  • FilterDispatcher_Executed

    FilterDispatcher_Executedorg.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterorg.apache.struts2.dispatcher.FilterDispatcherFilterDispatcher是早期struts2的过滤器,后期的都用StrutsPrepareAndExecuteFilter了,如2.1.6、2.1.

  • Ubuntu安装GCC编译器

    Ubuntu安装GCC编译器没有gcc啊如何安装gccapt-getinstallbuild-essential

  • MYSQL中TINYINT的取值范围

    原文地址:https://blog.csdn.net/lysygyy/article/details/5983433在MySQL的数据类型中,Tinyint的取值范围是:带符号的范围是-128到127。无符号的范围是0到255(见官方《MySQL5.1参考手册》http://dev.mysql.com/doc/refman/5.1/zh/column-types.html#numeri…

发表回复

您的电子邮箱地址不会被公开。

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