Chinaunix首页 | 论坛 | 博客
  • 博客访问: 284625
  • 博文数量: 68
  • 博客积分: 352
  • 博客等级: 入伍新兵
  • 技术积分: 296
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-16 15:41
文章分类
文章存档

2021年(1)

2020年(2)

2017年(4)

2016年(3)

2015年(1)

2013年(1)

2012年(21)

2011年(35)

分类:

2012-09-12 16:17:07

原文地址:platform设备驱动模型 作者:zhongli_i

弄了两天,把platform模型看了。
linux设备驱动模型中,三个重要的实体,总线、设备、驱动。
其中的概念和意义不再强调,网上还有书上都有很多。
这里只强调两点:
1)引入platform使得设备被挂在在总线上,符合了linux2.6内核的设备模型。
2)隔离BSP和驱动。在BSP中定义的platform设备和设备使用的资源、设备的具体配置信息,而在驱动中,只需要通过通用API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。

几个重要的结构体:
platform_bus_type是platform总线定义了一个bus_type的实例。
这部分有系统内部为我们完成,我们不需要去做特殊的定义或者初始化。

点击(此处)折叠或打开

  1. struct bus_type platform_bus_type = {
  2.     .name = "platform", //总线名称
  3.     .dev_attrs = platform_dev_attrs,
  4.     .match = platform_match, //匹配函数,该函数确定了platform_device和platform_driver之间如何匹配
  5.     .uevent = platform_uevent, //用于添加环境变量
  6.     .pm = PLATFORM_PM_OPS_PTR,
  7. };
  8. EXPORT_SYMBOL_GPL(platform_bus_type);
  9.   
  10. int __init platform_bus_init(void)  //总线的初始化
  11. {
  12.     int error;
  13.   
  14.     early_platform_cleanup();
  15.   
  16.     error = device_register(&platform_bus);
  17.     if (error)
  18.         return error;
  19.     error = bus_register(&platform_bus_type);
  20.     if (error)
  21.         device_unregister(&platform_bus);
  22.     return error;
  23. }

总线也是一种设备,要先注册总线设备,在使用总线设备完成总线注册。

点击(此处)折叠或打开

  1. static struct platform_device *smdk6410_devices[] __initdata = {

  2. //#ifdef CONFIG_SMDK6410_SD_CH0
  3.     &s3c_device_hsmmc0,
  4. //#endif
  5. //#ifdef CONFIG_SMDK6410_SD_CH1
  6.     &s3c_device_hsmmc1,
  7. //#endif
  8.     &s3c_device_i2c0,
  9. //    &s3c_device_i2c1,
  10.     &s3c_device_fb,

  11.     &s3c_device_ohci,
  12.     &s3c_device_usb_hsotg,
  13.  ......
描述了总线,就要描述设备和驱动了。下面是两个重要的数据结构体,定义在

点击(此处)折叠或打开

  1. struct platform_device {       //platform设备  在设备源文件中实现
  2.     const char    * name;
  3.     int        id;
  4.     struct device    dev;
  5.     u32        num_resources;
  6.     struct resource    * resource;

  7.     const struct platform_device_id    *id_entry;

  8.     /* arch specific additions */
  9.     struct pdev_archdata    archdata;
  10. };

  11. struct platform_driver {      //platform驱动  在驱动源文件中实现
  12.     int (*probe)(struct platform_device *);  //函数指针也有驱动程序实现
  13.     int (*remove)(struct platform_device *);
  14.     void (*shutdown)(struct platform_device *);
  15.     int (*suspend)(struct platform_device *, pm_message_t state);
  16.     int (*resume)(struct platform_device *);
  17.     struct device_driver driver;
  18.     const struct platform_device_id *id_table;
  19. };
resource结构,dm9000_resources是他的一个实例(OK6410)
对resource的定义通常在BSP板文件中进行,具体设备驱动通过platform_get_resource()这个API来获取。

点击(此处)折叠或打开

  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. };

  8. static struct resource dm9000_resources[] = {
  9.     [0] = {
  10.         .start        = S3C64XX_PA_DM9000,
  11.         .end        = S3C64XX_PA_DM9000 + 3,
  12.         .flags        = IORESOURCE_MEM,
  13.     },
  14.     [1] = {
  15.         .start        = S3C64XX_PA_DM9000 + 4,
  16.         .end        = S3C64XX_PA_DM9000 + S3C64XX_SZ_DM9000 - 1,
  17.         .flags        = IORESOURCE_MEM,
  18.     },
  19.     [2] = {
  20.         .start        = IRQ_EINT(7),//DM9000AE中断信号使用S3C6410处理器中断EINT7信号
  21.         .end        = IRQ_EINT(7),
  22.         .flags        = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH,
  23.     },
  24. };
除了这些信息,硬件设备可能还会有一些配置信息,也是依赖于板的,不适合放在驱动程序中。
因此,platform提供platform_date支持

点击(此处)折叠或打开

  1. static struct dm9000_plat_data dm9000_setup = {
  2.     .flags            = DM9000_PLATF_16BITONLY,
  3.     .dev_addr        = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 },
  4. };

  5. static struct platform_device s3c_device_dm9000 = {
  6.     .name            = "dm9000",
  7.     .id                = 0,
  8.     .num_resources    = ARRAY_SIZE(dm9000_resources),
  9.     .resource        = dm9000_resources,
  10.     .dev            = {
  11.         .platform_data = &dm9000_setup,
  12.     }
  13. };
  14. #endif //#ifdef CONFIG_DM9000
