Chinaunix首页 | 论坛 | 博客
  • 博客访问: 43000
  • 博文数量: 9
  • 博客积分: 155
  • 博客等级: 入伍新兵
  • 技术积分: 661
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-09 22:40
个人简介

技术无止境--trouble is a friend

文章分类

全部博文(9)

文章存档

2013年(6)

2012年(3)

我的朋友

分类: LINUX

2013-09-18 10:32:06

一、platform总线、设备与驱动相关概念
    platform机制是在linux 2.6版本中引入的。我们知道通常大部分的设备以及驱动需要挂在一条具体的总线上,如i2c、spi、usb等。但是对于现在的嵌入式系统而言,出现了一种时髦的东西-soc,就是片内系统的意思。这个soc系统中所集成的一些独立的外设控制器、挂在soc内存空间的外设等却不依附于我们上面说的总线。基于这种现象,linux中出现了一种称为platform的虚拟的总线,我们称之为platform总线,当然了,它也有自己对应的设备以及驱动,分别是platform_device以及platform_driver。但是这里的platform_device以及platform_driver与我们的字符、块、网络设备不是同一个概念。这里我们将platform_device以及platform_driver理解为一种linux系统所提供的附加手段。但这并不冲突。
二、platform总线、设备与驱动的使用
    在说明使用之前,先理清三者的关系。在设备模型中,我们一般关心总线、设备以及驱动这三个实体。我们platform也提供了同样的实体。这里总线将设备与驱动进行了绑定。在注册设备时会去总线上寻找与之匹配的驱动,同样驱动被注册时也去它所属的总线上找寻设备。若找到了,并且匹配成功,则就会进行具体的操作。下面看看具体的使用情况。
    1、platform_device
    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设备有个特点,就是cpu总线可以直接寻址。我们对platform_device的定义通常在bsp的板级文件中进行,一般将platform归纳为一个数组,再通过platform_add_devices()函数统一注册。如下(arch/xxx/mach-xxx/xx):

点击(此处)折叠或打开

  1. static struct i2c_board_info dkb_i2c_camera[] = {
  2. #if defined(CONFIG_SOC_CAMERA_OV5642)
  3.         {
  4.                 I2C_BOARD_INFO("ov5642", 0x3c),
  5.         },
  6. };

  7. static struct soc_camera_link iclink_ov5642_dvp = {
  8.         .bus_id = 0, /* Must match with the camera ID */
  9.         .power = camera_sensor_power,
  10.         .board_info = &dkb_i2c_camera[0],
  11.         .i2c_adapter_id = 0,
  12.  /* .flags = SOCAM_MIPI, */
  13.         .module_name = "ov5642",
  14.         .priv = "pxa910-dvp",
  15. };

  16. static struct platform_device dkb_ov5642_dvp = {
  17.         .name = "soc-camera-pdrv",
  18.         .id = 0,
  19.         .dev = {
  20.                 .platform_data = &iclink_ov5642_dvp,
  21.         },
  22. };
  23. static struct platform_device *ttc_dkb_devices[] = {
  24.         &ttc_dkb_device_onenand,
  25.         &pxa910_device_rtc,
  26.         &dkb_ov5642_dvp,
  27. };
  28.                                         。
  29.                                         。
  30.                                         。
  31. platform_add_devices(ARRAY_AND_SIZE(ttc_dkb_devices));
    这里关于platform_data要说下,我们知道设备除了可以在bsp中定义资源外,还可以附加一些数据信息,因为对设备的硬件描述除了中断、内存、DMA通道外,可能还会有一些其它的依赖于板的配置,并且它们不适宜放置在driver中,因此这里的platform就提供了platform_data来容纳这些信息。platform_add_devices()将设备加到platform中去了。跟踪下看看如何增加的。

