linux platform 驱动模型分析


一. 概述
    platform设备和驱动与linux设备模型密切相关。platform在linux设备模型中,其实就是一种虚拟总线没有对应的硬件结构。它的主要作用就是管理系统的外设资源,比如io内存,中断信号线。现在大多数处理器芯片都是soc,如s3c2440,它包括处理器内核(arm920t)和系统的外设(lcd接口,nandflash接口等)。linux在引入了platform机制之后,内核假设所有的这些外设都挂载在platform虚拟总线上,以便进行统一管理。
二. platform 总线
   1. 在系统中platform对应的文件drivers/base/platform.c,它不是作为一个模块注册到内核的,关键的注册总线的函数由系统初始化部分,对应/init/main.c中的do_basic_setup函数间接调用。这里可以看出platform非常重要,要在系统其他驱动加载之前注册。下面分析platform总线注册函数
[cpp] view plaincopy
  1. int __init platform_bus_init(void)  
  2. {  
  3.     int error;  
  4.     early_platform_cleanup();  
  5.     error = device_register(&platform_bus);  
  6.         //总线也是设备,所以也要进行设备的注册  
  7.     if (error)  
  8.         return error;  
  9.     error =  bus_register(&platform_bus_type);  
  10.         //注册platform_bus_type总线到内核  
  11.     if (error)  
  12.         device_unregister(&platform_bus);  
  13.     return error;  
  14. }  
    这个函数向内核注册了一种总线。他首先由/drivers/base/init.c中的driver_init函数调用,driver_init函数由/init/main.c中的do_basic_setup函数调用,do_basic_setup这个函数由kernel_init调用,所以platform总线是在内核初始化的时候就注册进了内核。
   2. platform_bus_type 总线结构与设备结构
    (1) platform总线 设备结构
[cpp] view plaincopy
  1. struct device platform_bus = {  
  2.     .init_name  = "platform",  
  3. };  
    platform总线也是一种设备,这里初始化一个device结构,设备名称platform,因为没有指定父设备,所以注册后将会在/sys/device/下出现platform目录。
    (2) platform总线 总线结构
[cpp] view plaincopy
  1. struct bus_type platform_bus_type = {  
  2.     .name       = "platform",  
  3.     .dev_attrs  = platform_dev_attrs,   
  4.     .match      = platform_match,  
  5.     .uevent     = platform_uevent,  
  6.     .pm     = &platform_dev_pm_ops,  
  7. };  
    platform_dev_attrs    设备属性
    platform_match        match函数,这个函数在当属于platform的设备或者驱动注册到内核时就会调用,完成设备与驱动的匹配工作。
    platform_uevent       热插拔操作函数
三. platform 设备
   1. platform_device 结构
[cpp] view plaincopy
  1. struct platform_device {  
  2.     const char  * name;  
  3.     int     id;  
  4.     struct device   dev;  
  5.     u32     num_resources;  
  6.     struct resource * resource;  
  7.     struct platform_device_id   *id_entry;  
  8.     /* arch specific additions */  
  9.     struct pdev_archdata    archdata;  
  10. };  
    (1)platform_device结构体中有一个struct resource结构,是设备占用系统的资源,定义在ioport.h中,如下
[cpp] view plaincopy
  1. struct resource {  
  2.     resource_size_t start;  
  3.     resource_size_t end;  
  4.     const char *name;  
  5.     unsigned long flags;  
  6.     struct resource *parent, *sibling, *child;  
  7. };  
    (2) num_resources 占用系统资源的数目,一般设备都占用两种资源,io内存和中断信号线。这个为两种资源的总和。
   2. 设备注册函数 platform_device_register
[cpp] view plaincopy
  1. int platform_device_register(struct platform_device *pdev)  
  2. {  
  3.     device_initialize(&pdev->dev);  
  4.     return platform_device_add(pdev);  
  5. }  
    这个函数首先初始化了platform_device的device结构,然后调用platform_device_add,这个是注册函数的关键,下面分析platform_device_add:
