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)


相关推荐

发表回复

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

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