点击(此处)折叠或打开

  1. /**
  2.  * platform_add_devices - add a numbers of platform devices
  3.  * @devs: array of platform devices to add
  4.  * @num: number of platform devices in array
  5.  */
  6. int platform_add_devices(struct platform_device **devs, int num)
  7. {
  8.         int i, ret = 0;
  9.         
  10.         for (i = 0; i < num; i++) {
  11.                 ret = platform_device_register(devs[i]);
  12.                 if (ret) {
  13.                         while (--i >= 0)
  14.                                 platform_device_unregister(devs[i]);
  15.                         break;
  16.                 }
  17.         }

  18.         return ret;
  19. }
  20. EXPORT_SYMBOL_GPL(platform_add_devices);
    没啥好说的,再看platform_devcie_register():

点击(此处)折叠或打开

  1. /**
  2.  * platform_device_register - add a platform-level device
  3.  * @pdev: platform device we're adding
  4.  */
  5. int platform_device_register(struct platform_device *pdev)
  6. {
  7.         device_initialize(&pdev->dev);
  8.         return platform_device_add(pdev);
  9. }
  10. EXPORT_SYMBOL_GPL(platform_device_register);
    我们看到这里有一个初始化的函数以及增加platform设备的函数。其中device_initialize()函数主要是对pdev嵌入的struct device结构的初始化,这里将platform设备与struct device进行了绑定。我们再看platform_device_add()函数,此函数比较长,我们截取重要部分进行分析。

点击(此处)折叠或打开

  1. int platform_device_add(struct platform_device *pdev)
  2. {
  3.         int i, ret = 0;

  4.         if (!pdev)
  5.                 return -EINVAL;

  6.         if (!pdev->dev.parent)
  7.                 pdev->dev.parent = &platform_bus;

  8.         pdev->dev.bus = &platform_bus_type;
  9.             。
  10.             。
  11.             。
  12.         ret = device_add(&pdev->dev);
  13.         if (ret == 0)
  14.                 return ret;
  15.             。
  16.             。
  17.             。
  18.         return ret;
  19. }
  20. EXPORT_SYMBOL_GPL(platform_device_add);
    我们看到我们的platform挂在platform_bus上(第9行),这里的platform_bus在具体的驱动中已经被注册了。作为我们现在设备的父目录。而device_add()函数中主要是调用很多的例程完成dev的添加,其中我们需要关注的是bus_probe_device(dev),如下:

点击(此处)折叠或打开

  1. /**
  2.  * bus_probe_device - probe drivers for a new device
  3.  * @dev: device to probe
  4.  *
  5.  * - Automatically probe for a driver if the bus allows it.
  6.  */
  7. void bus_probe_device(struct device *dev)
  8. {
  9.         struct bus_type *bus = dev->bus;
  10.         int ret;

  11.         if (bus && bus->p->drivers_autoprobe) {
  12.                 ret = device_attach(dev);
  13.                 WARN_ON(ret < 0);
  14.         }
  15. }
    这个函数实现对我们新加的设备的探测,再看device_attach()函数:

点击(此处)折叠或打开

  1. /**
  2.  * device_attach - try to attach device to a driver.
  3.  * @dev: device.
  4.  *
  5.  * Walk the list of drivers that the bus has and call
  6.  * driver_probe_device() for each pair. If a compatible
  7.  * pair is found, break out and return.
  8.  *
  9.  * Returns 1 if the device was bound to a driver;
  10.  * 0 if no matching driver was found;
  11.  * -ENODEV if the device is not registered.
  12.  *
  13.  * When called for a USB interface, @dev->parent lock must be held.
  14.  */
  15. int device_attach(struct device *dev)
  16. {
  17.         int ret = 0;
  18.                 
  19.         device_lock(dev);
  20.         if (dev->driver) {
  21.                 if (klist_node_attached(&dev->p->knode_driver)) {
  22.                         ret = 1;
  23.                         goto out_unlock;
  24.                 }
  25.                 ret = device_bind_driver(dev);
  26.                 if (ret == 0)
  27.                         ret = 1;
  28.                 else {
  29.                         dev->driver = NULL;
  30.                         ret = 0;
  31.                 }
  32.         } else {
  33.                 pm_runtime_get_noresume(dev);
  34.                 ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
  35.                 pm_runtime_put_sync(dev);
  36.         }
  37. out_unlock:
  38.         device_unlock(dev);
  39.         return ret;
  40. }
  41. EXPORT_SYMBOL_GPL(device_attach);
    一个bus_for_each_drv()揭开了谜底,它要实现的就是查找与我们device匹对的driver。如果找到就调用__device_attach()这个回调函数。看下__device_attach():