[cpp] view plaincopy
  1. int platform_device_add(struct platform_device *pdev)  
  2. {  
  3.     int i, ret = 0;  
  4.   
  5.     if (!pdev)  
  6.         return -EINVAL;  
  7.   
  8.     if (!pdev->dev.parent)  
  9.         pdev->dev.parent = &platform_bus;  
  10.         //可以看出,platform设备的父设备一般都是platform_bus,所以注册后的platform设备都出现在/sys/devices/platform_bus下  
  11.     pdev->dev.bus = &platform_bus_type;  
  12.         //挂到platform总线上  
  13.     if (pdev->id != -1)  
  14.         dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);  
  15.     else  
  16.         dev_set_name(&pdev->dev, "%s", pdev->name);  
  17.         //设置设备名字,这个名字与/sys/devices/platform_bus下的名字对应  
  18.     for (i = 0; i < pdev->num_resources; i++) { //下面操作设备所占用的系统资源  
  19.         struct resource *p, *r = &pdev->resource[i];  
  20.   
  21.         if (r->name == NULL)  
  22.             r->name = dev_name(&pdev->dev);  
  23.   
  24.         p = r->parent;  
  25.         if (!p) {  
  26.             if (resource_type(r) == IORESOURCE_MEM)  
  27.                 p = &iomem_resource;  
  28.             else if (resource_type(r) == IORESOURCE_IO)  
  29.                 p = &ioport_resource;  
  30.         }  
  31.   
  32.         if (p && insert_resource(p, r)) {  
  33.             printk(KERN_ERR  
  34.                    "%s: failed to claim resource %d\n",  
  35.                    dev_name(&pdev->dev), i);  
  36.             ret = -EBUSY;  
  37.             goto failed;  
  38.         }  
  39.     }  
  40.        //上面主要是遍历设备所占用的资源,找到对应的父资源,如果没有定义,那么根据资源的类型,分别赋予iomem_resource和ioport_resource,然后调用insert_resource插入资源。  
  41.        //这样系统的资源就形成了一个树形的数据结构,便于系统的管理  
  42.     pr_debug("Registering platform device '%s'. Parent at %s\n",  
  43.          dev_name(&pdev->dev), dev_name(pdev->dev.parent));  
  44.   
  45.     ret = device_add(&pdev->dev);  
  46.         //注册到设备模型中  
  47.     if (ret == 0)  
  48.         return ret;  
  49.  failed:  
  50.     while (--i >= 0) {  
  51.         struct resource *r = &pdev->resource[i];  
  52.         unsigned long type = resource_type(r);  
  53.         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)  
  54.             release_resource(r);  
  55.     }  
  56.     return ret;  
  57. }  
   3. mini2440内核注册platform设备过程
    因为一种soc确定之后,其外设模块就已经确定了,所以注册platform设备就由板级初始化代码来完成,在mini2440中是mach-mini2440.c的mini2440_machine_init函数中调用platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))来完成注册。这个函数完成mini2440的所有platform设备的注册:
    (1) platform_add_devices函数是platform_device_register的简单封装,它向内核注册一组platform设备
    (2) mini2440_devices是一个platform_device指针数组,定义如下:
[cpp] view plaincopy
  1. static struct platform_device *mini2440_devices[] __initdata = {  
  2.     &s3c_device_usb,  
  3.     &s3c_device_rtc,  
  4.     &s3c_device_lcd,  
  5.     &s3c_device_wdt,  
  6.     &s3c_device_i2c0,  
  7.     &s3c_device_iis,  
  8.     &mini2440_device_eth,  
  9.     &s3c24xx_uda134x,  
  10.     &s3c_device_nand,  
  11.     &s3c_device_sdi,  
  12.     &s3c_device_usbgadget,  
  13. };  
    这个就是mini2440的所有外设资源了,每个外设的具体定义在/arch/arm/plat-s3c24xx/devs.c,下面以s3c_device_lcd为例说明,其他的类似。s3c_device_lcd在devs.c中它定义为:
[cpp] view plaincopy
  1. struct platform_device s3c_device_lcd = {  
  2.     .name         = "s3c2410-lcd",  
  3.     .id       = -1,  
  4.     .num_resources    = ARRAY_SIZE(s3c_lcd_resource),  
  5.     .resource     = s3c_lcd_resource,  
  6.     .dev              = {  
  7.         .dma_mask       = &s3c_device_lcd_dmamask,  
  8.         .coherent_dma_mask  = 0xffffffffUL  
  9.     }  
  10. };  
    可以看出,它占用的资源s3c_lcd_resource,定义如下:
