3472|2

854

帖子

0

TA的资源

五彩晶圆(中级)

楼主
 

关于platform_device与platform_driver 的认识和了解 [复制链接]

  1. 做Linux方面也有三个多月了,对代码中的有些结构一直不是很明白,比如platform_device与platform_driver一直分不清关系。在网上搜了下,做个总结。两者的工作顺序是先定义platform_device -> 注册 platform_device->,再定义 platform_driver-> 注册 platform_driver。
  2. (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。详细过程描述如下:
  3.       Linux从2.6版本开始引入了platform这个概念,在开发底层驱动程序时,首先要确认的就是设备的资源信息,在2.6内核中将每个设备的资源用结构platform_device来描述,该结构体定义在kernel/include/linux/platform_device.h中,
  4. struct platform_device

  5. {
  6.       const char * name;
  7.       u32  id;
  8.       struct device dev;
  9.       u32  num_resources;
  10.       struct resource * resource;
  11. };


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

  15. {
  16.        const char *name;
  17.        unsigned long start, end;
  18.        unsigned long flags;
  19.        struct resource *parent, *sibling, *child;
  20. };

  21. 实例如:
  22. static struct resource s3c_usb_resource[] = {
  23. [0] = {
  24.        .start = S3C_PA_USBHOST,
  25.        .end   = S3C_PA_USBHOST + S3C_SZ_USBHOST - 1,
  26.        .flags = IORESOURCE_MEM,
  27.      },
  28. [1] = {
  29.        .start = IRQ_UHOST,
  30.        .end   = IRQ_UHOST,
  31.        .flags = IORESOURCE_IRQ,
  32.      }
  33. };


  34. 以上是6410的USB  HOST分配的资源信息。第1组描述了这个usb host设备所占用的总线地址范围,起始地址和大小由硬件决定,IORESOURCE_MEM表示第1组描述的是内存类型的资源信息;第2组描述了这个usb host设备的中断号,也由硬件设定,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。
  35.       有了resource信息,就可以定义platform_device了:
  36. struct platform_device s3c_device_usb = {
  37.          .name    = "s3c2410-ohci",  //s3c6410-usb
  38.          .id    = -1,
  39.          .num_resources   = ARRAY_SIZE(s3c_usb_resource),
  40.          .resource   = s3c_usb_resource,
  41.          .dev              = {
  42.                  .dma_mask = &s3c_device_usb_dmamask,
  43.                  .coherent_dma_mask = 0xffffffffUL
  44.              }
  45. };


  46. 有了platform_device就可以调用函数platform_add_devices向系统中添加该设备了。系统中的设备资源都可以采用这种方式列举在一起,然后成一个指针数组,如:
  47. static struct platform_device *smdk6410_devices[] __initdata = {
  48. ......
  49. &s3c_device_usbgadget,
  50. &s3c_device_usb,  //jeff add.
  51. ......
  52. }
  53. 然后在6410的初始化函数smdk6410_machine_init()中执行:
  54. platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));将所有的device添加进系统。platform_add_devices的好处在于它是一次性的执行多个platform_device_register。
  55. (2) 至于驱动程序需要实现结构体struct platform_driver,也定义在kernel/include/linux/platform_device.h中:
  56. struct platform_driver {
  57.       int (*probe)(struct platform_device *);
  58.       int (*remove)(struct platform_device *);
  59.       void (*shutdown)(struct platform_device *);
  60.       int (*suspend)(struct platform_device *, pm_message_t state);
  61.       int (*suspend_late)(struct platform_device *, pm_message_t state);
  62.       int (*resume_early)(struct platform_device *);
  63.       int (*resume)(struct platform_device *);
  64.       struct pm_ext_ops *pm;
  65.       struct device_driver driver;
  66. };


  67. 则该处的USB HOST实现是:
  68. static struct platform_driver ohci_hcd_s3c2410_driver = {
  69.      .probe  = ohci_hcd_s3c2410_drv_probe,
  70.      .remove  = ohci_hcd_s3c2410_drv_remove,
  71.      .shutdown = usb_hcd_platform_shutdown,
  72.      /*.suspend = ohci_hcd_s3c2410_drv_suspend, */
  73.      /*.resume = ohci_hcd_s3c2410_drv_resume, */
  74.      .driver  = {
  75.           .owner = THIS_MODULE,
  76.           .name = "s3c2410-ohci",
  77.         },
  78. };


  79.       在驱动初始化(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作为参数进入。
  80. (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成功则绑定该设备到该驱动。
  81.       当进入probe函数后,需要获取设备的资源信息,根据参数type所指定类型,例如IORESOURCE_MEM,来分别获取指定的资源。
  82. 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);
  83.       probe函数一般完成硬件设备使能,struct resource的获取以及虚拟地址的动态映射和具体类型设备的注册(因为平台设备只是一种虚拟的设备类型);remove函数完成硬件设备的关闭,struct resource以及虚拟地址的动态映射的释放和具体类型设备的注销。只要和内核本身运行依赖性不大的外围设备 ( 换句话说只要不在内核运行所需的一个最小系统之内的设备 ), 相对独立的拥有各自独自的资源 (addresses and IRQs) ,都可以用platform_driver 实现。如:lcd,usb,uart 等,都可以用platfrom_driver 写,而timer,irq等最小系统之内的设备则最好不用platfrom_driver 机制,实际上内核实现也是这样的。

  84. 参考原文:http://blog.chinaunix.net/u1/49507/showart_494193.html
  85. 参考原文:http://blog.csdn.net/yd4330152763132/archive/2010/02/01/5275776.aspx
复制代码



[ 本帖最后由 Wince.Android 于 2013-6-14 16:37 编辑 ]
点赞 关注
个人签名如果对linux,Android,wince 等嵌入式底层有兴趣的,请加这个QQ群吧,群号:27100460

回复
举报

854

帖子

0

TA的资源

五彩晶圆(中级)

沙发
 
文章还是不错的,适合初学者和中级开发者
原文地址
http://blog.csdn.net/zhandoushi1982/article/details/5130207
 
个人签名如果对linux,Android,wince 等嵌入式底层有兴趣的,请加这个QQ群吧,群号:27100460
 

回复

854

帖子

0

TA的资源

五彩晶圆(中级)

板凳
 

这里以Sate210 android4.0 的代码来分析一下

在mach-s5pv210.c 里面有
  1. MACHINE_START(SMDKV210, "SMDKV210")
  2.         /* Maintainer: Kukjin Kim */
  3.         .boot_params        = S5P_PA_SDRAM + 0x100,
  4.         .init_irq        = s5pv210_init_irq,
  5.         .map_io                = smdkv210_map_io,
  6.         .init_machine        = smdkv210_machine_init,
  7. #ifdef CONFIG_S5P_HIGH_RES_TIMERS
  8.         .timer          = &s5p_systimer,
  9. #else
  10.         .timer                = &s5p_timer,
  11. #endif
  12. MACHINE_END
复制代码
  1. static void __init smdkv210_machine_init(void)
  2. {
  3.         s3c_pm_init();

  4.         smdkv210_dm9000_init();
  5.         platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
  6. ...
  7. ...
  8. }
复制代码
说到这里,就以最近几天的sate210 android4.0 的电阻屏幕触摸屏涉及到的adc和触摸屏驱动为例子学习分析
上面涉及到了s3c_device_adc 和s3c_device_ts了。
  1. static struct resource s3c_adc_resource[] = {
  2.         [0] = {
  3.                 .start = SAMSUNG_PA_ADC,
  4.                 .end   = SAMSUNG_PA_ADC + SZ_256 - 1,
  5.                 .flags = IORESOURCE_MEM,
  6.         },
  7.         [1] = {
  8.                 .start = IRQ_TC,
  9.                 .end   = IRQ_TC,
  10.                 .flags = IORESOURCE_IRQ,
  11.         },
  12.         [2] = {
  13.                 .start = IRQ_ADC,
  14.                 .end   = IRQ_ADC,
  15.                 .flags = IORESOURCE_IRQ,
  16.         },
  17. };

  18. struct platform_device s3c_device_adc = { ——这个和前面mach-s5pv210.c 的文件是一样的了,就是靠这个链接起来的
  19.         .name                = "samsung-adc",
  20.         .id                = -1,
  21.         .num_resources        = ARRAY_SIZE(s3c_adc_resource),
  22.         .resource        = s3c_adc_resource,
  23. };
复制代码
  1. static struct platform_device_id s3c_adc_driver_ids[] = {
  2.         {
  3.                 .name                = "s3c24xx-adc",
  4.                 .driver_data        = TYPE_S3C24XX,
  5.         }, {
  6.                 .name                = "s3c64xx-adc",
  7.                 .driver_data        = TYPE_S3C64XX,
  8.         }, {
  9.                 .name                = "s5pv210-adc",  //这里怎么回事!怎么会没有platform_device s3c_device_adc 提到的名字samsung-adc 一致的?
  10.                 .driver_data        = TYPE_S5PV210,
  11.         },
  12.         { }
  13. };
  14. MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids);

  15. static struct platform_driver s3c_adc_driver = {
  16.         .id_table        = s3c_adc_driver_ids,
  17.         .driver                = {
  18.                 .name        = "s3c-adc",
  19.                 .owner        = THIS_MODULE,
  20.         },
  21.         .probe                = s3c_adc_probe,
  22.         .remove                = __devexit_p(s3c_adc_remove),
  23.         .suspend        = s3c_adc_suspend,
  24.         .resume                = s3c_adc_resume,
  25. };
复制代码
 
个人签名如果对linux,Android,wince 等嵌入式底层有兴趣的,请加这个QQ群吧,群号:27100460
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表