点击(此处)折叠或打开

  1. static int __device_attach(struct device_driver *drv, void *data)
  2. {
  3.         struct device *dev = data;

  4.         if (!driver_match_device(drv, dev))
  5.                 return 0;

  6.         return driver_probe_device(drv, dev);
  7. }
    如果device与driver匹对成功,则调用driver_probe_device()函数,此函数中使用really_probe()完成最后的调用,看下:

点击(此处)折叠或打开

  1. static int really_probe(struct device *dev, struct device_driver *drv)
  2. {
  3.         .
  4.         .
  5.         .
  6.         if (dev->bus->probe) {
  7.                 ret = dev->bus->probe(dev);
  8.                 if (ret)
  9.                         goto probe_failed;
  10.         } else if (drv->probe) {
  11.                 ret = drv->probe(dev);
  12.                 if (ret)
  13.                         goto probe_failed;
  14.         }
  15.         .
  16.         .
  17.         .
  18. }
    6-14行:如果设置了dev->bus->probe,则调用之,但在platform_bus_type 没有设置,所以我们这里就调用platform驱动注册在device_driver的probe,但是这里暂时还未实现,关于此probe就要看下面了。

2、platform_driver
    粗略浅析了platform_device,我们再看看platform_driver又是怎样工作的呢。一般我们通过platform_driver_probe()或者platform_driver_register()完成driver的注册,这两者功能基本相同,它们的区别就是如果使用前者,则此driver不能被以后其他的device probe了,也就是说此driver只能和一个device进行绑定。下面我们看看具体的driver是怎么跑起来的:

点击(此处)折叠或打开

  1. static struct platform_driver __refdata soc_camera_pdrv = {
  2.         .remove = __devexit_p(soc_camera_pdrv_remove),
  3.         .driver = {
  4.                 .name = "soc-camera-pdrv",
  5.                 .owner = THIS_MODULE,
  6.         },
  7. };
  8.  
  9. platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
    这边的name跟上面的name一模一样,为了日后的绑定埋下伏笔。在platform_driver_probe()函数里调用了platform_driver_register()函数,看下它:

点击(此处)折叠或打开

  1. /**
  2.  * platform_driver_register - register a driver for platform-level devices
  3.  * @drv: platform driver structure
  4.  */
  5. int platform_driver_register(struct platform_driver *drv)
  6. {
  7.         drv->driver.bus = &platform_bus_type;
  8.         if (drv->probe)
  9.                 drv->driver.probe = platform_drv_probe;
  10.         if (drv->remove)
  11.                 drv->driver.remove = platform_drv_remove;
  12.         if (drv->shutdown)
  13.                 drv->driver.shutdown = platform_drv_shutdown;

  14.         return driver_register(&drv->driver);
  15. }
  16. EXPORT_SYMBOL_GPL(platform_driver_register);
    这里将我们的driver设置成了platform_bus_type,我们说过driver和device是通过bus联系在一起的,这里就是通过platform_bus_type中注册的回调例程和属性来实现的,上面说的device与这里的driver的匹配就是通过platform_bus_type注册的回调例程mach()函数来完成的。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.         .pm = &platform_dev_pm_ops,
  7. };
  8.              。                   
  9.              。                   
  10.              。                   
  11. bus_register(&platform_bus_type);

    这里注册了一个名为”platform“的bus,在syfsfs显示为sys/bus/platform。好了,再看driver_register(),此函数中主要调用了bus_add_driver()函数,顾名思义,就是add driver to bus的意思。bus_add_driver()函数中又调用了driver_attach()函数,跟上面device_attach()很像,driver_attach()就是找寻跟此driver相匹配的device。

点击(此处)折叠或打开

  1. /**
  2.  * driver_attach - try to bind driver to devices.
  3.  * @drv: driver.
  4.  *
  5.  * Walk the list of devices that the bus has on it and try to
  6.  * match the driver with each one. If driver_probe_device()
  7.  * returns 0 and the @dev->driver is set, we've found a
  8.  * compatible pair.
  9.  */
  10. int driver_attach(struct device_driver *drv)
  11. {
  12.         return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
  13. }
  14. EXPORT_SYMBOL_GPL(driver_attach);
    第11行:遍历bus,对每个device使用回调函数__driver_attach()来鉴别是否和driver匹配。这里__driver_attach()与上面__device_attach()很相似,看下__driver_attach():

点击(此处)折叠或打开

  1. static int __driver_attach(struct device *dev, void *data)
  2. {
  3.         struct device_driver *drv = data;

  4.         /*
  5.          * Lock device and try to bind to it. We drop the error
  6.          * here and always return 0, because we need to keep trying
  7.          * to bind to devices and some drivers will return an error
  8.          * simply if it didn't support the device.
  9.          *
  10.          * driver_probe_device() will spit a warning if there
  11.          * is an error.
  12.          */

  13.         if (!driver_match_device(drv, dev))
  14.                 return 0;

  15.         if (dev->parent) /* Needed for USB */
  16.                 device_lock(dev->parent);
  17.         device_lock(dev);
  18.         if (!dev->driver)
  19.                 driver_probe_device(drv, dev);
  20.         device_unlock(dev);
  21.         if (dev->parent)
  22.                 device_unlock(dev->parent);

  23.         return 0;
  24. }
   真是犹抱琵琶半遮面,我们要得结果快要出来了。对dev->driver进行判断,显然我们的dev还未有driver,所以会执行driver_probe_device()函数,来看看它:

点击(此处)折叠或打开

  1. /**
  2.  * driver_probe_device - attempt to bind device & driver together
  3.  * @drv: driver to bind a device to
  4.  * @dev: device to try to bind to the driver
  5.  *
  6.  * This function returns -ENODEV if the device is not registered,
  7.  * 1 if the device is bound successfully and 0 otherwise.
  8.  *
  9.  * This function must be called with @dev lock held. When called for a
  10.  * USB interface, @dev->parent lock must be held as well.
  11.  */
  12. int driver_probe_device(struct device_driver *drv, struct device *dev)
  13. {
  14.         int ret = 0;

  15.         if (!device_is_registered(dev))
  16.                 return -ENODEV;

  17.         pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
  18.                  drv->bus->name, __func__, dev_name(dev), drv->name);

  19.         pm_runtime_get_noresume(dev);
  20.         pm_runtime_barrier(dev);
  21.         ret = really_probe(dev, drv);
  22.         pm_runtime_put_sync(dev);

  23.         return ret;
  24. }
    根据platform_device部分知晓,really_probe()完成最后的调用,我们在platform_device部分really_probe()时未能进行,因为driver不存在,而经过本driver的注册后就可以顺利进行了。最终会调用我们driver中绑定的probe,我们这里就是soc_camera_pdrv_probe。好了,platform_device与platform_driver浅析至此告一段落。上面结合了相关的例子对platform部分进行了简单的跟踪分析,其中牵扯到很多的东西,这里也不能具体罗列了,如果想深入了解的话,可以看下ldd3部分十四章linux设备模型,这样更有助于理解。






   
阅读(2037) | 评论(0) | 转发(0) |
0

上一篇:虚拟内存与物理内存的理解1

下一篇:没有了

给主人留下些什么吧!~~