[cpp] view plaincopy
  1. static struct resource s3c_lcd_resource[] = {  
  2.     [0] = {  
  3.         .start = S3C24XX_PA_LCD,  
  4.         .end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,  
  5.         .flags = IORESOURCE_MEM,  
  6.     },  
  7.     [1] = {  
  8.         .start = IRQ_LCD,  
  9.         .end   = IRQ_LCD,  
  10.         .flags = IORESOURCE_IRQ,  
  11.     }  
  12. };  
   这是一个数组,有两个元素,说明lcd占用了系统两个资源,一个资源类型是IORESOURCE_MEM代表io内存,起使地址S3C24XX_PA_LCD,这个是LCDCON1寄存器的地址。另外一个资源是中断信号线。
四. platform设备驱动
   如果要将所写的驱动程序注册成platform驱动,那么所做的工作就是初始化一个platform_driver,然后调用platform_driver_register进行注册。
   1. 基本数据机构platform_driver
[cpp] view plaincopy
  1. struct platform_driver {  
  2.     int (*probe)(struct platform_device *);  
  3.     int (*remove)(struct platform_device *);  
  4.     void (*shutdown)(struct platform_device *);  
  5.     int (*suspend)(struct platform_device *, pm_message_t state);  
  6.     int (*resume)(struct platform_device *);  
  7.     struct device_driver driver;  
  8.     struct platform_device_id *id_table;  
  9. };  
    这是platform驱动基本的数据结构,在驱动程序中我们要做的就是声明一个这样的结构并初始化。下面是lcd驱动程序对它的初始化:
[cpp] view plaincopy
  1. static struct platform_driver s3c2412fb_driver = {  
  2.     .probe      = s3c2412fb_probe,  
  3.     .remove     = s3c2410fb_remove,  
  4.     .suspend    = s3c2410fb_suspend,  
  5.     .resume     = s3c2410fb_resume,  
  6.     .driver     = {  
  7.         .name   = "s3c2412-lcd",  
  8.         .owner  = THIS_MODULE,  
  9.     },  
  10. };  
    上面几个函数是我们要实现的,它将赋值给device_driver中的相关成员,probe函数是用来查询特定设备是够真正存在的函数。当设备从系统删除的时候调用remove函数。
   2. 注册函数platform_driver_register
[cpp] view plaincopy
  1. int platform_driver_register(struct platform_driver *drv)  
  2. {  
  3.     drv->driver.bus = &platform_bus_type;  
  4.     if (drv->probe)  
  5.         drv->driver.probe = platform_drv_probe;  
  6.     if (drv->remove)  
  7.         drv->driver.remove = platform_drv_remove;    
  8.     if (drv->shutdown)  
  9.         drv->driver.shutdown = platform_drv_shutdown;  
  10.     return driver_register(&drv->driver);  
  11. }  
    这个函数首先使驱动属于platform_bus_type总线,将platform_driver结构中的定义的probe,remove,shutdown赋值给device_driver结构中的相应成员,以供linux设备模型核心调用,然后调用driver_regster将设备驱动注册到linux设备模型核心中。
五. 各环节的整合
    前面提到mini2440板级初始化程序将它所有的platform设备注册到了linux设备模型核心中,在/sys/devices/platform目录中都有相应的目录表示。platform驱动则是由各个驱动程序模块分别注册到系统中的。但是他们是如何联系起来的呢,这就跟linux设备模型核心有关系了。在ldd3中的linux设备模型的各环节的整合中有详细的论述。这里简要说明一下platform实现的方法。每当注册一个platform驱动的时候就会调用driver_register,这个函数的调用会遍历设备驱动所属总线上的所有设备,并对每个设备调用总线的match函数。platform驱动是属于platform_bus_type总线,所以调用platform_match函数。这个函数实现如下:
