平台总线设备模型

来源:互联网 发布:搜图片出处软件 编辑:程序博客网 时间:2024/05/01 23:31

http://blog.csdn.net/mcgrady_tracy/article/details/7210915

平台总线是内核实现的一条虚拟总线,Linux设备模型包含三个重要的元素,总线、设备和驱动,那看看平台总线又是怎样去实现的。

首先看平台总线的定义:

[cpp] view plaincopy
  1. 946 struct bus_type platform_bus_type = {   
  2. 947         .name           = "platform",   
  3. 948         .dev_attrs      = platform_dev_attrs,   
  4. 949         .match          = platform_match,   
  5. 950         .uevent         = platform_uevent,   
  6. 951         .pm             = &platform_dev_pm_ops,   
  7. 952 };   

我们知道总线匹配设备和驱动是通过它的match函数,那具体看看这个函数是怎样实现的。

[cpp] view plaincopy
  1. 606 static int platform_match(struct device *dev, struct device_driver *drv)   
  2. 607 {   
  3. 608         struct platform_device *pdev = to_platform_device(dev);   
  4. 609         struct platform_driver *pdrv = to_platform_driver(drv);   
  5. 610   
  6. 611         /* match against the id table first */   
  7. 612         if (pdrv->id_table)   
  8. 613                 return platform_match_id(pdrv->id_table, pdev) != NULL;   
  9. 614   
  10. 615         /* fall-back to driver name match */   
  11. 616         return (strcmp(pdev->name, drv->name) == 0);   
  12. 617 }   

我们看,如果平台驱动有一个id_table,那就通过函数platform_match_id去匹配,如果没有就比较平台设备的name字段和平台驱动的name字段是否相同,这也就是平台总线的匹配规则。再来看平台总线的注册。

[cpp] view plaincopy
  1. 955 int __init platform_bus_init(void)   
  2. 956 {   
  3. 957         int error;   
  4. 958   
  5. 959         early_platform_cleanup();   
  6. 960   
  7. 961         error = device_register(&platform_bus);   
  8. 962         if (error)   
  9. 963                 return error;   
  10. 964         error =  bus_register(&platform_bus_type);   
  11. 965         if (error)   
  12. 966                 device_unregister(&platform_bus);   
  13. 967         return error;   
  14. 968 }   

我们看平台总线注册就是采用的bus_register函数,再看在注册平台总线之前,还调用了

device_register去注册了一个设备,因为总线它也是一个设备,也要被注册进内核。那就具体来看这个设备是怎么定义的。

[cpp] view plaincopy
  1. 28 struct device platform_bus = {   
  2. 29         .init_name      = "platform",   
  3. 30 };   
  4. 31 EXPORT_SYMBOL_GPL(platform_bus);   

我们看就给了一个名字。看完了总线,又来看看平台设备又是怎样定义怎样去注册。

[cpp] view plaincopy
  1.  17 struct platform_device {   
  2.  18         const char      * name;   
  3.  19         int             id;   
  4.  20         struct device   dev;   
  5.  21         u32             num_resources;   
  6.  22         struct resource * resource;   
  7.  23   
  8.  24         struct platform_device_id       *id_entry;   
  9.  25   
  10.  26         /* arch specific additions */   
  11.  27         struct pdev_archdata    archdata;   
  12.  28 };   

其中有个重要的元素resource,该元素存入的最重要的设备资源信息,比如I/O基地址,中断号等等。structresource结构定义在include/linux/ioport.h

[cpp] view plaincopy
  1. 18 struct resource {   
  2. 19         resource_size_t start;   
  3. 20         resource_size_t end;   
  4. 21         const char *name;   
  5. 22         unsigned long flags;   
  6. 23         struct resource *parent, *sibling, *child;   
  7. 24 };   

有可能设备的资源不只一个,定义资源时定义成一个数组的形式,那就使用函数去获取,

platform_get_resource就是用来获取设备的资源信息,去看看这个函数

