zoukankan      html  css  js  c++  java
  • 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中,

    1. struct platform_device  
    2.   
    3. {  
    4.       const char * name;  
    5.       u32  id;  
    6.       struct device dev;  
    7.       u32  num_resources;  
    8.       struct resource * resource;  
    9. };  


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

    1. struct resource   
    2.   
    3. {  
    4.        const char *name;  
    5.        unsigned long start, end;  
    6.        unsigned long flags;  
    7.        struct resource *parent, *sibling, *child;  
    8. };  

    实例如:

    1. static struct resource s3c_usb_resource[] = {  
    2.  [0] = {  
    3.        .start = S3C_PA_USBHOST,  
    4.        .end   = S3C_PA_USBHOST + S3C_SZ_USBHOST - 1,  
    5.        .flags = IORESOURCE_MEM,  
    6.      },  
    7.  [1] = {  
    8.        .start = IRQ_UHOST,  
    9.        .end   = IRQ_UHOST,  
    10.        .flags = IORESOURCE_IRQ,  
    11.      }  
    12. };  


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

          有了resource信息,就可以定义platform_device了:

    1. struct platform_device s3c_device_usb = {  
    2.          .name    = "s3c2410-ohci",  //s3c6410-usb  
    3.          .id    = -1,  
    4.          .num_resources   = ARRAY_SIZE(s3c_usb_resource),  
    5.          .resource   = s3c_usb_resource,  
    6.          .dev              = {  
    7.                  .dma_mask = &s3c_device_usb_dmamask,  
    8.                  .coherent_dma_mask = 0xffffffffUL  
    9.              }  
    10. };  


    有了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中:

    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 (*suspend_late)(struct platform_device *, pm_message_t state);  
    7.       int (*resume_early)(struct platform_device *);  
    8.       int (*resume)(struct platform_device *);  
    9.       struct pm_ext_ops *pm;  
    10.       struct device_driver driver;  
    11. };  


    则该处的USB HOST实现是:

    1. static struct platform_driver ohci_hcd_s3c2410_driver = {  
    2.      .probe  = ohci_hcd_s3c2410_drv_probe,  
    3.      .remove  = ohci_hcd_s3c2410_drv_remove,  
    4.      .shutdown = usb_hcd_platform_shutdown,  
    5.      /*.suspend = ohci_hcd_s3c2410_drv_suspend, */  
    6.      /*.resume = ohci_hcd_s3c2410_drv_resume, */  
    7.      .driver  = {  
    8.           .owner = THIS_MODULE,  
    9.           .name = "s3c2410-ohci",  
    10.         },  
    11. };  


          在驱动初始化(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

  • 相关阅读:
    15. DML, DDL, LOGON 触发器
    5. 跟踪标记 (Trace Flag) 834, 845 对内存页行为的影响
    4. 跟踪标记 (Trace Flag) 610 对索引组织表(IOT)最小化日志
    14. 类似正则表达式的字符处理问题
    01. SELECT显示和PRINT打印超长的字符
    3. 跟踪标记 (Trace Flag) 1204, 1222 抓取死锁信息
    2. 跟踪标记 (Trace Flag) 3604, 3605 输出DBCC命令结果
    1. 跟踪标记 (Trace Flag) 1117, 1118 文件增长及空间分配方式
    0. 跟踪标记 (Trace Flag) 简介
    SpringBoot + Redis + Shiro 实现权限管理(转)
  • 原文地址:https://www.cnblogs.com/liang123/p/6325468.html
Copyright © 2011-2022 走看看