平台总线设备模型

来源:互联网 发布:叶子节点算法 编辑:程序博客网 时间:2024/05/01 21:17

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

首先看平台总线的定义:

 946 struct bus_type platform_bus_type = {  947         .name           = "platform",  948         .dev_attrs      = platform_dev_attrs,  949         .match          = platform_match,  950         .uevent         = platform_uevent,  951         .pm             = &platform_dev_pm_ops,  952 }; 

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

 606 static int platform_match(struct device *dev, struct device_driver *drv)  607 {  608         struct platform_device *pdev = to_platform_device(dev);  609         struct platform_driver *pdrv = to_platform_driver(drv);  610  611         /* match against the id table first */  612         if (pdrv->id_table)  613                 return platform_match_id(pdrv->id_table, pdev) != NULL;  614  615         /* fall-back to driver name match */  616         return (strcmp(pdev->name, drv->name) == 0);  617 } 

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

 955 int __init platform_bus_init(void)  956 {  957         int error;  958  959         early_platform_cleanup();  960  961         error = device_register(&platform_bus);  962         if (error)  963                 return error;  964         error =  bus_register(&platform_bus_type);  965         if (error)  966                 device_unregister(&platform_bus);  967         return error;  968 } 

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

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

  28 struct device platform_bus = {   29         .init_name      = "platform",   30 };   31 EXPORT_SYMBOL_GPL(platform_bus); 

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

 17 struct platform_device {  18         const char      * name;  19         int             id;  20         struct device   dev;  21         u32             num_resources;  22         struct resource * resource;  23  24         struct platform_device_id       *id_entry;  25  26         /* arch specific additions */  27         struct pdev_archdata    archdata;  28 }; 

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

 18 struct resource {  19         resource_size_t start;  20         resource_size_t end;  21         const char *name;  22         unsigned long flags;  23         struct resource *parent, *sibling, *child;  24 }; 

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

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

  33 /**   34  * platform_get_resource - get a resource for a device   35  * @dev: platform device   36  * @type: resource type   37  * @num: resource index   38  */   39 struct resource *platform_get_resource(struct platform_device *dev,   40                                        unsigned int type, unsigned int num)   41 {   42         int i;   43   44         for (i = 0; i < dev->num_resources; i++) {   45                 struct resource *r = &dev->resource[i];   46   47                 if (type == resource_type(r) && num-- == 0)   48                         return r;   49         }   50         return NULL;   51 }   52 EXPORT_SYMBOL_GPL(platform_get_resource); 

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

  54 /**   55  * platform_get_irq - get an IRQ for a device   56  * @dev: platform device   57  * @num: IRQ number index   58  */   59 int platform_get_irq(struct platform_device *dev, unsigned int num)   60 {   61         struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);   62   63         return r ? r->start : -ENXIO;   64 }   65 EXPORT_SYMBOL_GPL(platform_get_irq); 

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

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

  54 /**   55  * platform_get_irq - get an IRQ for a device   56  * @dev: platform device   57  * @num: IRQ number index   58  */   59 int platform_get_irq(struct platform_device *dev, unsigned int num)   60 {   61         struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);   62   63         return r ? r->start : -ENXIO;   64 }   65 EXPORT_SYMBOL_GPL(platform_get_irq); 

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

 227 /**  228  * platform_device_add - add a platform device to device hierarchy  229  * @pdev: platform device we're adding  230  *  231  * This is part 2 of platform_device_register(), though may be called  232  * separately _iff_ pdev was allocated by platform_device_alloc().  233  */  234 int platform_device_add(struct platform_device *pdev)  235 {  236         int i, ret = 0;  237  238         if (!pdev)  239                 return -EINVAL;  240  241         if (!pdev->dev.parent)  242                 pdev->dev.parent = &platform_bus;  243  244         pdev->dev.bus = &platform_bus_type;  245  246         if (pdev->id != -1)  247                 dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);  248         else  249                 dev_set_name(&pdev->dev, "%s", pdev->name);  250  251         for (i = 0; i < pdev->num_resources; i++) {  252                 struct resource *p, *r = &pdev->resource[i];  253  254                 if (r->name == NULL)  255                         r->name = dev_name(&pdev->dev);  256  257                 p = r->parent;  258                 if (!p) {  259                         if (resource_type(r) == IORESOURCE_MEM)  260                                 p = &iomem_resource;  261                         else if (resource_type(r) == IORESOURCE_IO)  262                                 p = &ioport_resource;  263                 }  264  265                 if (p && insert_resource(p, r)) {  266                         printk(KERN_ERR  267                                "%s: failed to claim resource %d\n",  268                                dev_name(&pdev->dev), i);  269                         ret = -EBUSY;  270                         goto failed;  271                 }  272         }  273  274         pr_debug("Registering platform device '%s'. Parent at %s\n",  275                  dev_name(&pdev->dev), dev_name(pdev->dev.parent));  276  277         ret = device_add(&pdev->dev);  278         if (ret == 0)  279                 return ret;  280  281  failed:  282         while (--i >= 0) {  283                 struct resource *r = &pdev->resource[i];  284                 unsigned long type = resource_type(r);  285  286                 if (type == IORESOURCE_MEM || type == IORESOURCE_IO)  287                         release_resource(r);  288         }  289  290         return ret;  291 }  292 EXPORT_SYMBOL_GPL(platform_device_add); 

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

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

 103 /**  104  * platform_add_devices - add a numbers of platform devices  105  * @devs: array of platform devices to add  106  * @num: number of platform devices in array  107  */  108 int platform_add_devices(struct platform_device **devs, int num)  109 {  110         int i, ret = 0;  111  112         for (i = 0; i < num; i++) {  113                 ret = platform_device_register(devs[i]);  114                 if (ret) {  115                         while (--i >= 0)  116                                 platform_device_unregister(devs[i]);  117                         break;  118                 }  119         }  120  121         return ret;  122 }  123 EXPORT_SYMBOL_GPL(platform_add_devices); 

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

 331 /**  332  * platform_device_unregister - unregister a platform-level device  333  * @pdev: platform device we're unregistering  334  *  335  * Unregistration is done in 2 steps. First we release all resources  336  * and remove it from the subsystem, then we drop reference count by  337  * calling platform_device_put().  338  */  339 void platform_device_unregister(struct platform_device *pdev)  340 {  341         platform_device_del(pdev);  342         platform_device_put(pdev);  343 }  344 EXPORT_SYMBOL_GPL(platform_device_unregister);  294 /**  295  * platform_device_del - remove a platform-level device  296  * @pdev: platform device we're removing  297  *  298  * Note that this function will also release all memory- and port-based  299  * resources owned by the device (@dev->resource).  This function must  300  * _only_ be externally called in error cases.  All other usage is a bug.  301  */  302 void platform_device_del(struct platform_device *pdev)  303 {  304         int i;  305  306         if (pdev) {  307                 device_del(&pdev->dev);  308  309                 for (i = 0; i < pdev->num_resources; i++) {  310                         struct resource *r = &pdev->resource[i];  311                         unsigned long type = resource_type(r);  312  313                         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)  314                                 release_resource(r);  315                 }  316         }  317 }  318 EXPORT_SYMBOL_GPL(platform_device_del); 