而在网卡驱动中,通过
struct dm9000_plat_data *pdate=pdev->dev.platform_data 
获得platform_data 即硬件的配置信息

最重要的在于platform_device与platform_driver之间是如何通过platform总线建立联系的。
通过对函数platform_driver_register的追踪,可以看到,最终两者通过platform_mach建立联系的。

点击(此处)折叠或打开

  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.   
  10.     /* fall-back to driver name match */
  11.     return (strcmp(pdev->name, drv->name) == 0);
  12. }
可以看出,platform_device与platform_driver是通过name来匹配的。
匹配成功后,调用probe函数。

点击(此处)折叠或打开

  1. int driver_probe_device(struct device_driver *drv, struct device *dev)
  2. {
  3.     。。。。。。。。。
  4.     ret = really_probe(dev, drv);
  5.     。。。。。。。。
  6. }
  7. static int really_probe(struct device *dev, struct device_driver *drv)
  8. {
  9.     。。。。。。。。
  10.     if (dev->bus->probe) {
  11.         ret = dev->bus->probe(dev);
  12.         if (ret)
  13.             goto probe_failed;
  14.     } else if (drv->probe) {
  15.         ret = drv->probe(dev);
  16.         if (ret)
  17.             goto probe_failed;
  18.     }
  19.     。。。。。。。。
  20. }
可以看出,如果bus定义了probe函数,则调用bus的probe函数;如果bus,没有定义而driver定义了probe函数,则调用driver的probe函数。由上边的platform_bus_type可以看出bus并没有定义probe函数,所以调用driver的probe函数。

我的测试程序用的是linux设备驱动开发详解这本书,但是这里出现一个问题。
书上的例程是将device和driver放在同一个文件中,我将源代码完全拷贝后编译可以通过,但是insmod并没有把生成的模块加载进来。
用lsmod可以查看到加载的模块,但是cat /proc/devices找不到设备号,无法创建设备节点,也无法测试应用程序。
然后按照网友的说明,将两者分开,然后分别编译分别加载,就可以通过了。
部分代码如下,没列出的和原来的部分是一样的。
在globalfifo-device.c中添加

点击(此处)折叠或打开

  1. static struct platform_device *globalfifo_device;
  2. /* module_init */
  3. static int __init globalfifo_dev_init(void)
  4. {
  5.   int ret; 
  6.   globalfifo_device=platform_device_alloc("globalfifo",-1);//分配设备
  7.   ret=platform_device_add(globalfifo_device); //注册设备 挂载设备到总线
  8.   if(ret)
  9.   {
  10.     printk("platform_device_add failed\n");
  11.   }
  12.   else
  13.     printk("platform_device_add success!\n");
  14. }

  15. /* module_exit */
  16. void __exit globalfifo_dev_exit(void)
  17. {
  18.   platform_device_unregister(globalfifo_device); //设备注销
  19. }
在platform-driver.c中添加

点击(此处)折叠或打开

  1. static int __devexit globalfifo_remove(struct platform_device *pdev)
  2. {
  3.   cdev_del(&globalfifo_devp->cdev);
  4.   kfree(globalfifo_devp);
  5.   unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1);
  6.   return 0;
  7. }

  8. static struct platform_driver globalfifo_driver=
  9. {
  10.   .probe=globalfifo_probe,
  11.   .remove=__devexit_p(globalfifo_remove),
  12.   .driver=
  13.    {
  14.      .name="globalfifo",
  15.      .owner=THIS_MODULE,
  16.    }
  17. };

  18. /* module_init */
  19. static int __init globalfifo_drv_init(void)
  20. {
  21.   return platform_driver_register(&globalfifo_driver);
  22. }

  23. /* module_exit */
  24. void __exit globalfifo_drv_exit(void)
  25. {
  26.   platform_driver_unregister(&globalfifo_driver);
  27. }
同时还需要在板文件中mach-smdk6410.c中添加如下信息

点击(此处)折叠或打开

  1. static struct platform_device globalfifo_device={
  2.     .name="globalfifo",
  3.     .id=-1,
  4. };
为了完成globalfifo_device这个设备的注册,修改如下

点击(此处)折叠或打开

  1. static struct platform_device *smdk6410_devices[] __initdata = {
  2. +&globalfifo_device
  3. //#ifdef CONFIG_SMDK6410_SD_CH0
  4.     &s3c_device_hsmmc0,
  5. //#endif
  6. //#ifdef CONFIG_SMDK6410_SD_CH1
  7.     &s3c_device_hsmmc1,
  8. //#endif
  9.     &s3c_device_i2c0,
  10. //    &s3c_device_i2c1,
  11.     &s3c_device_fb,

  12.     &s3c_device_ohci,
下面编译,加载就可以了。
加载后可以发现如下结点
/sys/bus/platform/devices/globalfifo
/sys/devices/platform/globalfifo
cat /proc/devices可以查看到设备号。
mknod创建设备节点后就可以测试应用程序了。



阅读(1129) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~