/********************************************************/
內核版本: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設備驅動模型》