[cpp] view plaincopy
  1. 33 /**  
  2. 34  * platform_get_resource - get a resource for a device  
  3. 35  * @dev: platform device  
  4. 36  * @type: resource type  
  5. 37  * @num: resource index  
  6. 38  */   
  7. 39 struct resource *platform_get_resource(struct platform_device *dev,   
  8. 40                                        unsigned int type, unsigned int num)   
  9. 41 {   
  10. 42         int i;   
  11. 43   
  12. 44         for (i = 0; i < dev->num_resources; i++) {   
  13. 45                 struct resource *r = &dev->resource[i];   
  14. 46   
  15. 47                 if (type == resource_type(r) && num-- == 0)   
  16. 48                         return r;   
  17. 49         }   
  18. 50         return NULL;   
  19. 51 }   
  20. 52 EXPORT_SYMBOL_GPL(platform_get_resource);   

这个函数的第一个参数为要获取资源的平台设备,第二个参数为资源类型,比如IORESOURCE_MEM,第三个参数为资源在数组中的一个号。如果是获取中断号,还可以使用函数platform_get_irq

[cpp] view plaincopy
  1. 54 /**  
  2. 55  * platform_get_irq - get an IRQ for a device  
  3. 56  * @dev: platform device  
  4. 57  * @num: IRQ number index  
  5. 58  */   
  6. 59 int platform_get_irq(struct platform_device *dev, unsigned int num)   
  7. 60 {   
  8. 61         struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);   
  9. 62   
  10. 63         return r ? r->start : -ENXIO;   
  11. 64 }   
  12. 65 EXPORT_SYMBOL_GPL(platform_get_irq);   

我们看这个函数也是调用platform_get_resource去获取资源,只是它获取资源的类型为IORESOURCE_IRQ,最后返回中断号。

再来看平台设备的注册,平台设备注册采用platform_device_register函数

[cpp] view plaincopy
  1. 54 /**  
  2. 55  * platform_get_irq - get an IRQ for a device  
  3. 56  * @dev: platform device  
  4. 57  * @num: IRQ number index  
  5. 58  */   
  6. 59 int platform_get_irq(struct platform_device *dev, unsigned int num)   
  7. 60 {   
  8. 61         struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);   
  9. 62   
  10. 63         return r ? r->start : -ENXIO;   
  11. 64 }   
  12. 65 EXPORT_SYMBOL_GPL(platform_get_irq);   

device_initialize就是device_register那的函数,那就看platform_device_add

[cpp] view plaincopy
  1. 227 /**  
  2. 228  * platform_device_add - add a platform device to device hierarchy  
  3. 229  * @pdev: platform device we're adding  
  4. 230  *  
  5. 231  * This is part 2 of platform_device_register(), though may be called  
  6. 232  * separately _iff_ pdev was allocated by platform_device_alloc().  
  7. 233  */   
  8. 234 int platform_device_add(struct platform_device *pdev)   
  9. 235 {   
  10. 236         int i, ret = 0;   
  11. 237   
  12. 238         if (!pdev)   
  13. 239                 return -EINVAL;   
  14. 240   
  15. 241         if (!pdev->dev.parent)   
  16. 242                 pdev->dev.parent = &platform_bus;   
  17. 243   
  18. 244         pdev->dev.bus = &platform_bus_type;   
  19. 245   
  20. 246         if (pdev->id != -1)   
  21. 247                 dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);   
  22. 248         else   
  23. 249                 dev_set_name(&pdev->dev, "%s", pdev->name);   
  24. 250   
  25. 251         for (i = 0; i < pdev->num_resources; i++) {   
  26. 252                 struct resource *p, *r = &pdev->resource[i];   
  27. 253   
  28. 254                 if (r->name == NULL)   
  29. 255                         r->name = dev_name(&pdev->dev);   
  30. 256   
  31. 257                 p = r->parent;   
  32. 258                 if (!p) {   
  33. 259                         if (resource_type(r) == IORESOURCE_MEM)   
  34. 260                                 p = &iomem_resource;   
  35. 261                         else if (resource_type(r) == IORESOURCE_IO)   
  36. 262                                 p = &ioport_resource;   
  37. 263                 }   
  38. 264   
  39. 265                 if (p && insert_resource(p, r)) {   
  40. 266                         printk(KERN_ERR   
  41. 267                                "%s: failed to claim resource %d\n",   
  42. 268                                dev_name(&pdev->dev), i);   
  43. 269                         ret = -EBUSY;   
  44. 270                         goto failed;   
  45. 271                 }   
  46. 272         }   
  47. 273   
  48. 274         pr_debug("Registering platform device '%s'. Parent at %s\n",   
  49. 275                  dev_name(&pdev->dev), dev_name(pdev->dev.parent));   
  50. 276   
  51. 277         ret = device_add(&pdev->dev);   
  52. 278         if (ret == 0)   
  53. 279                 return ret;   
  54. 280   
  55. 281  failed:   
  56. 282         while (--i >= 0) {   
  57. 283                 struct resource *r = &pdev->resource[i];   
  58. 284                 unsigned long type = resource_type(r);   
  59. 285   
  60. 286                 if (type == IORESOURCE_MEM || type == IORESOURCE_IO)   
  61. 287                         release_resource(r);   
  62. 288         }   
  63. 289   
  64. 290         return ret;   
  65. 291 }   
  66. 292 EXPORT_SYMBOL_GPL(platform_device_add);   