device_del就是device_unregister那的函数

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

 58 struct platform_driver {  59         int (*probe)(struct platform_device *);  60         int (*remove)(struct platform_device *);  61         void (*shutdown)(struct platform_device *);  62         int (*suspend)(struct platform_device *, pm_message_t state);  63         int (*resume)(struct platform_device *);  64         struct device_driver driver;  65         struct platform_device_id *id_table;  66 }; 

驱动注册

 474 /**  475  * platform_driver_register  476  * @drv: platform driver structure  477  */  478 int platform_driver_register(struct platform_driver *drv)  479 {  480         drv->driver.bus = &platform_bus_type;  481         if (drv->probe)  482                 drv->driver.probe = platform_drv_probe;  483         if (drv->remove)  484                 drv->driver.remove = platform_drv_remove;  485         if (drv->shutdown)  486                 drv->driver.shutdown = platform_drv_shutdown;  487  488         return driver_register(&drv->driver);  489 }  490 EXPORT_SYMBOL_GPL(platform_driver_register); 

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

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

 445 static int platform_drv_probe(struct device *_dev)  446 {  447         struct platform_driver *drv = to_platform_driver(_dev->driver);  448         struct platform_device *dev = to_platform_device(_dev);  449  450         return drv->probe(dev);  451 } 

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

 458 static int platform_drv_remove(struct device *_dev)  459 {  460         struct platform_driver *drv = to_platform_driver(_dev->driver);  461         struct platform_device *dev = to_platform_device(_dev);  462  463         return drv->remove(dev);  464 } 

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

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

驱动注销

 492 /**  493  * platform_driver_unregister  494  * @drv: platform driver structure  495  */  496 void platform_driver_unregister(struct platform_driver *drv)  497 {  498         driver_unregister(&drv->driver);  499 }  500 EXPORT_SYMBOL_GPL(platform_driver_unregister);