1. 程式人生 > >Linux裝置驅動模型簡述(原始碼剖析)

Linux裝置驅動模型簡述(原始碼剖析)

 1. Linux裝置驅動模型和sysfs檔案系統

Linux核心在2.6版本中引入裝置驅動模型,簡化了驅動程式的編寫。Linux裝置驅動模型包含裝置(device)、匯流排(bus)、類(class)和驅動(driver),它們之間相互關聯。其中裝置(device)和驅動(driver)通過匯流排(bus)繫結在一起。

Linux核心中,分別用 bus_type、 device_driver和 device結構來描述匯流排、驅動和裝置,結構體定義詳見 linux/device.h。裝置和對應的驅動必須依附於同一種匯流排,因此 device_driver和 device結構中都包含 struct bus_type指標。

Linux sysfs 是一個虛擬的檔案系統,它把連線在系統上的裝置和匯流排組織成為一個分級的檔案,可以由使用者空間存取,向用戶空間匯出核心資料結構以及它們的屬性。

sysfs展示出裝置驅動模型中各個元件的層次關係,某個系統上的sysfs頂層目錄展示如下:

 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/

重要子目錄介紹:

  • block:    包含所有的塊裝置,如ram,sda等
  • bus:     包含系統中所有的匯流排型別,如pci,usb,i2c等
  • class:    包含系統中的裝置型別,如input,pci_bus,mmc_host等
  • dev:     包含兩個子目錄:char和block,分別存放字元裝置和塊裝置的主次裝置號(major:minor),指向 /sys/devices 目錄下的裝置
  • devices:  包含系統所有的裝置

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驅動目錄,可以發現存在指向裝置的連結。

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 項:

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 目錄下可以找到指向同樣裝置的一個連結:

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裝置驅動模型下,裝置和驅動可以分開註冊,依賴匯流排完成相互繫結。系統每註冊一個裝置的時候,會尋找與之匹配的驅動;相反,系統每註冊一個驅動的時候,會尋找與之匹配的裝置。這個過程中,裝置和驅動的匹配工作由匯流排完成。

下文中將會用關鍵的核心原始碼說明驅動和裝置間匹配機制的實現,分析的過程中以platform匯流排為例。【如果只想瞭解大致過程,可提前轉至最後一節:總結】

platform匯流排是一種虛擬的匯流排,與之相對應的是PCI、I2C、SPI等實體匯流排。引入虛擬platform匯流排是為了解決某些裝置無法直接依附在現有實體總線上的問題,例如SoC系統中整合的獨立外設控制器,掛接在SoC記憶體空間的外設等等。

 

2. platform匯流排的註冊

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 }

 

2.1 device_register(&platform_bus)

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 }
  • device_initialize() :對 struct device 中基本成員進行初始化,包括 kobject 、 struct device_private 、 struct mutex 等。
  • device_add(dev) :將platform匯流排也作為一個裝置 platform_bus 註冊到驅動模型中,重要的函式包括 device_create_file() 、 device_add_class_symlinks() 、 bus_add_device() 、 bus_probe_device() 等,下文中對設備註冊的介紹一節,將對這個函式做更詳細的介紹。 device_add(&platform_bus) 主要功能是完成 /sys/devices/platform 目錄的建立。

 

2.2 bus_register(&platform_bus_type)

 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->p->drivers_autoprobe = 1 
  • 對 struct kset 型別成員sysfs進行初始化,作為子系統中 kobject 物件的 parent 。 kset 本身也包含 kobject 物件,在sysfs中也表現為一個目錄,即 /sys/bus/platform 。
  • 建立 struct kset 型別的 drivers_kset 和 devices_kset ,作為匯流排下掛載的所有驅動和裝置的集合,sysfs中表現為 /sys/bus/platform/drivers 和 /sys/bus/platform/devices 。
  • 初始化連結串列 klist_drivers 和 klist_devices ,將匯流排下的驅動和裝置分別連結在一起。
  • 增加 probe 檔案,對應 /sys/bus/platform 目錄的檔案 drivers_autoprobe 和 drivers_probe 。

註冊完成後 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 };

 

3. platform驅動的註冊

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 }
 

3.1 driver_register(&(globalfifo_driver.driver))

 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)) 主要的工作包括:

  • 確認驅動依附的匯流排 platform_bus 已經被註冊並初始化(必要條件)。
  • 對bus、probe、remove等回撥函式初始化進行判斷,保證匯流排和驅動上相應的函式只能存在一個。
  • driver_find() 查詢總線上是否已存在當前驅動的同名驅動。
  • bus_add_driver(&(globalfifo_driver.driver)) ,將驅動註冊到總線上,下文詳述。
  • 發起 KOBJ_ADD 型別uevent,指示驅動已經新增完成,TODO。

 