最终调用device_register那的device_add完成平台设备的注册。

我们也可以使用platform_add_devices去注册一组平台设备

[cpp] view plaincopy
  1. 103 /**  
  2. 104  * platform_add_devices - add a numbers of platform devices  
  3. 105  * @devs: array of platform devices to add  
  4. 106  * @num: number of platform devices in array  
  5. 107  */   
  6. 108 int platform_add_devices(struct platform_device **devs, int num)   
  7. 109 {   
  8. 110         int i, ret = 0;   
  9. 111   
  10. 112         for (i = 0; i < num; i++) {   
  11. 113                 ret = platform_device_register(devs[i]);   
  12. 114                 if (ret) {   
  13. 115                         while (--i >= 0)   
  14. 116                                 platform_device_unregister(devs[i]);   
  15. 117                         break;   
  16. 118                 }   
  17. 119         }   
  18. 120   
  19. 121         return ret;   
  20. 122 }   
  21. 123 EXPORT_SYMBOL_GPL(platform_add_devices);   

看完了注册来看注销函数,注销函数就是platform_device_unregister

[cpp] view plaincopy
  1. 331 /**  
  2. 332  * platform_device_unregister - unregister a platform-level device  
  3. 333  * @pdev: platform device we're unregistering  
  4. 334  *  
  5. 335  * Unregistration is done in 2 steps. First we release all resources  
  6. 336  * and remove it from the subsystem, then we drop reference count by  
  7. 337  * calling platform_device_put().  
  8. 338  */   
  9. 339 void platform_device_unregister(struct platform_device *pdev)   
  10. 340 {   
  11. 341         platform_device_del(pdev);   
  12. 342         platform_device_put(pdev);   
  13. 343 }   
  14. 344 EXPORT_SYMBOL_GPL(platform_device_unregister);   
  15. 294 /**  
  16. 295  * platform_device_del - remove a platform-level device  
  17. 296  * @pdev: platform device we're removing  
  18. 297  *  
  19. 298  * Note that this function will also release all memory- and port-based  
  20. 299  * resources owned by the device (@dev->resource).  This function must  
  21. 300  * _only_ be externally called in error cases.  All other usage is a bug.  
  22. 301  */   
  23. 302 void platform_device_del(struct platform_device *pdev)   
  24. 303 {   
  25. 304         int i;   
  26. 305   
  27. 306         if (pdev) {   
  28. 307                 device_del(&pdev->dev);   
  29. 308   
  30. 309                 for (i = 0; i < pdev->num_resources; i++) {   
  31. 310                         struct resource *r = &pdev->resource[i];   
  32. 311                         unsigned long type = resource_type(r);   
  33. 312   
  34. 313                         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)   
  35. 314                                 release_resource(r);   
  36. 315                 }   
  37. 316         }   
  38. 317 }   
  39. 318 EXPORT_SYMBOL_GPL(platform_device_del);   

