Linux內核在2.6版本中引入設備驅動模型,簡化了驅動程序的編寫。Linux設備驅動模型包含設備(device)、總線(bus)、類(class)和驅動(driver),它們之間相互關聯。其中設備(device)和驅動(driver)經過總線(bus)綁定在一塊兒。node
Linux內核中,分別用 bus_type、 device_driver和 device結構來描述總線、驅動和設備,結構體定義詳見 linux/device.h。設備和對應的驅動必須依附於同一種總線,所以 device_driver和 device結構中都包含 struct bus_type指針。linux
Linux sysfs 是一個虛擬的文件系統,它把鏈接在系統上的設備和總線組織成爲一個分級的文件,能夠由用戶空間存取,向用戶空間導出內核數據結構以及它們的屬性。git
sysfs展現出設備驅動模型中各個組件的層次關係,某個系統上的sysfs頂層目錄展現以下:github
1 /sys$ ll 2 total 0 3 drwxr-xr-x 2 root root 0 Aug 20 15:27 block/ 4 drwxr-xr-x 29 root root 0 Aug 20 15:27 bus/ 5 drwxr-xr-x 61 root root 0 Aug 20 15:27 class/ 6 drwxr-xr-x 4 root root 0 Aug 20 15:27 dev/ 7 drwxr-xr-x 14 root root 0 Aug 20 15:27 devices/ 8 drwxr-xr-x 4 root root 0 Aug 20 15:27 firmware/ 9 drwxr-xr-x 8 root root 0 Aug 20 15:27 fs/ 10 drwxr-xr-x 2 root root 0 Sep 2 17:08 hypervisor/ 11 drwxr-xr-x 8 root root 0 Aug 20 15:27 kernel/ 12 drwxr-xr-x 147 root root 0 Aug 20 15:27 module/ 13 drwxr-xr-x 2 root root 0 Aug 20 15:27 power/
重要子目錄介紹:數據結構
sysfs中顯示的每個對象都對應一個 kobject結構(完整定義位於 linux/kobject.h ,結構內部包含一個 parent 指針),而另外一個相聯繫的結構爲 kset 。 kset 是嵌入相同類型結構的 kobject 對象的集合。 內核用 kobject 、 kset 和 parent 之間的關係將各個對象鏈接起來組成一個分層的結構體系,從而與模型化的子系統相匹配。(有機會詳細介紹)併發
sysfs中能清晰地看出 device 、 driver 和 bus 的相互聯繫,以某系統上PCI總線上的igb驅動爲例。 /sys/bus/pci/ 下存在 devices 和 drivers 兩個目錄,分別包含了依附於PCI總線上的設備和驅動。進入igb驅動目錄,能夠發現存在指向設備的連接。app
1 /sys/bus/pci/drivers/igb$ ll 2 total 0 3 ... 0 Sep 2 17:08 0000:07:00.0 -> ../../../../devices/pci0000:00/0000:00:1c.4/0000:07:00.0/ 4 ... 0 Sep 2 17:08 0000:07:00.1 -> ../../../../devices/pci0000:00/0000:00:1c.4/0000:07:00.1/ 5 ...
對應地,在 /sys/devices/ 目錄下,能夠看到設備存在一個指向igb的 driver 項:async
1 /sys/devices/pci0000:00/0000:00:1c.4/0000:07:00.0$ ll 2 total 0 3 ... 4 lrwxrwxrwx 1 root root 0 Aug 20 15:27 driver -> ../../../../bus/pci/drivers/igb/ 5 ...
一樣地, /sys/bus/pci/devices 目錄下能夠找到指向一樣設備的一個連接:ide
1 /sys/bus/pci/devices$ ll 2 total 0 3 ... 4 ... 0 Aug 20 15:27 0000:07:00.0 -> ../../../devices/pci0000:00/0000:00:1c.4/0000:07:00.0/ 5 ... 0 Aug 20 15:27 0000:07:00.1 -> ../../../devices/pci0000:00/0000:00:1c.4/0000:07:00.1/ 6 ...
對於早期的Linux內核(2.6版本之前)來講,一般在驅動代碼中xxx_driver
註冊過程當中調用 probe() 函數來對設備進行初始化。函數
引入Linux設備驅動模型下,設備和驅動能夠分開註冊,依賴總線完成相互綁定。系統每註冊一個設備的時候,會尋找與之匹配的驅動;相反,系統每註冊一個驅動的時候,會尋找與之匹配的設備。這個過程當中,設備和驅動的匹配工做由總線完成。
下文中將會用關鍵的內核源碼(基於Linux 5.2.14 Kernel)說明驅動和設備間匹配機制的實現,分析的過程當中以platform總線爲例。【若是隻想了解大體過程,可提早轉至最後一節:總結】
platform總線是一種虛擬的總線,與之相對應的是PCI、I2C、SPI等實體總線。引入虛擬platform總線是爲了解決某些設備沒法直接依附在現有實體總線上的問題,例如SoC系統中集成的獨立外設控制器,掛接在SoC內存空間的外設等等。
platform總線做爲Linux的基礎總線,在內核啓動階段便完成了註冊,註冊的入口函數爲 platform_bus_init() 。內核啓動階段調用該函數的路徑爲:
1 start_kernel() --> arch_call_rest_init()[last step in start_kernel] 2 --> rest_init() --> kernel_init() 3 --> kernel_init_freeable() --> do_basic_setup() 4 --> driver_init() --> platform_bus_init()
Linux內核中定義了 platform_bus_type 結構體來描述platform總線,同時也定義了設備 platform_bus ,用於管理全部掛載在platform總線下的設備,定義以下:
1 struct bus_type platform_bus_type = { 2 .name = "platform", 3 .dev_groups = platform_dev_groups, 4 .match = platform_match, 5 .uevent = platform_uevent, 6 .dma_configure = platform_dma_configure, 7 .pm = &platform_dev_pm_ops, 8 }; 9 10 struct device platform_bus = { 11 .init_name = "platform", 12 };
platform_bus_init()對 platform 總線的註冊主要分爲兩步:
device_register(&platform_bus)
bus_register(&platform_bus_type)
1 int __init platform_bus_init(void) 2 { 3 int error; 4 5 /* Clear up early_platform_device_list, then only remain head_list */ 6 early_platform_cleanup(); 7 8 /* register platform_bus device (platform_bus is also regarded as a device) */ 9 error = device_register(&platform_bus); 10 if (error) { 11 put_device(&platform_bus); 12 return error; 13 } 14 /* Main process to register platform_bus */ 15 error = bus_register(&platform_bus_type); 16 if (error) 17 device_unregister(&platform_bus); 18 of_platform_register_reconfig_notifier(); 19 return error; 20 }
1 /***** drivers/base/core.c *****/ 2 int device_register(struct device *dev) 3 { 4 device_initialize(dev); // init device structure 5 return device_add(dev); // add device to device hierarchy 6 }
1 /***** drivers/base/bus.c *****/ 2 int bus_register(struct bus_type *bus) 3 { 4 struct subsys_private *priv; 5 struct lock_class_key *key = &bus->lock_key; 6 7 priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); 8 9 priv->bus = bus; 10 bus->p = priv; 11 12 BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); 13 14 retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); 15 if (retval) 16 goto out; 17 18 priv->subsys.kobj.kset = bus_kset; 19 priv->subsys.kobj.ktype = &bus_ktype; 20 priv->drivers_autoprobe = 1; 21 22 /* Register kset (subsys) */ 23 retval = kset_register(&priv->subsys); 24 25 retval = bus_create_file(bus, &bus_attr_uevent); 26 27 /* Setup "devices" and "drivers" subfolder under "platform" */ 28 priv->devices_kset = kset_create_and_add("devices", NULL, 29 &priv->subsys.kobj); 30 31 priv->drivers_kset = kset_create_and_add("drivers", NULL, 32 &priv->subsys.kobj); 33 34 INIT_LIST_HEAD(&priv->interfaces); 35 __mutex_init(&priv->mutex, "subsys mutex", key); 36 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); 37 klist_init(&priv->klist_drivers, NULL, NULL); 38 39 /* bus_create_file(bus, &bus_attr_drivers_probe); BUS_ATTR_WO(drivers_probe) 40 * bus_create_file(bus, &bus_attr_drivers_autoprobe); BUS_ATTR_RW(drivers_autoprobe) 41 * Add two attribute files for current bus /sys/bus/platform 42 */ 43 retval = add_probe_files(bus); 44 45 retval = bus_add_groups(bus, bus->bus_groups); 46 47 return 0; 48 }
bus_register(&platform_bus_type) 將總線 platform 註冊到 Linux 的總線系統中,主要完成了 subsystem 的註冊,對 struct subsys_private 結構進行了初始化,具體包括:
註冊完成後 platform_bus_type 結構重要的成員列舉以下:
1 struct bus_type platform_bus_type = { 2 .name = "platform", 3 .dev_groups = platform_dev_groups, 4 .match = platform_match, 5 .uevent = platform_uevent, 6 .dma_configure = platform_dma_configure, 7 .pm = &platform_dev_pm_ops, 8 .p (struct subsys_private) = { 9 .bus = &platform_bus_type, 10 .subsys (struct kset) = { 11 .kobj = { 12 .name = 「platform」 13 .kref->refcount->refs = 1, // kset_init() 14 INIT_LIST_HEAD(.entry), 15 .state_in_sysfs = 0, 16 .state_add_uevent_sent = 1, // kset_register() 17 .state_remove_uevent_sent = 0, 18 .state_initialized = 1, 19 .kset = bus_kset, // attached to /sys/bus/ 20 .ktype= bus_ktype, 21 22 .parent = bus_kset->kobj, 23 .sd (kernfs_node) = { // create_dir, kobject_add_internal 24 .parent = bus_kset->kobj->sd, 25 .dir.root = bus_kset->kobj->sd->dir.root, 26 .ns = NULL, 27 .priv = .kobj 28 } 29 } 30 INIT_LIST_HEAD(&k->list); 31 spin_lock_init(&k->list_lock); 32 } 33 /* key point for driver to autoprobe device, set in bus_register() */ 34 . drivers_autoprobe = 1 35 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); 36 klist_init(&priv->klist_drivers, NULL, NULL); 37 .devices_kset = kset_create_and_add("devices", NULL, &.p->subsys.kobj); 38 /* .drivers_kset = kset_create_and_add("drivers", NULL, &.p->subsys.kobj) */ 39 .drivers_kset = { 40 .kobj = { 41 .name = 「drivers」, 42 .parent = &.subsys.kobj, 43 .ktype = &kset_ktype, 44 .kset = NULL, 45 46 .kref->refcount->refs = 1, // kset_init 47 INIT_LIST_HEAD(.entry), 48 .state_in_sysfs = 0, 49 .state_add_uevent_sent = 1, // kset_register 50 .state_remove_uevent_sent = 0, 51 .state_initialized = 1, 52 53 .sd = { // create_dir: /sys/bus/platform/drivers 54 /* kobject_add_internal */ 55 .parent = &.subsys.kobj.sd, 56 .dir.root = = &.subsys.kobj.sd->dir.root 57 .ns = NULL, 58 .priv = .kobj 59 } 60 } 61 INIT_LIST_HEAD(.list); 62 spin_lock_init(.list_lock); 63 .uevent_ops = NULL, 64 } 65 } 66 };
Linux內核中對依賴於platform總線的驅動定義了 platform_driver 結構體,內部封裝了前述的 struct device_driver 。
1 struct platform_driver { 2 int (*probe)(struct platform_device *); 3 int (*remove)(struct platform_device *); 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; 8 const struct platform_device_id *id_table; 9 bool prevent_deferred_probe; 10 };
爲了更好地說明 platform 驅動的註冊過程,以驅動 globalfifo_driver 爲實例 (完整代碼), globalfifo_driver 結構成員定義以下:
1 static struct platform_driver globalfifo_driver = { 2 .driver = { 3 .name = "globalfifo_platform", 4 .owner = THIS_MODULE, 5 }, 6 .probe = globalfifo_probe, 7 .remove = globalfifo_remove, 8 };
globalfifo_driver 註冊的入口函數爲 platform_driver_register(&globalfifo_driver) ,具體實現爲 __platform_driver_register(&globalfifo_driver, THIS_MODULE) 。
該函數會對 struct device_driver 的bus、probe、remove等回調函數進行初始化,緊接着調用 driver_register(&globalfifo_driver->driver) 。
1 /***** drivers/base/platform.c *****/ 2 /** 3 * __platform_driver_register - register a driver for platform-level devices 4 * @drv: platform driver structure 5 * @owner: owning module/driver 6 */ 7 int __platform_driver_register(struct platform_driver *drv, 8 struct module *owner) 9 { 10 drv->driver.owner = owner; 11 drv->driver.bus = &platform_bus_type; 12 drv->driver.probe = platform_drv_probe; 13 drv->driver.remove = platform_drv_remove; 14 drv->driver.shutdown = platform_drv_shutdown; 15 16 return driver_register(&drv->driver); 17 }
1 /***** drivers/base/driver.c *****/ 2 /** 3 * driver_register - register driver with bus 4 * @drv: driver to register 5 * 6 * We pass off most of the work to the bus_add_driver() call, 7 * since most of the things we have to do deal with the bus 8 * structures. 9 */ 10 int driver_register(struct device_driver *drv) 11 { 12 int ret; 13 struct device_driver *other; 14 15 if (!drv->bus->p) { 16 pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n", 17 drv->name, drv->bus->name); 18 return -EINVAL; 19 } 20 21 if ((drv->bus->probe && drv->probe) || 22 (drv->bus->remove && drv->remove) || 23 (drv->bus->shutdown && drv->shutdown)) 24 printk(KERN_WARNING "Driver '%s' needs updating - please use " 25 "bus_type methods\n", drv->name); 26 27 other = driver_find(drv->name, drv->bus); 28 29 ret = bus_add_driver(drv); 30 31 ret = driver_add_groups(drv, drv->groups); 32 33 kobject_uevent(&drv->p->kobj, KOBJ_ADD); 34 35 return ret; 36 }
driver_register(&(globalfifo_driver.driver)) 主要的工做包括:
1 /***** drivers/base/bus.c *****/ 2 /** 3 * bus_add_driver - Add a driver to the bus. 4 * @drv: driver. 5 */ 6 int bus_add_driver(struct device_driver *drv) 7 { 8 struct bus_type *bus; 9 struct driver_private *priv; 10 int error = 0; 11 12 bus = bus_get(drv->bus); 13 14 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 15 16 klist_init(&priv->klist_devices, NULL, NULL); 17 priv->driver = drv; 18 drv->p = priv; 19 priv->kobj.kset = bus->p->drivers_kset; 20 error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, 21 "%s", drv->name); 22 23 klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 24 25 /* Entrance to match device: try to bind driver to devices */ 26 if (drv->bus->p->drivers_autoprobe) { 27 error = driver_attach(drv); 28 } 29 module_add_driver(drv->owner, drv); 30 31 error = driver_create_file(drv, &driver_attr_uevent); 32 error = driver_add_groups(drv, bus->drv_groups); 33 34 if (!drv->suppress_bind_attrs) { 35 error = add_bind_files(drv); 36 } 37 return 0; 38 }
bus_add_driver(&(globalfifo_driver.driver)) 的主要工做包括:
初始化後 globalfifo_driver 結構主要的成員列舉以下:
1 static struct platform_driver globalfifo_driver = { 2 .driver = { 3 .name = "globalfifo_platform", 4 .owner = THIS_MODULE, 5 .bus = &platform_bus_type, 6 .probe = platform_drv_probe 7 .remove = platform_drv_remove, 8 .shutdown = platform_drv_shutdown, 9 .p (struct driver_private) = { 10 .driver = & globalfifo_driver.driver, 11 klist_init(&.klist_devices, NULL, NULL); 12 klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 13 14 .kobj = { 15 .kset = platform_bus_type->p->drivers_kset, 16 .ktype = driver_ktype, 17 18 .kref->refcount->refs = 1, // kset_init 19 INIT_LIST_HEAD(.entry), 20 .state_in_sysfs = 1, 21 .state_add_uevent_sent = 0, 22 .state_remove_uevent_sent = 0, 23 .state_initialized = 1, 24 .name = "globalfifo_platform", 25 .parent = platform_bus_type->p->drivers_kset->kobj, 26 } 27 } 28 }, 29 .probe = globalfifo_probe, 30 .remove = globalfifo_remove, 31 };
1 /***** drivers/base/dd.c *****/ 2 /** 3 * driver_attach - try to bind driver to devices. 4 * @drv: driver. 5 */ 6 int driver_attach(struct device_driver *drv) 7 { 8 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); 9 } 10 11 /***** drivers/base/bus.c *****/ 12 /** 13 * bus_for_each_dev - device iterator. 14 * @bus: bus type. 15 * @start: device to start iterating from. 16 * @data: data for the callback. 17 * @fn: function to be called for each device. 18 */ 19 int bus_for_each_dev(struct bus_type *bus, struct device *start, 20 void *data, int (*fn)(struct device *, void *)) 21 { 22 struct klist_iter i; 23 struct device *dev; 24 int error = 0; 25 26 if (!bus || !bus->p) 27 return -EINVAL; 28 29 klist_iter_init_node(&bus->p->klist_devices, &i, 30 (start ? &start->p->knode_bus : NULL)); 31 while (!error && (dev = next_device(&i))) 32 error = fn(dev, data); 33 klist_iter_exit(&i); 34 return error; 35 }
driver_attach() 函數找到驅動依附的總線信息,遍歷總線上鏈表 klist_devices 獲得當前總線上存在的設備,而後調用 __driver_attach(dev, drv) 函數,嘗試將驅動和設備綁定。
__driver_attach(dev, drv) 函數包含兩個主要的部分:
1 static int __driver_attach(struct device *dev, void *data) 2 { 3 struct device_driver *drv = data; 4 int ret; 5 6 /* 7 * Lock device and try to bind to it. We drop the error 8 * here and always return 0, because we need to keep trying 9 * to bind to devices and some drivers will return an error 10 * simply if it didn't support the device. 11 * 12 * driver_probe_device() will spit a warning if there 13 * is an error. 14 */ 15 ret = driver_match_device(drv, dev); 16 if (ret == 0) { 17 /* no match */ 18 return 0; 19 } else if (ret == -EPROBE_DEFER) { 20 dev_dbg(dev, "Device match requests probe deferral\n"); 21 driver_deferred_probe_add(dev); 22 } else if (ret < 0) { 23 dev_dbg(dev, "Bus failed to match device: %d", ret); 24 return ret; 25 } /* ret > 0 means positive match */ 26 27 ... ... 28 29 device_driver_attach(drv, dev); 30 return 0; 31 }
1 static inline int driver_match_device(struct device_driver *drv, 2 struct device *dev) 3 { 4 return drv->bus->match ? drv->bus->match(dev, drv) : 1; 5 }
driver_match_device(drv, dev) 回調 drv->bus->match() 函數,對於platform_bus爲 platform_match() 。
platform_match() 函數會依次嘗試以下幾種方式:
1 /** 2 * platform_match - bind platform device to platform driver. 3 * @dev: device. 4 * @drv: driver. 5 */ 6 static int platform_match(struct device *dev, struct device_driver *drv) 7 { 8 struct platform_device *pdev = to_platform_device(dev); 9 struct platform_driver *pdrv = to_platform_driver(drv); 10 11 /* When driver_override is set, only bind to the matching driver */ 12 if (pdev->driver_override) 13 return !strcmp(pdev->driver_override, drv->name); 14 15 /* Attempt an OF style match first */ 16 if (of_driver_match_device(dev, drv)) 17 return 1; 18 19 /* Then try ACPI style match */ 20 if (acpi_driver_match_device(dev, drv)) 21 return 1; 22 23 /* Then try to match against the id table */ 24 if (pdrv->id_table) 25 return platform_match_id(pdrv->id_table, pdev) != NULL; 26 27 /* fall-back to driver name match */ 28 return (strcmp(pdev->name, drv->name) == 0); 29 }
1 /** 2 * device_driver_attach - attach a specific driver to a specific device 3 * @drv: Driver to attach 4 * @dev: Device to attach it to 5 */ 6 int device_driver_attach(struct device_driver *drv, struct device *dev) 7 { 8 int ret = 0; 9 10 __device_driver_lock(dev, dev->parent); 11 12 /* 13 * If device has been removed or someone has already successfully 14 * bound a driver before us just skip the driver probe call. 15 */ 16 if (!dev->p->dead && !dev->driver) 17 ret = driver_probe_device(drv, dev); 18 19 __device_driver_unlock(dev, dev->parent); 20 21 return ret; 22 }
在驅動和設備匹配成功以後,便將驅動和設備進行綁定。調用 driver_probe_device(drv, dev) 完成此工做,進一步調用 really_probe(dev, drv) 。
1 /** 2 * driver_probe_device - attempt to bind device & driver together 3 * @drv: driver to bind a device to 4 * @dev: device to try to bind to the driver 5 */ 6 int driver_probe_device(struct device_driver *drv, struct device *dev) 7 { 8 int ret = 0; 9 10 if (!device_is_registered(dev)) 11 return -ENODEV; 12 13 ... ... 14 if (initcall_debug) 15 ret = really_probe_debug(dev, drv); 16 else 17 ret = really_probe(dev, drv); 18 ... ... 19 return ret; 20 }
1 /***** drivers/base/dd.c *****/ 2 static int really_probe(struct device *dev, struct device_driver *drv) 3 { 4 dev->driver = drv; 5 ... ... 6 driver_sysfs_add(dev); 7 ... ... 8 /* Routine to probe device */ 9 if (dev->bus->probe) { 10 ret = dev->bus->probe(dev); 11 if (ret) 12 goto probe_failed; 13 } else if (drv->probe) { 14 ret = drv->probe(dev); 15 if (ret) 16 goto probe_failed; 17 } 18 ... ... 19 driver_bound(dev); 20 }
really_probe(dev, drv) 主要完成的工做包括:
最後,對設備的註冊過程進行簡要梳理。
和驅動相似,Linux內核中對依賴於platform總線的設備也定義了特有的結構: platform_device ,內部封裝了 struct device 結構。
1 struct platform_device { 2 const char *name; 3 int id; 4 bool id_auto; 5 struct device dev; 6 u32 num_resources; 7 struct resource *resource; 8 9 const struct platform_device_id *id_entry; 10 char *driver_override; /* Driver name to force a match */ 11 12 /* MFD cell pointer */ 13 struct mfd_cell *mfd_cell; 14 15 /* arch specific additions */ 16 struct pdev_archdata archdata; 17 };
與 globalfifo_driver 相對應,一樣定義 globalfifo_device 結構體(完整代碼),成員定義以下:
1 static struct platform_device globalfifo_device = { 2 .name = "globalfifo_platform", 3 .id = -1, 4 };
對設備 globalfifo_device 進行註冊的入口函數爲 platform_device_register(&globalfifo_device) 。
1 int platform_device_register(struct platform_device *pdev) 2 { 3 device_initialize(&pdev->dev); 4 arch_setup_pdev_archdata(pdev); 5 return platform_device_add(pdev); 6 }
其中 device_initialize(&pdev->dev) 在第一節platform_bus註冊中也提到過,主要對 struct device 中基本成員進行初始化,包括 kobject 、 struct device_private 、 struct mutex 等。着重介紹 platform_device_add(pdev) 。
1 int platform_device_add(struct platform_device *pdev) 2 { 3 int i, ret; 4 5 if (!pdev->dev.parent) 6 pdev->dev.parent = &platform_bus; 7 8 pdev->dev.bus = &platform_bus_type; 9 10 switch (pdev->id) { 11 default: 12 dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); 13 break; 14 case PLATFORM_DEVID_NONE: 15 dev_set_name(&pdev->dev, "%s", pdev->name); 16 break; 17 case PLATFORM_DEVID_AUTO: 18 /* 19 * Automatically allocated device ID. We mark it as such so 20 * that we remember it must be freed, and we append a suffix 21 * to avoid namespace collision with explicit IDs. 22 */ 23 ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL); 24 pdev->id = ret; 25 pdev->id_auto = true; 26 dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id); 27 break; 28 } 29 for (i = 0; i < pdev->num_resources; i++) { 30 struct resource *p, *r = &pdev->resource[i]; 31 if (r->name == NULL) 32 r->name = dev_name(&pdev->dev); 33 34 p = r->parent; 35 if (!p) { 36 if (resource_type(r) == IORESOURCE_MEM) 37 p = &iomem_resource; 38 else if (resource_type(r) == IORESOURCE_IO) 39 p = &ioport_resource; 40 } 41 42 if (p) { 43 ret = insert_resource(p, r); 44 } 45 } 46 47 ret = device_add(&pdev->dev); 48 if (ret == 0) 49 return ret; 50 ... ... 51 }
platform_device_add(&globalfifo_device) 主要工做以下:
1 int device_add(struct device *dev) 2 { 3 struct device *parent; 4 struct kobject *kobj; 5 int error = -EINVAL; 6 7 /* This will incr the ref_count */ 8 dev = get_device(dev); 9 10 /* Init dev->p->device = dev */ 11 if (!dev->p) 12 error = device_private_init(dev); 13 14 /* if init_name exists, use it to initialize dev.kobj->name */ 15 if (dev->init_name) { 16 dev_set_name(dev, "%s", dev->init_name); 17 dev->init_name = NULL; 18 } 19 20 /* subsystems can specify simple device enumeration */ 21 if (!dev_name(dev) && dev->bus && dev->bus->dev_name) 22 dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); 23 24 /* Return ERROR if dev name is not specified */ 25 if (!dev_name(dev)) { 26 error = -EINVAL; 27 goto name_error; 28 } 29 30 ... ... 31 parent = get_device(dev->parent); 32 /* get_device_parent(dev, parent) --> platform_bus.kobj */ 33 kobj = get_device_parent(dev, parent); 34 if (kobj) 35 dev->kobj.parent = kobj; 36 37 ... ... 38 /* first, register with generic layer. */ 39 /* we require the name to be set before, and pass NULL */ 40 error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); 41 42 /* notify platform of device entry */ 43 error = device_platform_notify(dev, KOBJ_ADD); 44 45 error = device_create_file(dev, &dev_attr_uevent); 46 47 error = device_add_class_symlinks(dev); 48 error = device_add_attrs(dev); 49 50 /* Main Entrance to add device into existing bus */ 51 error = bus_add_device(dev); 52 53 error = dpm_sysfs_add(dev); 54 device_pm_add(dev); 55 56 /* Create related node in devfs */ 57 if (MAJOR(dev->devt)) { 58 error = device_create_file(dev, &dev_attr_dev); 59 60 error = device_create_sys_dev_entry(dev); 61 62 devtmpfs_create_node(dev); 63 } 64 ... ... 65 66 kobject_uevent(&dev->kobj, KOBJ_ADD); 67 68 /* Try to find driver to bind this device */ 69 bus_probe_device(dev); 70 71 ... ... 72 73 }
主要工做以下:
globalfifo_device 初步初始化後主要成員列舉以下:
static struct platform_device globalfifo_device = { .name = "globalfifo_platform", .id = -1, .dev = { .parent = &platform_bus, .bus = &platform_bus_type, .p = { .device = & globalfifo_device.dev, INIT_LIST_HEAD(.klist_children->k_list), spin_lock_init(.klist_children->k_lock), .klist_children->get = klist_children_get, .klist_children->put = klist_children_put, INIT_LIST_HEAD(&.deferred_probe) }, .kobj = { .name = "globalfifo_platform", .kref->refcount->refs = 1, INIT_LIST_HEAD(.entry), .state_in_sysfs = 0, .state_add_uevent_sent = 0, .state_remove_uevent_sent = 0, .state_initialized = 1, .kset = devices_kset, .ktype = device_ktype, .name = "globalfifo_platform", .parent = & platform_bus.kobj, .sd = { //create_dir: /sys/devices/platform/globalfifo_platform .parent = platform_bus.kobj.sd, .dir.root = platform_bus.kobj.sd->dir.root, .ns = NULL, .priv = .kobj } }, INIT_LIST_HEAD(.dma_pools), Mutex_init(.mutex), spin_lock_init(.devres_lock), INIT_LIST_HEAD(&dev->devres_head), device_pm_init(.), .numa_node = -1, INIT_LIST_HEAD(&dev->msi_list), INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); dev->links.status = DL_DEV_NO_DRIVER; }, };
1 /***** drivers/base/bus.c *****/ 2 /** 3 * bus_probe_device - probe drivers for a new device 4 * @dev: device to probe 5 * 6 * - Automatically probe for a driver if the bus allows it. 7 */ 8 void bus_probe_device(struct device *dev) 9 { 10 struct bus_type *bus = dev->bus; 11 12 if (!bus) 13 return; 14 15 if (bus->p->drivers_autoprobe) 16 device_initial_probe(dev); 17 ... ... 18 } 19 20 /***** drivers/base/dd.c *****/ 21 void device_initial_probe(struct device *dev) 22 { 23 __device_attach(dev, true); 24 } 25 26 static int __device_attach(struct device *dev, bool allow_async) 27 { 28 ... ... 29 ret = bus_for_each_drv(dev->bus, NULL, &data, 30 __device_attach_driver); 31 ... ... 32 } 33 34 static int __device_attach_driver(struct device_driver *drv, void *_data) 35 { 36 struct device_attach_data *data = _data; 37 struct device *dev = data->dev; 38 bool async_allowed; 39 int ret; 40 41 ret = driver_match_device(drv, dev); 42 if (ret == 0) { 43 /* no match */ 44 return 0; 45 } else if (ret == -EPROBE_DEFER) { 46 dev_dbg(dev, "Device match requests probe deferral\n"); 47 driver_deferred_probe_add(dev); 48 } else if (ret < 0) { 49 dev_dbg(dev, "Bus failed to match device: %d", ret); 50 return ret; 51 } /* ret > 0 means positive match */ 52 53 async_allowed = driver_allows_async_probing(drv); 54 55 if (async_allowed) 56 data->have_async = true; 57 58 if (data->check_async && async_allowed != data->want_async) 59 return 0; 60 61 return driver_probe_device(drv, dev); 62 }
bus_probe_device(&globalfifo_device.dev) 的執行函數路線分析以下所示,通過層層調用,最終又調用到 driver_match_device() 和 driver_probe_device() 函數,查找總線上能和當前設備匹配的驅動,並將驅動和設備綁定在了一塊兒。
struct device *dev = &globalfifo_device.dev; struct device_attach_data *data = { .dev = dev, .check_async = allow_async, .want_async = false, }; struct device_driver *drv; --------------------------------------------------- bus_probe_device(dev) | V device_initial_probe(dev) | V __device_attach(dev, true) | V bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver) | V __device_attach_driver(drv, data) | V driver_match_device(drv, dev) / driver_probe_device(drv, dev)
綜上述分析,能夠看到驅動註冊的過程當中,會嘗試尋找總線上能夠與之匹配的設備;一樣地,設備註冊的過程當中,也會嘗試尋找總線上能夠與之綁定的驅動。整個過程當中,總線、設備、驅動的關鍵註冊函數分別爲:
--> driver_register() --> bus_add_driver()
--> device_add() --> bus_add_device() / bus_probe_device()
從sysfs的角度,能夠清楚地看到 platform_device 、 platform_driver 、 platform_bus 之間的聯繫:
/sys/bus/platform/drivers/globalfifo_platform$ ll total 0 bind globalfifo_platform -> ../../../../devices/platform/globalfifo_platform/ module -> ../../../../module/globalfifo_platform/ uevent unbind /sys/bus/platform/devices$ ll total 0 ... ... globalfifo_platform -> ../../../devices/platform/globalfifo_platform/ /sys/devices/platform/globalfifo_platform$ ll total 0 driver -> ../../../bus/platform/drivers/globalfifo_platform/ modalias power/ subsystem -> ../../../bus/platform/ uevent /sys/module/globalfifo_platform/drivers$ ll total 0 platform:globalfifo_platform -> ../../../bus/platform/drivers/globalfifo_platform/
[1] Linux設備驅動開發詳解(基於最新的Linux4.0內核),宋寶華編著,2016年
[2] Linux設備驅動程序,第三版
[3] 知識整理–linux設備驅動模型:https://blog.csdn.net/TongxinV/article/details/54853122
[4] Linux設備驅動模型:http://www.javashuo.com/article/p-drftedcw-m.html