[cpp] view plaincopy
  1. static int platform_match(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     struct platform_device *pdev = to_platform_device(dev);  
  4.     struct platform_driver *pdrv = to_platform_driver(drv);  
  5.   
  6.     /* match against the id table first */  
  7.     if (pdrv->id_table)  
  8.         return platform_match_id(pdrv->id_table, pdev) != NULL;  
  9.     /* fall-back to driver name match */  
  10.     return (strcmp(pdev->name, drv->name) == 0);  
  11. }  
    这个函数将device结构转换为platform_devcie结构,将device_driver结构转换为platform_driver结构,并调用platform_match_id对设备与驱动相关信息进行比较。如果没有比较成功会返回0,以便进行下一个设备的比较,如果比较成功就会返回1,并且将device结构中的driver指针指向这个驱动。然后调用device_driver中的probe函数,在lcd驱动中就是s3c2412fb_probe。这个函数是我们要编写的函数。这个函数检测驱动的状态,并且测试能否真正驱动设备,并且做一些初始化工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/469794.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Pytorch完成多分类问题

多分类问题在最后的输出层采用的Softmax Layer&#xff0c;其具有两个特点&#xff1a;1.每个输出的值都是在(0,1)&#xff1b;2.所有值加起来和为1. 假设是最后线性层的输出&#xff0c;则对应的Softmax function为&#xff1a; 输出经过sigmoid运算即可是西安输出的分类概率…

PyTorch的nn.Linear()详解

1. nn.Linear() nn.Linear()&#xff1a;用于设置网络中的全连接层&#xff0c;需要注意的是全连接层的输入与输出都是二维张量 一般形状为[batch_size, size]&#xff0c;不同于卷积层要求输入输出是四维张量。其用法与形参说明如下&#xff1a; in_features指的是输入的二维…

罗彻斯特大学计算机科学系专业排名,罗切斯特大学计算机科学专业