device_del就是device_unregister那的函数

再来看驱动,平台设备驱动结构定义

[cpp] view plaincopy
  1. 58 struct platform_driver {   
  2. 59         int (*probe)(struct platform_device *);   
  3. 60         int (*remove)(struct platform_device *);   
  4. 61         void (*shutdown)(struct platform_device *);   
  5. 62         int (*suspend)(struct platform_device *, pm_message_t state);   
  6. 63         int (*resume)(struct platform_device *);   
  7. 64         struct device_driver driver;   
  8. 65         struct platform_device_id *id_table;   
  9. 66 };   

驱动注册

[cpp] view plaincopy
  1. 474 /**  
  2. 475  * platform_driver_register  
  3. 476  * @drv: platform driver structure  
  4. 477  */   
  5. 478 int platform_driver_register(struct platform_driver *drv)   
  6. 479 {   
  7. 480         drv->driver.bus = &platform_bus_type;   
  8. 481         if (drv->probe)   
  9. 482                 drv->driver.probe = platform_drv_probe;   
  10. 483         if (drv->remove)   
  11. 484                 drv->driver.remove = platform_drv_remove;   
  12. 485         if (drv->shutdown)   
  13. 486                 drv->driver.shutdown = platform_drv_shutdown;   
  14. 487   
  15. 488         return driver_register(&drv->driver);   
  16. 489 }   
  17. 490 EXPORT_SYMBOL_GPL(platform_driver_register);   

平台驱动结构里面有个成员driver,它是device_driver结构类型,它的probe函数指针赋值了这里的platform_drv_probe,也就是平台总线匹配设备和驱动成功后,将调用这里的

platform_drv_probe函数,那就在去看看这个函数。

[cpp] view plaincopy
  1. 445 static int platform_drv_probe(struct device *_dev)   
  2. 446 {   
  3. 447         struct platform_driver *drv = to_platform_driver(_dev->driver);   
  4. 448         struct platform_device *dev = to_platform_device(_dev);   
  5. 449   
  6. 450         return drv->probe(dev);   
  7. 451 }   

还有一点的是它的remove函数为这里的platform_drv_remove,不管是设备注销还是驱动注销都是先调用这个函数,然后才调用平台驱动的remove函数。

[cpp] view plaincopy
  1. 458 static int platform_drv_remove(struct device *_dev)   
  2. 459 {   
  3. 460         struct platform_driver *drv = to_platform_driver(_dev->driver);   
  4. 461         struct platform_device *dev = to_platform_device(_dev);   
  5. 462   
  6. 463         return drv->remove(dev);   
  7. 464 }   

也就是最后调用platform_driverprobe函数,它的参数devplatform_device结构类型。注意这里有两个probe,一个是platform_driverprobe,它是要求我们在编写平台设备驱动时自己去定义,另一个是device_driverprobe,它供总线匹配设备和驱动成功后调用,probe为这里的platform_drv_probe,这个函数的功能就是调用platform_driverprobe

平台设备驱动注册最后调用的就是driver_register,只不过这里的总线是平台总线。

驱动注销

[cpp] view plaincopy
  1. 492 /**  
  2. 493  * platform_driver_unregister  
  3. 494  * @drv: platform driver structure  
  4. 495  */   
  5. 496 void platform_driver_unregister(struct platform_driver *drv)   
  6. 497 {   
  7. 498         driver_unregister(&drv->driver);   
  8. 499 }   
  9. 500 EXPORT_SYMBOL_GPL(platform_driver_unregister);   

原创粉丝点击