当前位置: 首页 > news >正文

platform_device与platform_driver

      做Linux方面也有三个多月了,对代码中的有些结构一直不是非常明确,比方platform_device与platform_driver一直分不清关系。在网上搜了下,做个总结。两者的工作顺序是先定义platform_device -> 注冊 platform_device->,再定义 platform_driver-> 注冊 platform_driver。

 (1)platform_device设备的注冊过程必须在对应设备驱动载入之前被调用,由于驱动注冊时须要匹配内核中所以已注冊的设备名。platform_device 是在系统启动时在init.c 里的s3c_arch_init() 函数里进行注冊的。这个函数申明为arch_initcall(s3c_arch_init); 会在系统初始化阶段被调用。arch_initcall 的优先级高于module_init,所以会在Platform 驱动注冊之前调用。如今内核中不是採用arch_initcall(s3c_arch_init) 注冊platform_device 结构体而是通过.init_machine成员将其保存在arch_initcall(customize_machine)等待调用(在mach-smdk6410.c中定义的MACHINE_START到MACHINE_END);事实上质是一样的均放在.initcall3.init等待调用。之后再定义结构体struct platform_driver,在驱动初始化函数中调用函数platform_driver_register() 注冊 platform_driver。具体过程描写叙述例如以下:

      Linux从2.6版本号開始引入了platform这个概念,在开发底层驱动程序时,首先要确认的就是设备的资源信息,在2.6内核中将每一个设备的资源用结构platform_device来描写叙述,该结构体定义在kernel/include/linux/platform_device.h中,

struct platform_device

{
      const char * name;
      u32  id;
      struct device dev;
      u32  num_resources;
      struct resource * resource;
};


该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel/include/linux/ioport.h中,
比方:

struct resource 

{
       const char *name;
       unsigned long start, end;
       unsigned long flags;
       struct resource *parent, *sibling, *child;
};

实比如:

static struct resource s3c_usb_resource[] = {
 [0] = {
       .start = S3C_PA_USBHOST,
       .end   = S3C_PA_USBHOST + S3C_SZ_USBHOST - 1,
       .flags = IORESOURCE_MEM,
     },
 [1] = {
       .start = IRQ_UHOST,
       .end   = IRQ_UHOST,
       .flags = IORESOURCE_IRQ,
     }
};


以上是6410的USB  HOST分配的资源信息。第1组描写叙述了这个usb host设备所占用的总线地址范围,起始地址和大小由硬件决定,IORESOURCE_MEM表示第1组描写叙述的是内存类型的资源信息;第2组描写叙述了这个usb host设备的中断号,也由硬件设定,IORESOURCE_IRQ表示第2组描写叙述的是中断资源信息。设备驱动会依据flags来获取对应的资源信息。

      有了resource信息,就能够定义platform_device了:

struct platform_device s3c_device_usb = {
         .name    = "s3c2410-ohci",  //s3c6410-usb
         .id    = -1,
         .num_resources   = ARRAY_SIZE(s3c_usb_resource),
         .resource   = s3c_usb_resource,
         .dev              = {
                 .dma_mask = &s3c_device_usb_dmamask,
                 .coherent_dma_mask = 0xffffffffUL
             }
};


有了platform_device就能够调用函数platform_add_devices向系统中加入该设备了。系统中的设备资源都能够採用这样的方式列举在一起,然后成一个指针数组,如:

static struct platform_device *smdk6410_devices[] __initdata = {

......

 &s3c_device_usbgadget,
 &s3c_device_usb,  //jeff add.

......

}

然后在6410的初始化函数smdk6410_machine_init()中运行:

platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));将全部的device加入进系统。platform_add_devices的优点在于它是一次性的运行多个platform_device_register。

(2) 至于驱动程序须要实现结构体struct platform_driver,也定义在kernel/include/linux/platform_device.h中:

struct platform_driver {
      int (*probe)(struct platform_device *);
      int (*remove)(struct platform_device *);
      void (*shutdown)(struct platform_device *);
      int (*suspend)(struct platform_device *, pm_message_t state);
      int (*suspend_late)(struct platform_device *, pm_message_t state);
      int (*resume_early)(struct platform_device *);
      int (*resume)(struct platform_device *);
      struct pm_ext_ops *pm;
      struct device_driver driver;
};


则该处的USB HOST实现是:

