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總線相關代碼:driver\base\platform.c 文件

相關結構體定義:include\linux\platform_device.h 文件中

 

2、platform總線管理下的2員大將

(1)兩個結構體platform_device和platform_driver

對於任何一種Linux設備驅動模型下的總線都由兩個部分組成:描述設備相關的結構體和描述驅動相關的結構體

在platform總線下就是platform_device和platform_driver,下面是對兩個結構體的各個元素進行分析:

platform_device結構體:(include\linux\platform_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結構體:(include\linux\platform_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)兩組接口函數(driver\base\platform.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\n", 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\n",
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\n",
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\n", 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...\n", 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\n", 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\n",
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\n",
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\n",
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\n", 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\n", 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\n", 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\n", 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\n", 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設備驅動模型》

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM