Chinaunix首页 | 论坛 | 博客
  • 博客访问: 789552
  • 博文数量: 264
  • 博客积分: 592
  • 博客等级: 中士
  • 技术积分: 1574
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-24 22:02
文章分类

全部博文(264)

文章存档

2019年(2)

2018年(1)

2017年(1)

2016年(4)

2015年(14)

2014年(57)

2013年(88)

2012年(97)

分类: LINUX

2013-08-13 09:06:51

原文地址:platform 总线、设备与驱动 作者:flztsj

Linux2.6的设备驱动模型中,关心总线、设备和驱动3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每次注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。

Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动称为platform_driver


platform_device结构体:

点击(此处)折叠或打开

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


platform_driver结构体:

点击(此处)折叠或打开

  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 device_driver driver;
  10. };

系统中为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,
  5.     .uevent        = platform_uevent,
  6.     .suspend    = platform_suspend,
  7.     .suspend_late    = platform_suspend_late,
  8.     .resume_early    = platform_resume_early,
  9.     .resume        = platform_resume,
  10. };


下面就以2.6.22.6内核和DM9000为例,介绍下platform总线、设备与驱动在代码中的实现,DM9000驱动部分,依照《嵌入式Linux应用开发完全手册》进行配置,配置如下:

arch\arm\plat-s3c24xx\common-smdk.c文件中添加,对应的是platform_device这个结构体:

点击(此处)折叠或打开

  1.   #include <linux/dm9000.h>
  2.   
  3.   static struct resource s3c_dm9k_resource[] = {
  4.       [0] = {
  5.           .start = S3C2410_CS4,        //ADDR2 = 0 ,发送地址时使用这个地址
  6.           .end = S3C2410_CS4 + 3,
  7.           .flags = IORESOURCE_MEM,
  8.       },
  9.       [1] = {
  10.           .start = S3C2410_CS4 + 4, //ADDR2 = 1 ,发送数据时使用这个地址
  11.           .end = S3C2410_CS4 + 4 + 3,
  12.           .flags = IORESOURCE_MEM,
  13.       },
  14.       [2] = {
  15.           .start = IRQ_EINT7, //IRQ_DM9000
  16.           .end = IRQ_EINT7, //IRQ_DM9000
  17.           .flags = IORESOURCE_IRQ,
  18.       }
  19.   
  20.   };
  21.   
  22.   static struct dm9000_plat_data s3c_dm9k_platdata = {
  23.       .flags        = DM9000_PLATF_16BITONLY, //数据总线宽度为16
  24.   };
  25.   
  26.   static struct platform_device s3c_device_dm9k = {
  27.       .name        = "dm9000",
  28.       .id        = 0,
  29.       .num_resources    = ARRAY_SIZE(s3c_dm9k_resource),
  30.       .resource    = s3c_dm9k_resource,
  31.       .dev        = {
  32.           .platform_data = &s3c_dm9k_platdata,
  33.       }
  34.   };
  35.   
  36.   
  37.   static struct platform_device __initdata *smdk_devs[] = {
  38.       ……
  39.       &s3c_device_dm9k,
  40.   };


那么很显然有platform_device就对应应该有个platform_driver,其在dm9000.c文件中:

点击(此处)折叠或打开

  1.   static struct platform_driver dm9000_driver = {
  2.       .driver    = {
  3.           .name = "dm9000",
  4.           .owner     = THIS_MODULE,
  5.       },
  6.       .probe = dm9000_probe,
  7.       .remove = dm9000_drv_remove,
  8.       .suspend = dm9000_drv_suspend,
  9.       .resume = dm9000_drv_resume,
  10.   };


那么具体去加载platform_device和platform_driver到函数是什么呢?在内核代码中可以找到:

点击(此处)折叠或打开

  1. static int __init dm9000_init(void)
  2. {
  3.     ……
  4.     return platform_driver_register(&dm9000_driver);    /* search board and register */
  5. }
  6. module_init(dm9000_init);

  7. void __init smdk_machine_init(void)
  8. {
  9.     ……
  10.     platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));
  11.   ……
  12. }
有一点需要注明:

对platform_device的定义通常是在BSP的板文件中实现,在板文件中,platform_device归纳为一个数组,最终通过platform_add_devices(struct platform_device **devs, int num)函数统一注册。

接下来就分析下platform_device_register和platform_driver_register这两个函数的流程:

点击(此处)折叠或打开

  1. platform_device_register(devs[i])
  2.     platform_device_add(pdev);
  3.         device_add(&pdev->dev);
  4.             bus_add_device(dev)        //将设备加载到总线
  5.             bus_attach_device(dev);        //在总线上查找对应的驱动
  6.                 device_attach(dev);
  7.                     bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
  8.                         while ((drv = next_driver(&i)) && !error)
  9.                             error = fn(drv, data); //fn --> __device_attach
  10.                                 driver_probe_device(drv, dev);
  11.                                     if (drv->bus->match && !drv->bus->match(dev, drv))
  12.                                         goto done;
  13.                                     really_probe(dev, drv);
  14.                                         drv->probe(dev);
  15.   
  16.   
  17. platform_driver_register(struct platform_driver *drv)
  18.   drv->driver.bus = &platform_bus_type;
  19.         driver_register(&drv->driver)
  20.             bus_add_driver(drv);
  21.               driver_attach(drv);
  22.                   bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
  23.                       while ((dev = next_device(&i)) && !error)
  24.                           error = fn(dev, data); //fn --> __driver_attach
  25.                               driver_probe_device(drv, dev);
  26.                                   if (drv->bus->match && !drv->bus->match(dev, drv))
  27.                                       goto done;
  28.                                   really_probe(dev, drv);
  29.                                       drv->probe(dev);


drv->bus->match对应的就视platform_bus_type
实例中的platform_match函数:

点击(此处)折叠或打开

  1. static int platform_match(struct device * dev, struct device_driver * drv)
  2. {
  3.     struct platform_device *pdev = container_of(dev, struct platform_device, dev);

  4.     return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); //也就是说匹配platform设备和驱动是根据两者的name字段是否相同。
  5. }

当总线匹配到了相应的设备和驱动,则就会运行drv->probe(dev);


这个对应的就是开发者自行定义的probe函数,在此处就是指dm9000_driver实例中dm9000_probe函数,函数中会对dm9000进行相应的探测和初始化操作,并在最后运行register_netdev(ndev);在内核中注册一个网络设备,这样,系统运行后会调用到dm9000_open函数申请相应的中断和初始化操作,这样网络设备dm9000就完成了全部的初始化操作,并可以使用了。

platform总线的引入,方便了设备和驱动的管理与匹配,并能完成相应设备的自举和初始化操作,整个过程清晰明了。



resource这个结构体需要说明下:

点击(此处)折叠或打开

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

resource 结构体主要是表明设备的物理资源是那些,通过flags来区别是内存地址还是中断资源,或是别的。这些资源会在probe中进行调用,dm9000_probe中使用如下:


点击(此处)折叠或打开

  1. db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  2. db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  3. db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);


具体的代码就不去分析了,比较简单。

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