static struct platform_driver ohci_hcd_s3c2410_driver = {
     .probe  = ohci_hcd_s3c2410_drv_probe,
     .remove  = ohci_hcd_s3c2410_drv_remove,
     .shutdown = usb_hcd_platform_shutdown,
     /*.suspend = ohci_hcd_s3c2410_drv_suspend, */
     /*.resume = ohci_hcd_s3c2410_drv_resume, */
     .driver  = {
          .owner = THIS_MODULE,
          .name = "s3c2410-ohci",
        },
};


      在驱动初始化(ohci-hcd.c的1124行)函数中调用函数platform_driver_register()注冊该platform_driver,须要注意的是s3c_device_usb结构中name元素和ohci_hcd_s3c2410_driver 结构中driver.name必须是同样的,这样在platform_driver_register()注冊时会对全部已注冊的platform_device中元素的name和当前注冊的platform_driver的driver.name进行比較,仅仅有找到具备同样名称的platform_device存在后,platform_driver才干注冊成功。当注冊成功时会调用platform_driver结构元素probe函数指针,这里就是ohci_hcd_s3c2410_drv_probe開始探測载入。platform driver中的函数都是以platform device作为參数进入。

(3)为什么两个name的名字必须匹配才干实现device和driver的绑定?(1)在内核初始化时kernel_init()->do_basic_setup()->driver_init()->platform_bus_init()初始化platform_bus(虚拟总线);(2)设备注冊的时候platform_device_register()->platform_device_add()->(pdev->dev.bus = &platform_bus_type)把设备挂在虚拟的platform bus下;(3)驱动注冊的时候platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev(),对每一个挂在虚拟的platform bus的设备作__driver_attach()->driver_probe_device(),推断drv->bus->match()是否存在而且是否运行成功,此时通过指针运行platform_match,比較strncmp(pdev->name, drv->name, BUS_ID_SIZE),假设相符就调用really_probe(实际就是运行的对应设备的platform_driver->probe(platform_device),注意platform_drv_probe的_dev參数是由bus_for_each_dev的next_device获得)開始真正的探測载入,假设probe成功则绑定该设备到该驱动。

      当进入probe函数后,须要获取设备的资源信息,依据參数type所指定类型,比如IORESOURCE_MEM,来分别获取指定的资源。
struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);当然,也能够固定资源类型,如获取资源中的中断号:struct int platform_get_irq(struct platform_device *dev, unsigned int num);

      probe函数一般完毕硬件设备使能,struct resource的获取以及虚拟地址的动态映射和详细类型设备的注冊(由于平台设备仅仅是一种虚拟的设备类型);remove函数完毕硬件设备的关闭,struct resource以及虚拟地址的动态映射的释放和详细类型设备的注销。仅仅要和内核本身执行依赖性不大的外围设备 ( 换句话说仅仅要不在内核执行所需的一个最小系统之内的设备 ), 相对独立的拥有各自独自的资源 (addresses and IRQs) ,都能够用platform_driver 实现。如:lcd,usb,uart 等,都能够用platfrom_driver 写,而timer,irq等最小系统之内的设备则最好不用platfrom_driver 机制,实际上内核实现也是这种。


 參考原文:http://blog.chinaunix.net/u1/49507/showart_494193.html

參考原文:http://blog.csdn.net/yd4330152763132/archive/2010/02/01/5275776.aspx

相关文章:

  • 那个外包工场
  • Node学习5-events模块
  • javascript中 visibility和display的区别
  • npm ERR! Unexpected end of JSON input while parsing near '...inimist:^1.2.0}
  • mysql show命令集合
  • 单例模式的N种写法(Java版)
  • 基于dba_hist_sqlstat查看sql语句的性能历史
  • es6
  • win32之全屏窗口
  • 【ocp新题库】052最新考题收集整理-第7题
  • 蓝桥杯-基础练习12 十六进制转八进制
  • 8 quick ways to clear up drive space in Windows 10
  • 【原创】Hacker学习发展流程图 V1.0
  • 设计模式(八)_门面模式
  • 【Leetcode】104. 二叉树的最大深度
  • echarts的各种常用效果展示
  • golang中接口赋值与方法集
  • JavaScript的使用你知道几种?(上)
  • MySQL数据库运维之数据恢复
  • nginx 负载服务器优化
  • XML已死 ?
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 和 || 运算
  • 开发了一款写作软件(OSX,Windows),附带Electron开发指南
  • 如何学习JavaEE,项目又该如何做?
  • 双管齐下,VMware的容器新战略
  • 微服务入门【系列视频课程】
  • 译自由幺半群
  • 正则与JS中的正则
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​LeetCode解法汇总518. 零钱兑换 II
  • #QT(一种朴素的计算器实现方法)
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • $.proxy和$.extend
  • (二)springcloud实战之config配置中心
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (转)shell调试方法
  • (转)关于pipe()的详细解析
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • *p++,*(p++),*++p,(*p)++区别?
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution
  • .net反编译的九款神器
  • .NET使用存储过程实现对数据库的增删改查
  • @for /l %i in (1,1,10) do md %i 批处理自动建立目录
  • @selector(..)警告提示
  • [autojs]逍遥模拟器和vscode对接
  • [BZOJ] 3262: 陌上花开
  • [BZOJ1877][SDOI2009]晨跑[最大流+费用流]
  • [CareerCup] 14.5 Object Reflection 对象反射
  • [ESP32 IDF]web server