3.2 bus_add_driver(&(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)) 的主要工作包括:

  • 為 struct device_driver 中結構 struct driver_private 動態分配空間,並完成後者 kobject 物件初始化。對應地,在 /sys/bus/platform/drivers 下建立目錄 globalfifo_platform 。
  • 初始化 klist_devices 連結串列,用來維護驅動相關聯的裝置。對應sysfs中在每個驅動目錄下關聯的裝置。
  •  klist_add_tail() 將當前驅動加入到匯流排對應的 klist_drivers 連結串列中。
  • 如果匯流排使能 drivers_autoprobe ,將呼叫 driver_attach() 嘗試匹配裝置。下文中將詳述此過程。
  •  module_add_driver(drv->owner, drv) 通過 sysfs_create_link() ,在 globalfifo_platform 目錄下新建module項指向 /sys/module/globalfifo_platform 。同時,也在 /sys/module/globalfifo_platform/ 目錄下新建driver目錄,建立 bus->name:drv->name連結到 /sys/bus/platform/drivers/globalfifo_platform 。
  • uevent設定。

初始化後 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 };

 

3.3 driver_attach(&(globalfifo_driver.driver))

 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) 函式包含兩個主要的部分:

  •  driver_match_device(drv, dev)  : 嘗試將驅動和裝置匹配,返回值指示是否能匹配。
  •  device_driver_attach(drv, dev) : 將驅動和裝置繫結。
 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 }

 

3.4 driver_match_device(drv, dev)

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() 函式會依次嘗試如下幾種方式:

  • driver_override 有效時,嘗試將驅動名字和 driver_override 匹配
  • 基於裝置樹風格的匹配
  • 基於 ACPI 風格的匹配
  • 匹配 ID 表
  • 匹配 platform_device 裝置名和驅動的名字
 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 }

 

3.5 device_driver_attach(drv, dev)

 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 }

 

3.6 really_probe(dev, drv)

 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) 主要完成的工作包括:

  • 將裝置 struct device 中 driver 指標指向 globalfifo_driver->driver 。
  • driver_sysfs_add(dev) 完成sysfs中裝置和驅動的連結,包括在驅動目錄下建立到裝置的連結,和在裝置目錄下建立到驅動的連結。
  • 裝置probe函式的呼叫:優先使用 platform_device->bus->probe 函式,其次使用 platform_driver->probe 函式。對於 globalfifo_driver ,會回撥 globalfifo_probe() ,完成裝置的初始化。
  • driver_bound(dev) 將裝置新增到驅動維護的裝置連結串列中,併發起 KOBJ_BIND 事件。

 

4. platform裝置的註冊

最後,對裝置的註冊過程進行簡要梳理。
和驅動類似,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) 。

 

4.1 platform_device_add(&globalfifo_device)

 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) 主要工作如下:

  • 對 globalfifo_device.dev.parent 和 globalfifo_device->dev.bus 初始化,分別指向 platform_bus 和 platform_bus_type 。
  • globalfifo_device.dev.kobj->name 初始化為 globalfifo_device.name  (“globalfifo_platform”)。
  • 呼叫 device_add(&globalfifo_device.dev)  device_add(&globalfifo_device.dev) 新增裝置。

 

4.2 device_add(&globalfifo_device.dev)

 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.dev.kobj.parent 初始化為 &platform_bus.kobj 。
  •  kobject_add() 函式初始化 globalfifo_device.dev.kobj 物件,在sysfs中建立相關的目錄,例如 /sys/devices/platform/globalfifo_platform 。
  •  bus_add_device(&globalfifo_device.dev) :將 globalfifo_device 註冊到匯流排系統裡,並建立sysfs的相關目錄:匯流排系統中建立到裝置的連結,同時也在裝置目錄下建立到匯流排的subsystem連結。
  •  bus_probe_device(dev) :嘗試在總線上尋找可以繫結的驅動。下文詳細介紹。

 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;
    },
};

 

4.3 bus_probe_device(&globalfifo_device.dev)

 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)

 

5. 總結

綜上述分析,可以看到驅動註冊的過程中,會嘗試尋找總線上可以與之匹配的裝置;同樣地,設備註冊的過程中,也會嘗試尋找總線上可以與之繫結的驅動。整個過程中,匯流排、裝置、驅動的關鍵註冊函式分別為:

  • 匯流排註冊: bus_register() 
  • 驅動註冊: platform_driver_register()  -->  driver_register()  -->  bus_add_driver() 
  • 設備註冊: platform_device_add()  -->  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裝置驅動模型:https://blog.csdn.net/TongxinV/article/details/54853122
[3] Linux裝置驅動模型:https://blog.csdn.net/qq_40732350/article/details/82992904