zoukankan      html  css  js  c++  java
  • Linux设备驱动模型之platform(平台)总线详解

    /********************************************************/

    内核版本:2.6.35.7

    运行平台:三星s5pv210

    /********************************************************/

    1、什么是platform(平台)总线?

    相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。

    那为什么需要platform总线呢?其实是Linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。因为对于usb设备、i2c设备、

    pci设备、spi设备等等,他们与cpu的通信都是直接挂在相应的总线下面与我们的cpu进行数据交互的,但是在我们的嵌入式系统当中,

    并不是所有的设备都能够归属于这些常见的总线,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设

    却不依附与此类总线。所以Linux驱动模型为了保持完整性,将这些设备挂在一条虚拟的总线上(platform总线),而不至于使得有些

    设备挂在总线上,另一些设备没有挂在总线上。

    platform总线相关代码:driveraseplatform.c 文件

    相关结构体定义:includelinuxplatform_device.h 文件中

    2、platform总线管理下的2员大将

    (1)两个结构体platform_device和platform_driver

    对于任何一种Linux设备驱动模型下的总线都由两个部分组成:描述设备相关的结构体和描述驱动相关的结构体

    在platform总线下就是platform_device和platform_driver,下面是对两个结构体的各个元素进行分析:

    platform_device结构体:(includelinuxplatform_device.h)

     1 struct platform_device {           //  platform总线设备
     2     const char    * name;          //  平台设备的名字
     3     int        id;                 //   ID 是用来区分如果设备名字相同的时候(通过在后面添加一个数字来代表不同的设备,因为有时候有这种需求)
     4     struct device    dev;          //   内置的device结构体
     5     u32        num_resources;      //   资源结构体数量
     6     struct resource    * resource; //   指向一个资源结构体数组
     7 
     8     const struct platform_device_id    *id_entry; //  用来进行与设备驱动匹配用的id_table表
     9 
    10     /* arch specific additions */
    11     struct pdev_archdata    archdata;             //  自留地    添加自己的东西
    12 };

    platform_device结构体中的struct 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 };

    platform_driver结构体:(includelinuxplatform_device.h)

    1 struct platform_driver {
    2     int (*probe)(struct platform_device *);     //  这个probe函数其实和  device_driver中的是一样的功能,但是一般是使用device_driver中的那个
    3     int (*remove)(struct platform_device *);    //  卸载平台设备驱动的时候会调用这个函数,但是device_driver下面也有,具体调用的是谁这个就得分析了
    4     void (*shutdown)(struct platform_device *);
    5     int (*suspend)(struct platform_device *, pm_message_t state);
    6     int (*resume)(struct platform_device *);
    7     struct device_driver driver;                //   内置的device_driver 结构体 
    8     const struct platform_device_id *id_table;  //  该设备驱动支持的设备的列表  他是通过这个指针去指向  platform_device_id 类型的数组
    9 };

    (2)两组接口函数(driveraseplatform.c)

    int platform_driver_register(struct platform_driver *);       // 用来注册我们的设备驱动    

    void platform_driver_unregister(struct platform_driver *);  // 用来卸载我们的设备驱动

    int platform_device_register(struct platform_device *);      // 用来注册我们的设备      

    void platform_device_unregister(struct platform_device *); // 用来卸载我们的设备

    3、platform平台总线的初始化

    (1)platform平台总线的注册初始化:  platform_bus_init

    /***********************************************************************/

    platform_bus_init

        early_platform_cleanup               //  进行一些早期的平台清理   

        device_register                          //  注册设备 (在/sys/devices/目录下建立 platform目录对应的设备对象  /sys/devices/platform/) 

        bus_register                              //  总线注册

    /************************************************************************/

    (2)相关结构体

     1 struct bus_type {
     2     const char        *name;                     //  总线名字
     3     struct bus_attribute    *bus_attrs;          //  该总线的属性
     4     struct device_attribute    *dev_attrs;       //  该总线下设备的属性
     5     struct driver_attribute    *drv_attrs;       //  该总线下设备驱动的属性
     6 
     7     int (*match)(struct device *dev, struct device_driver *drv);     //  该总线下设备与设备驱动的匹配函数
     8     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);  //   事件函数    热拨插
     9     int (*probe)(struct device *dev);                                //   总线下的  探针函数
    10     int (*remove)(struct device *dev);
    11     void (*shutdown)(struct device *dev);
    12 
    13     int (*suspend)(struct device *dev, pm_message_t state);
    14     int (*resume)(struct device *dev);
    15 
    16     const struct dev_pm_ops *pm;  //  电源管理相关的
    17 
    18     struct bus_type_private *p;   //  总线的私有数据  p->subsys.kobj 表示该总线在驱动模型中对应的对象
    19 };
     1 struct bus_type_private {
     2     struct kset subsys;                //  这个是bus主要的kset
     3     struct kset *drivers_kset;         //  这个kset指针用来指向该总线的 drivers目录的
     4     struct kset *devices_kset;         //  这个kse指针用来指向该总线的devices目录的
     5     struct klist klist_devices;        //  用来挂接该总线下的设备的一个链表头
     6     struct klist klist_drivers;        //   用来挂接该总线下的设备驱动的一个链表头
     7     struct blocking_notifier_head bus_notifier;
     8     unsigned int drivers_autoprobe:1;  //   是否需要在设备驱动注册时候子自动匹配设备
     9     struct bus_type *bus;              //  指向本bus结构体
    10 };

    (3)函数详解

    bus_register:

     1 int bus_register(struct bus_type *bus)
     2 {
     3     int retval;
     4     struct bus_type_private *priv;                               //  定义一个bus_type_private 结构体指针
     5 
     6     priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); //   申请分配内存
     7     if (!priv)
     8         return -ENOMEM;
     9 
    10     priv->bus = bus;            //   使用 priv->bus 指向我们传进来的bus
    11     bus->p = priv;              //   通过  bus->p  指向priv    这里其实就是将bus与priv建立关系,这个跟之前的device、class的设计是一样的
    12 
    13     BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
    14 
    15     retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);  //   给我们的bus在设备驱动模型中的对象设置名字   bus->p->subsys.kobj
    16     if (retval)
    17         goto out;
    18 
    19 //   这里就是对bus的私有数据进行一些填充
    20     priv->subsys.kobj.kset = bus_kset;      //  设置bus对象的父对象     也就是  /sys/bus 这目录 作为他的上层目录  所有的具体的总线类型对象都是在这个目录下
    21     priv->subsys.kobj.ktype = &bus_ktype;   //  设置bus对象的  对象类型为 bus_ktype
    22     priv->drivers_autoprobe = 1;            //  配置为在注册设备或者是注册设备驱动时自动进行配置    这个就决定了为什么我们在注册设备或者是设备驱动能够进行自动匹配
    23 
    24     retval = kset_register(&priv->subsys);  //  注册kset结构体(内部会调用kobject_add_internal函数,也就是将bus对象添加到 /sys/bus/目录下, /sys/bus/xxx_busType  对应具体的总线)
    25     if (retval)
    26         goto out;
    27 
    28     retval = bus_create_file(bus, &bus_attr_uevent);  //  在该bus下建立属性文件   (对应的就是 bus下的 uevent属性)
    29     if (retval)
    30         goto bus_uevent_fail;
    31 
    32     priv->devices_kset = kset_create_and_add("devices", NULL, //  在具体总线的目录下创建 kset 容器对象   /sys/bus/xxx_busType/devices
    33                          &priv->subsys.kobj);                 //  通过priv->devices_kset指针去指向 这个目录对应的对象
    34     if (!priv->devices_kset) {
    35         retval = -ENOMEM;
    36         goto bus_devices_fail;
    37     }
    38 
    39     priv->drivers_kset = kset_create_and_add("drivers", NULL,   //   /sys/bus/xxx_busType/drivers
    40                          &priv->subsys.kobj);                   //   通过 priv->drivers_kset 指针去指向  这个目录对应的对象
    41     if (!priv->drivers_kset) {
    42         retval = -ENOMEM;
    43         goto bus_drivers_fail;
    44     }
    45 
    46     klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);  //  初始化链表  klist
    47     klist_init(&priv->klist_drivers, NULL, NULL);                            //  初始化链表  klist
    48 
    49     retval = add_probe_files(bus);    //  添加探针文件   其实内部做的还是添加属性文件   /sys/bus/xxx_busType/drivers_probe    /sys/bus/xxx_busType/drivers_autoprobe
    50     if (retval)
    51         goto bus_probe_files_fail;
    52 
    53     retval = bus_add_attrs(bus);      //  根据 bus->bus_attrs 中的属性设置来添加属性文件
    54     if (retval)
    55         goto bus_attrs_fail;
    56 
    57     pr_debug("bus: '%s': registered
    ", bus->name);
    58     return 0;
    59 
    60 bus_attrs_fail:
    61     remove_probe_files(bus);
    62 bus_probe_files_fail:
    63     kset_unregister(bus->p->drivers_kset);
    64 bus_drivers_fail:
    65     kset_unregister(bus->p->devices_kset);
    66 bus_devices_fail:
    67     bus_remove_file(bus, &bus_attr_uevent);
    68 bus_uevent_fail:
    69     kset_unregister(&bus->p->subsys);
    70     kfree(bus->p);
    71 out:
    72     bus->p = NULL;
    73     return retval;
    74 }

    4、platform平台设备注册

    (1)platform平台总线注册函数:  platform_device_register

    /************************************************************************************/

    platform_device_register

        device_initialize

        platform_device_add

            device_add           //  这个函数之前分析过了,这里就不再分析了 

    /***********************************************************************************/

    (2)函数分析

     1 int platform_device_add(struct platform_device *pdev)
     2 {
     3     int i, ret = 0;
     4 
     5     if (!pdev)
     6         return -EINVAL;
     7 
     8     if (!pdev->dev.parent)              
     9         pdev->dev.parent = &platform_bus;       //  将平台设备的父设备设置为 platform_bus (对应的就是  /sys/devices/platform 这个目录)
    10 
    11     pdev->dev.bus = &platform_bus_type;         //  设置平台设备挂接在 platform总线下     platform_bus_type
    12 
    13     if (pdev->id != -1)
    14         dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id); //  给平台设备对应的对象设置名字  name.id  (如果我们的 pdev->id 设置不等于-1时)
    15     else
    16         dev_set_name(&pdev->dev, "%s", pdev->name);
    17 
    18 //   下面的for 循环是对平台设备资源的一些处理
    19     for (i = 0; i < pdev->num_resources; i++) {
    20         struct resource *p, *r = &pdev->resource[i];
    21 
    22         if (r->name == NULL)
    23             r->name = dev_name(&pdev->dev);
    24 
    25         p = r->parent;
    26         if (!p) {
    27             if (resource_type(r) == IORESOURCE_MEM)
    28                 p = &iomem_resource;
    29             else if (resource_type(r) == IORESOURCE_IO)
    30                 p = &ioport_resource;
    31         }
    32 
    33         if (p && insert_resource(p, r)) {
    34             printk(KERN_ERR
    35                    "%s: failed to claim resource %d
    ",
    36                    dev_name(&pdev->dev), i);
    37             ret = -EBUSY;
    38             goto failed;
    39         }
    40     }
    41 //////////////////////////////////////////////////////////////////
    42 
    43     pr_debug("Registering platform device '%s'. Parent at %s
    ",
    44          dev_name(&pdev->dev), dev_name(pdev->dev.parent));
    45 
    46     ret = device_add(&pdev->dev);    //   将平台设备添加到系统中去  /sys/devices/platform/xxx
    47     if (ret == 0)
    48         return ret;
    49 
    50  failed:
    51     while (--i >= 0) {
    52         struct resource *r = &pdev->resource[i];
    53         unsigned long type = resource_type(r);
    54 
    55         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
    56             release_resource(r);
    57     }
    58 
    59     return ret;
    60 }

    5、platform平台设备驱动注册

    (1)platform平台设备驱动注册函数: platform_driver_register

    /*********************************************************************/

    platform_driver_register

        driver_register

            driver_find

            bus_add_driver

                kobject_init_and_add

                driver_attach

                klist_add_tail

                module_add_driver

                driver_create_file

                driver_add_attrs

            driver_add_groups

    /************************************************************/

    (2)函数详解

    platform_driver_register:

     1 int platform_driver_register(struct platform_driver *drv)
     2 {
     3     drv->driver.bus = &platform_bus_type;            //  设置设备驱动 挂接在 platform平台总线下  
     4 
     5 //  下面做的就是对 drv 中的函数指针进行填充
     6     if (drv->probe)
     7         drv->driver.probe = platform_drv_probe;  
     8     if (drv->remove)
     9         drv->driver.remove = platform_drv_remove;
    10     if (drv->shutdown)
    11         drv->driver.shutdown = platform_drv_shutdown;
    12 
    13     return driver_register(&drv->driver);             //  注册设备驱动
    14 }

    driver_register:

     1 int driver_register(struct device_driver *drv)
     2 {
     3     int ret;
     4     struct device_driver *other;           //    定义一个设备驱动指针  other
     5 
     6     BUG_ON(!drv->bus->p);
     7 
     8     if ((drv->bus->probe && drv->probe) ||
     9         (drv->bus->remove && drv->remove) ||
    10         (drv->bus->shutdown && drv->shutdown))
    11         printk(KERN_WARNING "Driver '%s' needs updating - please use "
    12             "bus_type methods
    ", drv->name);
    13 
    14     other = driver_find(drv->name, drv->bus);  //   这个函数其实进行了一个校验  比对当前的 总线下是否存在名字和现在需要注册的设备驱动的名字相同的设备驱动
    15     if (other) {
    16         put_driver(other);                     //   如果名字相同 直接打印错误  并退出
    17         printk(KERN_ERR "Error: Driver '%s' is already registered, "
    18             "aborting...
    ", drv->name);
    19         return -EBUSY;
    20     }
    21 
    22     ret = bus_add_driver(drv);                  //   在总线挂接设备驱动  就是将设备驱动对应的kobj对象与组织建立关系
    23     if (ret)
    24         return ret;
    25     ret = driver_add_groups(drv, drv->groups);   //  
    26     if (ret)
    27         bus_remove_driver(drv);
    28     return ret;
    29 }

    bus_add_driver:

     1 int bus_add_driver(struct device_driver *drv)
     2 {
     3     struct bus_type *bus;             //  定义一个bus_type 结构体指针
     4     struct driver_private *priv;      //   定义一个 driver_private  指针
     5     int error = 0;
     6 
     7     bus = bus_get(drv->bus);       //   获取 drv的bus
     8     if (!bus)
     9         return -EINVAL;
    10 
    11     pr_debug("bus: '%s': add driver %s
    ", bus->name, drv->name);
    12 
    13     priv = kzalloc(sizeof(*priv), GFP_KERNEL);  //  给priv 申请分配内存空间
    14     if (!priv) {
    15         error = -ENOMEM;
    16         goto out_put_bus;
    17     }
    18     klist_init(&priv->klist_devices, NULL, NULL);  //  初始化 priv->klist_devices 链表
    19     priv->driver = drv;                            //  使用 priv->driver  指向 drv
    20     drv->p = priv;                                 //   使用drv->p 指向 priv    这两步见多了  ,跟之前分析的是一样的意思  就是建立关系
    21     priv->kobj.kset = bus->p->drivers_kset;        //   设置设备驱动对象的父对象(  也就是指向一个 kset )    父对象就是   /sys/bus/bus_type/drivers/  这个目录对应的对象
    22     error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, //  添加kobject 对象到目录层次中     就能够在  /sys/bus/bus_type/drivers/ 目录中看到设备驱动对应的文件了
    23                      "%s", drv->name);                             //  priv->kobj->ktype =  driver_ktype     对象类型
    24     if (error)
    25         goto out_unregister;
    26 
    27     if (drv->bus->p->drivers_autoprobe) {       //  如果定义了自动匹配设备标志位    则在线下面进行自动匹配
    28         error = driver_attach(drv);             //  尝试将驱动绑定到设备 也就是通过这个函数进行设备与设备驱动的匹配
    29         if (error)
    30             goto out_unregister;
    31     }
    32     klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);  //  链表挂接:   priv->knode_bus  挂接到  bus->p->klist_drivers 链表头上去
    33     module_add_driver(drv->owner, drv);
    34 
    35     error = driver_create_file(drv, &driver_attr_uevent);   //  建立属性文件:   uevent
    36     if (error) {
    37         printk(KERN_ERR "%s: uevent attr (%s) failed
    ",
    38             __func__, drv->name);
    39     }
    40     error = driver_add_attrs(bus, drv);                    //  根据总线的   bus->drv_attrs  来建立属性文件
    41     if (error) {
    42         /* How the hell do we get out of this pickle? Give up */
    43         printk(KERN_ERR "%s: driver_add_attrs(%s) failed
    ",
    44             __func__, drv->name);
    45     }
    46 
    47     if (!drv->suppress_bind_attrs) {
    48         error = add_bind_files(drv);
    49         if (error) {
    50             /* Ditto */
    51             printk(KERN_ERR "%s: add_bind_files(%s) failed
    ",
    52                 __func__, drv->name);
    53         }
    54     }
    55 
    56     kobject_uevent(&priv->kobj, KOBJ_ADD);
    57     return 0;
    58 
    59 out_unregister:
    60     kobject_put(&priv->kobj);
    61     kfree(drv->p);
    62     drv->p = NULL;
    63 out_put_bus:
    64     bus_put(bus);
    65     return error;
    66 }

    driver_attach:

      1 int driver_attach(struct device_driver *drv)
      2 {
      3     return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);   //  这个函数的功能就是:   依次去匹配bus总线下的各个设备
      4 }
      5 

    6 7 int bus_for_each_dev(struct bus_type *bus, struct device *start, 8 void *data, int (*fn)(struct device *, void *)) 9 { 10 struct klist_iter i; // 定义一个klist_iter 结构体变量 包含: struct klist 和 struct klist_node 11 struct device *dev; 12 int error = 0; 13 14 if (!bus) 15 return -EINVAL; 16 17 klist_iter_init_node(&bus->p->klist_devices, &i, // 这个函数的功能就是将 klist_devices 和 knode_bus填充到 i 变量中 18 (start ? &start->p->knode_bus : NULL)); 19 while ((dev = next_device(&i)) && !error) //  依次返回出总线上的各个设备结构体device 20 error = fn(dev, data); // 对于每一个设备和设备驱动都调用fn这个函数 直道成功 或者全部都匹配不上 21 klist_iter_exit(&i); 22 return error; 23 } 24 25

    26 27 static int __driver_attach(struct device *dev, void *data) 28 { 29 struct device_driver *drv = data; // 定义一个device_driver 指针 30 31 /* 32 * Lock device and try to bind to it. We drop the error 33 * here and always return 0, because we need to keep trying 34 * to bind to devices and some drivers will return an error 35 * simply if it didn't support the device. 36 * 37 * driver_probe_device() will spit a warning if there 38 * is an error. 39 */ 40 41 if (!driver_match_device(drv, dev)) // 通过这个函数进行匹配 调用总线下的match 函数 42 return 0; 43 44 if (dev->parent) /* Needed for USB */ 45 device_lock(dev->parent); 46 device_lock(dev); 47 if (!dev->driver) 48 driver_probe_device(drv, dev); // 调用probe函数 49 device_unlock(dev); 50 if (dev->parent) 51 device_unlock(dev->parent); 52 53 return 0; 54 } 55 56

    57 58 int driver_probe_device(struct device_driver *drv, struct device *dev) 59 { 60 int ret = 0; 61 62 if (!device_is_registered(dev)) // 判断这个设备是否已经注册了 63 return -ENODEV; 64 65 pr_debug("bus: '%s': %s: matched device %s with driver %s ", 66 drv->bus->name, __func__, dev_name(dev), drv->name); 67 68 pm_runtime_get_noresume(dev); 69 pm_runtime_barrier(dev); 70 ret = really_probe(dev, drv); // 在这个函数中就会调用设备驱动或者是总线下的 probe 函数 71 pm_runtime_put_sync(dev); 72 73 return ret; 74 } 75

    76 77 static int really_probe(struct device *dev, struct device_driver *drv) 78 { 79 int ret = 0; 80 81 atomic_inc(&probe_count); 82 pr_debug("bus: '%s': %s: probing driver %s with device %s ", 83 drv->bus->name, __func__, drv->name, dev_name(dev)); 84 WARN_ON(!list_empty(&dev->devres_head)); 85 86 dev->driver = drv; // 使用 dev->driver 指针去指向 drv 这就使得这两者建立了一种关系 87 if (driver_sysfs_add(dev)) { 88 printk(KERN_ERR "%s: driver_sysfs_add(%s) failed ", 89 __func__, dev_name(dev)); 90 goto probe_failed; 91 } 92 93 if (dev->bus->probe) { // 如果总线下的probe函数存在 则调用优先调用这个函数 94 ret = dev->bus->probe(dev); 95 if (ret) 96 goto probe_failed; 97 } else if (drv->probe) { // 否则调用设备驱动中的probe函数 98 ret = drv->probe(dev); // 所以由此可知: 总线中的probe函数具有更高的优先级 99 if (ret) 100 goto probe_failed; 101 } 102 103 driver_bound(dev); 104 ret = 1; 105 pr_debug("bus: '%s': %s: bound device %s to driver %s ", 106 drv->bus->name, __func__, dev_name(dev), drv->name); 107 goto done; 108 109 probe_failed: 110 devres_release_all(dev); 111 driver_sysfs_remove(dev); 112 dev->driver = NULL; 113 114 if (ret != -ENODEV && ret != -ENXIO) { 115 /* driver matched but the probe failed */ 116 printk(KERN_WARNING 117 "%s: probe of %s failed with error %d ", 118 drv->name, dev_name(dev), ret); 119 } 120 /* 121 * Ignore errors returned by ->probe so that the next driver can try 122 * its luck. 123 */ 124 ret = 0; 125 done: 126 atomic_dec(&probe_count); 127 wake_up(&probe_waitqueue); 128 return ret; 129 }

    上面说到了当注册platform平台设备驱动时会进行自动匹配的原理,那么当我们注册platform平台设备时进行自动匹配的代码在哪里呢?

    其实这个之前在分析device_create函数时就已经分析过了,只不过没有去详细的分析:

    /**********************************************/

    platform_device_add

        device_add

            bus_probe_device     //  关键就在这个函数

    /*********************************************/

    函数分析:

     1 void bus_probe_device(struct device *dev)
     2 {
     3     struct bus_type *bus = dev->bus;             //  获取设备中的总线类型   bus_type
     4     int ret;
     5 
     6     if (bus && bus->p->drivers_autoprobe) {       //  如果总线存在  并且  设置了自动进行设备与设备驱动匹配标志位
     7         ret = device_attach(dev);                           //  则调用这个函数进行匹配
     8         WARN_ON(ret < 0);
     9     }
    10 }
    11 
    12 
    13 
    14 
    15 int device_attach(struct device *dev)
    16 {
    17     int ret = 0;
    18 
    19     device_lock(dev);
    20     if (dev->driver) {    //    如果我们的设备早就绑定了设备驱动     那么执行下面的
    21         ret = device_bind_driver(dev);
    22         if (ret == 0)
    23             ret = 1;
    24         else {
    25             dev->driver = NULL;
    26             ret = 0;
    27         }
    28     } else {     //   我们就分析这条
    29         pm_runtime_get_noresume(dev);
    30         ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);   //  遍历总线的链表匹配对应的设备驱动
    31         pm_runtime_put_sync(dev);
    32     }
    33     device_unlock(dev);
    34     return ret;
    35 }
    36 //  到这里之后就和上面的其实是一样的了

    总结:  所以由此可知,当我们不管是先注册设备还是先注册设备驱动都会进行一次设备与设备驱动的匹配过程,匹配成功之后就会调用probe函数,

    匹配的原理就是去遍历总线下的相应的链表来找到挂接在他下面的设备或者设备驱动,所以由此可以看出来,这个东西的设计其实是很美的。

    6、platform总线下的匹配函数

    (1)platform_match函数

     1 static int platform_match(struct device *dev, struct device_driver *drv) // 总线下的设备与设备驱动的匹配函数
     2 {
     3     struct platform_device *pdev = to_platform_device(dev);      //  通过device  变量获取到 platform_device 
     4     struct platform_driver *pdrv = to_platform_driver(drv);      //   通过 driver 获取  platform_driver
     5 
     6     /* match against the id table first */
     7     if (pdrv->id_table)    //   如果pdrv中的id_table 表存在
     8         return platform_match_id(pdrv->id_table, pdev) != NULL;  //  匹配id_table
     9  
    10     /* fall-back to driver name match */   //  第二个就是指直接匹配 pdev->name     drv->name  名字是否形同
    11     return (strcmp(pdev->name, drv->name) == 0);
    12 }
    13 
    14 
    15 
    16 
    17 static const struct platform_device_id *platform_match_id(
    18             const struct platform_device_id *id,
    19             struct platform_device *pdev)
    20 {
    21     while (id->name[0]) {  //  循环去比较id_table数组中的各个id名字是否与pdev->name 相同
    22         if (strcmp(pdev->name, id->name) == 0) {
    23             pdev->id_entry = id;       // 将id_table数组中的名字匹配上的 这个数组项 指针赋值给 pdev->id_entry
    24             return id;         //  返回这个指针
    25         }
    26         id++;
    27     }
    28     return NULL;
    29 }

    总结: 由上面可知platform总线下设备与设备驱动的匹配原理就是通过名字进行匹配的,先去匹配platform_driver中的id_table表中的各个名字与platform_device->name

    名字是否相同,如果相同表示匹配成功直接返回,否则直接匹配platform_driver->name与platform_driver->name是否相同,相同则匹配成功,否则失败。

    参考:《朱友鹏嵌入式Linux开发5.Linux驱动开发5.5.linux设备驱动模型》

  • 相关阅读:
    mysql 触发器
    Yii 1.0 基础
    python解释执行原理(转载)
    python中使用selenium调用Firefox缺少geckodriver解决方法
    Python中os和shutil模块实用方法集锦
    pytesseract使用
    anaconda安装第三方库
    anaconda spyder异常如何重新启动
    windows下python3.6 32bit 安装django
    设置SO_RECVBUF和SO_SENDBUF套接字选项
  • 原文地址:https://www.cnblogs.com/deng-tao/p/6026373.html
Copyright © 2011-2022 走看看