罗切斯特大学(University of Rochester&#xff0c;U of R)建立于1850年&#xff0c;是一所美国著名的私立研究型大学&#xff0c;“新常春藤”联盟之一&#xff0c;北美大学协会(AAU)成员、世界大学联盟成员。360老师介绍&#xff0c;学校的7位学者是美国国家科学院院士&#…

系统权限管理设计 (转)

权限设计&#xff08;初稿&#xff09; 1. 前言&#xff1a; 权限管理往往是一个极其复杂的问题&#xff0c;但也可简单表述为这样的逻辑表达式&#xff1a;判断“Who对What(Which)进行How的操作”的逻辑表达式是否为真。针对不同的应用&#xff0c;需要根据项目的实…

卷积神经网络(基础篇)

说明 0、前一部分叫做Feature Extraction&#xff0c;后一部分叫做classification 1、每一个卷积核它的通道数量要求和输入通道是一样的。这种卷积核的总数有多少个和你输出通道的数量是一样的。 2、卷积(convolution)后&#xff0c;C(Channels)变&#xff0c;W(width)和H(Heig…

mini2440驱动分析之LCD

mini2440集成了lcd控制器的接口,板子上接的lcd硬件是统宝240*320,TFT型lcd。lcd驱动对应的文件为s3c2410fb.c。要读懂这个驱动必须了解linux platform子系统的知识。因为这个驱动是以platform驱动的形式注册到内核。而且还需要frambuffer驱动的知识,因为这个驱动还是frambuf…

数组长度改变方法

package com.lovo.array;public class SuperIntArray {//属性public int[] array;private int index;//代表两层含义&#xff1a;1、下一个元素所在的下标&#xff1b;2、已经放了多少个元素。public SuperIntArray(){this.array new int[20];}//行为//放入元素public void ad…

Inception(Pytorch实现)

论文在此: Going deeper with convolutions 论文下载: https://arxiv.org/pdf/1409.4842.pdf 网络结构图: import torch import torch.nn as nn import torch.nn.functional as Fclass Inception3(nn.Module):def __init__(self, num_classes1000, aux_logitsTrue, transform…

html导出pdf实例,jsPDF导出pdf示例

jsPDF貌似不支持中文复制代码 代码如下:Downloadifybody {background: #fff; width: 500px; margin: 20px auto;}input, textarea, p { font-family: 宋体, 黑体; font-size: 12pt;}input, textarea { border: solid 1px #aaa; padding: 4px; width: 98%;}window.loadfunction(…

SecureCRT 用来当串口工具的设置

今天从淘宝网上买的USB转串口线终于到了&#xff0c;从网上下载了驱动&#xff0c;关于USB转串口驱动在我上传的资源里面有&#xff0c;关于SecureCRT这个串口调试工具我也上传了&#xff0c;是个绿色免安装版本。 刚开始的时候一步一步的设置串口&#xff0c;连接串口也可以连…

Brainstorm-the walkthrough example: Image Classification

(1) 运行create data&#xff0c;其中包括下载cifar10&#xff0c;并转换为hdf5格式&#xff08;详见百度百科&#xff1a;http://baike.baidu.com/view/771949.htm#4_2&#xff09;: cifar10的数据简介见&#xff1a;http://www.cs.toronto.edu/~kriz/cifar.html cd data pyth…

卷积神经网络(高级篇) Inception Moudel

Inception Moudel 1、卷积核超参数选择困难&#xff0c;自动找到卷积的最佳组合。 2、1x1卷积核&#xff0c;不同通道的信息融合。使用1x1卷积核虽然参数量增加了&#xff0c;但是能够显著的降低计算量(operations) 3、Inception Moudel由4个分支组成&#xff0c;要分清哪些…

计算机谈音乐薛之谦,明星浮世绘之薛之谦:分析了50多首音乐作品,为其总结了五个特点...

原标题&#xff1a;明星浮世绘之薛之谦&#xff1a;分析了50多首音乐作品&#xff0c;为其总结了五个特点薛之谦&#xff0c;才华横溢思维敏捷&#xff0c;性格搞怪却又忧郁。我曾经用四个字来形容他&#xff0c;沙雕其外&#xff0c;金玉其中。记得老薛曾经发布了一个动态&…

linux内核下载 编译

linux内核下载网址 今天去看了一场电影“疯狂的原始人”----回来的车上看到一个老奶奶传教士,我想对自己多,加油,加油学习,深思深思 我们现在用的安霸系统,每搞一次我都会进行一次备份,一个系统加上GUI一起都有差不多一G多,而今天下载了最新的linux内核版本,才不80M左…

黑客与画家读后感

财富的定义&#xff0c;金钱只是媒介&#xff0c;财富是人们需要的东西。你如果不是富二代&#xff0c;只是说明你没钱&#xff0c;但你还是通过创造来获得财富。贫富差距不一定就是坏事。如何获得财富&#xff0c;你的工作要满足两个特征:可测量性&#xff0c;可放大性。比如一…

Deep learning

论文&#xff1a;doi:10.1038/nature14539 论文意义和主要内容 三巨头从机器学习谈起&#xff0c;指出传统机器学习的不足&#xff0c;总览深度学习理论、模型&#xff0c;给出了深度学习的发展历史&#xff0c;以及DL中最重要的算法和理论。 概念&#xff1a; 原理&#xff…

清华大学计算机学院主页,计算机图形学基础课程主页 | 清华大学计算机系

1. 2002级本科生黄其兴同学在完成图形学课作业的过程中就B样条的升阶和顶点插入算法进行了深入的研究&#xff0c;并在胡事民教授和Martin教授的共同指导下在国际著名刊物Computer Aided Geometric Design (CAGD)上发表文章.Fast degree elevation and knot insertion for B-s…

mkimage command not found

UIMAGE arch/arm/boot/uImage "mkimage" command not found - U-Boot images will not be built Image arch/arm/boot/uImage is ready cp: 无法获取"arch/arm/boot/uImage" 的文件状态(stat): 没有那个文件或目录 使用make uImage编译生成的内核能由ub…

克隆虚拟机 virtualbox 修改 uuid

cmd E:\Program Files\Oracle\VirtualBox>VBoxManage.exe internalcommands sethduuid "E:\Program Files\Oracle\VirtualBox VMs\115-3.vhd"转载于:https://www.cnblogs.com/lonelydreamer/p/6140931.html

第一周:深度学习引言(Introduction to Deep Learning)

1.1 欢迎(Welcome) 深度学习改变了传统互联网业务&#xff0c;例如如网络搜索和广告。但是深度学习同时也使得许多新产品和企业以很多方式帮助人们&#xff0c;从获得更好的健康关注。 深度学习做的非常好的一个方面就是读取X光图像&#xff0c;到生活中的个性化教育&#xf…