Linux設備驅動(6)模塊之間關系詳解


本文將對Linux系統中的sysfs進行簡單的分析,要分析sysfs就必須分析內核的driver-model(驅動模型),兩者是緊密聯系的。在分析過程中,本文將以platform總線和spi主控制器的platform驅動為例來進行講解。其實,platform機制是基於driver-model的,通過本文,也會對platform機制有個簡單的了解。

1. What is sysfs?
  個人理解:sysfs向用戶空間展示了驅動設備的層次結構,即目錄sys下的文件結構。我們都知道設備和對應的驅動都是由內核管理的,這些對於用戶空間是不可見的。現在通過sysfs,可以在用戶空間直觀的了解設備驅動的層次結構。

  我們來看看sysfs的文件結構:

[root@yj423 /sys]#ls
block     class     devices   fs        module
bus       dev       firmware  kernel    power

block:塊設備

bus:系統中的總線

class: 設備類型,比如輸入設備

dev:系統中已注冊的設備節點的視圖,有兩個子目錄char和block。

devices:系統中所有設備拓撲結構視圖

fireware:固件

fs:文件系統

kernel:內核配置選項和狀態信息

module:模塊

power:系統的電源管理數據

2. kobject ,kset和ktype
  要分析sysfs,首先就要分析kobject和kset,因為驅動設備的層次結構的構成就是由這兩個來完成的。

2.1 kobject
  kobject是一個對象的抽象,它用於管理對象。每個kobject對應着sysfs中的一個目錄。

  kobject用struct kobject來描述。

 1 struct kobject {
 2     const char        *name;            /*在sysfs建立目錄的名字*/
 3     struct list_head    entry;        /*用於連接到所屬kset的鏈表中*/
 4     struct kobject        *parent;    /*父對象*/
 5     struct kset        *kset;            /*屬於哪個kset*/
 6     struct kobj_type    *ktype;        /*類型*/
 7     struct sysfs_dirent    *sd;        /*sysfs中與該對象對應的文件節點*/
 8     struct kref        kref;            /*對象的應用計數*/
 9     unsigned int state_initialized:1;
10     unsigned int state_in_sysfs:1;
11     unsigned int state_add_uevent_sent:1;
12     unsigned int state_remove_uevent_sent:1;
13     unsigned int uevent_suppress:1;
14 };

2.2 kset
 kset是一些kobject的集合,這些kobject可以有相同的ktype,也可以不同。同時,kset自己也包含一個kobject。在sysfs中,kset也是對應這一個目錄,但是目錄下面包含着其他的kojbect。

  kset使用struct kset來描述。

 1 /**
 2  * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
 3  *
 4  * A kset defines a group of kobjects.  They can be individually
 5  * different "types" but overall these kobjects all want to be grouped
 6  * together and operated on in the same manner.  ksets are used to
 7  * define the attribute callbacks and other common events that happen to
 8  * a kobject.
 9  *
10  * @list: the list of all kobjects for this kset
11  * @list_lock: a lock for iterating over the kobjects
12  * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
13  * @uevent_ops: the set of uevent operations for this kset.  These are
14  * called whenever a kobject has something happen to it so that the kset
15  * can add new environment variables, or filter out the uevents if so
16  * desired.
17  */
18 struct kset {
19     struct list_head list;
20     spinlock_t list_lock;
21     struct kobject kobj;
22     const struct kset_uevent_ops *uevent_ops;
23 };

2.3 ktype
每個kobject對象都內嵌有一個ktype,該結構定義了kobject在創建和刪除時所采取的行為。

 1 struct kobj_type {
 2     void (*release)(struct kobject *kobj);
 3     const struct sysfs_ops *sysfs_ops;
 4     struct attribute **default_attrs;
 5     const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
 6     const void *(*namespace)(struct kobject *kobj);
 7 };
 8 
 9 /*以下兩個結構體定義位於include\linux\sysfs.h*/
10 struct sysfs_ops {
11     ssize_t    (*show)(struct kobject *, struct attribute *,char *);
12     ssize_t    (*store)(struct kobject *,struct attribute *,const char *, size_t);
13     const void *(*namespace)(struct kobject *, const struct attribute *);
14 };
15 
16 struct attribute {
17     const char        *name;
18     umode_t            mode;
19 #ifdef CONFIG_DEBUG_LOCK_ALLOC
20     bool            ignore_lockdep:1;
21     struct lock_class_key    *key;
22     struct lock_class_key    skey;
23 #endif

當kobject的引用計數為0時,通過release方法來釋放相關的資源。
attribute為屬性,每個屬性在sysfs中都有對應的屬性文件。

sysfs_op的兩個方法用於實現讀取和寫入屬性文件時應該采取的行為。

2.4 kobject與kset的關系
  下面這張圖非常經典。最下面的kobj都屬於一個kset,同時這些kobj的父對象就是kset內嵌的kobj。通過鏈表,kset可以獲取所有屬於它的kobj。

   從sysfs角度而言,kset代表一個文件夾,而下面的kobj就是這個文件夾里面的內容,而內容有可能是文件也有可能是文件夾。


3.舉例
在上一節中,我們知道sys下有一個bus目錄,這一將分析如何通過kobject創建bus目錄,以bus的創建過程分析kobject和kset。

下面代碼位於drivers/base/bus.c。

3.1從函數buses_init()開始分析,直接調用kset_create_and_add,第一個參數為要創建的目錄的名字,而第三個參數為空表示沒有父對象,因為bus在sys目錄下,沒有父對象。

bus是一個kset,其下面有很多kobject。

 1 int __init buses_init(void)
 2 {/*這里直接調用kset_create_and_add,第一個參數為要創建的目錄的名字,而第三個參數表示沒有父對象。*/
 3     bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
 4     if (!bus_kset)
 5         return -ENOMEM;
 6  /*這個函數中,動態分配了kset結構,調用kobject_set_name設置kset->kobj->name為bus,也就是我們要創建的目錄bus。同時這里kset->kobj.parent為NULL*/
 7     system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);//也就是沒有父對象。因為要創建的bus目錄是在sysfs所在的根目錄創建的,自然沒有父對象。
 8     if (!system_kset)
 9         return -ENOMEM;
10 
11     return 0;
12 }
13 
14 static const struct kset_uevent_ops bus_uevent_ops = {
15     .filter = bus_uevent_filter,
16 };
17 
18 static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
19 {
20     struct kobj_type *ktype = get_ktype(kobj);
21 
22     if (ktype == &bus_ktype)
23         return 1;
24     return 0;
25 }
 1 /**
 2  * kset_create_and_add - create a struct kset dynamically and add it to sysfs
 3  *
 4  * @name: the name for the kset
 5  * @uevent_ops: a struct kset_uevent_ops for the kset
 6  * @parent_kobj: the parent kobject of this kset, if any.
 7  *
 8  * This function creates a kset structure dynamically and registers it
 9  * with sysfs.  When you are finished with this structure, call
10  * kset_unregister() and the structure will be dynamically freed when it
11  * is no longer being used.
12  *
13  * If the kset was not able to be created, NULL will be returned.
14  */
15 struct kset *kset_create_and_add(const char *name,
16                  struct kset_uevent_ops *uevent_ops,
17                  struct kobject *parent_kobj)
18 {
19     struct kset *kset;
20     int error;
21  
22     kset = kset_create(name, uevent_ops, parent_kobj);    /*建立kset,設置某些字段*/
23     if (!kset)
24         return NULL;
25     error = kset_register(kset);    /*添加kset到sysfs*/
26     if (error) {
27         kfree(kset);
28         return NULL;
29     }
30     return kset;
31 }
接着函數kset_create中,動態分配了kset結構,初始化kset結構體成員部分變量。
 1 /**
 2  * kset_create - create a struct kset dynamically
 3  *
 4  * @name: the name for the kset
 5  * @uevent_ops: a struct kset_uevent_ops for the kset
 6  * @parent_kobj: the parent kobject of this kset, if any.
 7  *
 8  * This function creates a kset structure dynamically.  This structure can
 9  * then be registered with the system and show up in sysfs with a call to
10  * kset_register().  When you are finished with this structure, if
11  * kset_register() has been called, call kset_unregister() and the
12  * structure will be dynamically freed when it is no longer being used.
13  *
14  * If the kset was not able to be created, NULL will be returned.
15  */
16 static struct kset *kset_create(const char *name,
17                 struct kset_uevent_ops *uevent_ops,
18                 struct kobject *parent_kobj)
19 {
20     struct kset *kset;
21  
22     kset = kzalloc(sizeof(*kset), GFP_KERNEL);/*分配kset*/
23     if (!kset)
24         return NULL;
25     kobject_set_name(&kset->kobj, name);/*設置kobj->name*/
26     kset->uevent_ops = uevent_ops;
27     kset->kobj.parent = parent_kobj;    /*設置父對象*/
28  
29     /*
30      * The kobject of this kset will have a type of kset_ktype and belong to
31      * no kset itself.  That way we can properly free it when it is
32      * finished being used.
33      */
34     kset->kobj.ktype = &kset_ktype;
35     kset->kobj.kset = NULL;            /*本keset不屬於任何kset*/
36  
37     return kset;
38 }

接着調用kobject_set_name 采用標准化格式設置kset->kobj->name為bus,也就是我們要創建的目錄bus。同時這里kset->kobj.parent為NULL,也就是沒有父對象。因為要創建的bus目錄是在sysfs所在的根目錄sys創建的,自然沒有父對象。

/**
 * kobject_set_name - Set the name of a kobject
 * @kobj: struct kobject to set the name of
 * @fmt: format string used to build the name
 *
 * This sets the name of the kobject.  If you have already added the
 * kobject to the system, you must call kobject_rename() in order to
 * change the name of the kobject.
 */
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
{
    va_list vargs;
    int retval;
 
    va_start(vargs, fmt);
    retval = kobject_set_name_vargs(kobj, fmt, vargs);
    va_end(vargs);
 
    return retval;
}
 
/**
 * kobject_set_name_vargs - Set the name of an kobject
 * @kobj: struct kobject to set the name of
 * @fmt: format string used to build the name
 * @vargs: vargs to format the string.
 */
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
                  va_list vargs)
{
    const char *old_name = kobj->name;
    char *s;
 
    if (kobj->name && !fmt)
        return 0;
 
    kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
    if (!kobj->name)
        return -ENOMEM;
 
    /* ewww... some of these buggers have '/' in the name ... */
    while ((s = strchr(kobj->name, '/')))
        s[0] = '!';
 
    kfree(old_name);
    return 0;
}
 
/* Simplified asprintf. */
char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
{
    unsigned int len;
    char *p;
    va_list aq;
 
    va_copy(aq, ap);
    len = vsnprintf(NULL, 0, fmt, aq);
    va_end(aq);
 
    p = kmalloc(len+1, gfp);
    if (!p)
        return NULL;
 
    vsnprintf(p, len+1, fmt, ap);
 
    return p;
}

 

3.2kset_create_and_add函數中調用函數kset_create的流程分析完,然后緊接着調用函數kset_register

 1 /**
 2  * kset_register - initialize and add a kset.
 3  * @k: kset.
 4  */
 5 int kset_register(struct kset *k)
 6 {
 7     int err;
 8  
 9     if (!k)
10         return -EINVAL;
11  
12     kset_init(k);           /*初始化kset*/
13     err = kobject_add_internal(&k->kobj);  /*在sysfs中建立目錄*/
14     if (err)
15         return err;
16     kobject_uevent(&k->kobj, KOBJ_ADD);
17     return 0;
18 }

 

函數kset_init接着初始化kset中的其他成員變量

 1 /**
 2  * kset_init - initialize a kset for use
 3  * @k: kset
 4  */
 5 void kset_init(struct kset *k)
 6 {
 7     kobject_init_internal(&k->kobj);/*初始化kobject的某些字段*/
 8     INIT_LIST_HEAD(&k->list);    /*初始化鏈表頭*/
 9     spin_lock_init(&k->list_lock);    /*初始化自旋鎖*/
10 }
11  
12 static void kobject_init_internal(struct kobject *kobj)
13 {
14     if (!kobj)
15         return;
16     kref_init(&kobj->kref);           /*初始化引用基計數*/
17     INIT_LIST_HEAD(&kobj->entry);    /*初始化鏈表頭*/
18     kobj->state_in_sysfs = 0;
19     kobj->state_add_uevent_sent = 0;
20     kobj->state_remove_uevent_sent = 0;
21     kobj->state_initialized = 1;
22 }

 

 函數kobject_add_internal,將kobj加入內核,在sysfs中將建立目錄。

 1 static int kobject_add_internal(struct kobject *kobj)
 2 {
 3     int error = 0;
 4     struct kobject *parent;
 5  
 6     if (!kobj)
 7         return -ENOENT;
 8     /*檢查name字段是否存在*/
 9     if (!kobj->name || !kobj->name[0]) {
10         WARN(1, "kobject: (%p): attempted to be registered with empty "
11              "name!\n", kobj);
12         return -EINVAL;
13     }
14  
15     parent = kobject_get(kobj->parent);    /*有父對象則增加父對象引用計數*/
16  
17     /* join kset if set, use it as parent if we do not already have one */
18     if (kobj->kset) {    
19         if (!parent)
20             /*kobj屬於某個kset,但是該kobj沒有父對象,則以kset的kobj作為父對象*/
21             parent = kobject_get(&kobj->kset->kobj);
22         kobj_kset_join(kobj);        /*將kojbect添加到kset結構中的鏈表當中*/
23         kobj->parent = parent;
24     }
25  
26     pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
27          kobject_name(kobj), kobj, __func__,
28          parent ? kobject_name(parent) : "<NULL>",
29          kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
30  
31     error = create_dir(kobj);    /*根據kobj->name在sys中建立目錄*/
32     if (error) {
33         kobj_kset_leave(kobj);    /*刪除鏈表項*/
34         kobject_put(parent);    /*減少引用計數*/
35         kobj->parent = NULL;
36  
37         /* be noisy on error issues */
38         if (error == -EEXIST)
39             printk(KERN_ERR "%s failed for %s with "
40                    "-EEXIST, don't try to register things with "
41                    "the same name in the same directory.\n",
42                    __func__, kobject_name(kobj));
43         else
44             printk(KERN_ERR "%s failed for %s (%d)\n",
45                    __func__, kobject_name(kobj), error);
46         dump_stack();
47     } else
48         kobj->state_in_sysfs = 1;
49  
50     return error;
51 }

在上面的kset_create中有kset->kobj.kset = NULL,因此if (kobj->kset)條件不滿足。因此在這個函數中,對name進行了必要的檢查之后,調用了create_dir在sysfs中創建目錄。

在create_dir執行完成以后會在sysfs的根目錄(/sys/)建立文件夾bus。該函數的詳細分析將在后面給出。

至此,對bus目錄的建立有了簡單而直觀的了解。我們可以看出kset其實就是表示一個文件夾,而kset本身也含有一個kobject,而該kobject的name字段即為該目錄的名字,本例中為bus。

4. 驅動模型回顧
前2節所介紹的是最底層,最核心的內容。下面開始將描述較為高層的內容。Linux設備模型使用了bus/device/device_driver三個數據結構分別來描述總線、設備和驅動。所有的設備和對應的驅動都必須掛載在某一個總線上,通過總線,可以綁定設備和驅動。這個屬於分離的思想,將設備和驅動分開管理。同時驅動程序可以了解到所有它所支持的設備,同樣的,設備也能知道它對應驅動程序。

下面分析bus/device/device_driver三者之間的關系,三個結構體不再重復。

4.1 bus
總線是處理器與一個設備或者多個設備之間的通道。在設備模型中,所有的設備都掛載在某一個總線上。總線使用struct bus_type來表述。結構體定義代碼位於include/linux/device.h和driver/base/base.h。

 1 /**
 2  * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
 3  *
 4  * @subsys - the struct kset that defines this subsystem
 5  * @devices_kset - the subsystem's 'devices' directory
 6  * @interfaces - list of subsystem interfaces associated
 7  * @mutex - protect the devices, and interfaces lists.
 8  *
 9  * @drivers_kset - the list of drivers associated
10  * @klist_devices - the klist to iterate over the @devices_kset
11  * @klist_drivers - the klist to iterate over the @drivers_kset
12  * @bus_notifier - the bus notifier list for anything that cares about things
13  *                 on this bus.
14  * @bus - pointer back to the struct bus_type that this structure is associated
15  *        with.
16  *
17  * @glue_dirs - "glue" directory to put in-between the parent device to
18  *              avoid namespace conflicts
19  * @class - pointer back to the struct class that this structure is associated
20  *          with.
21  *
22  * This structure is the one that is the actual kobject allowing struct
23  * bus_type/class to be statically allocated safely.  Nothing outside of the
24  * driver core should ever touch these fields.
25  */
26 struct subsys_private {
27     struct kset subsys;
28     struct kset *devices_kset;//負責連接它下面的所有device的目錄
29     struct list_head interfaces;
30     struct mutex mutex;
31 
32     struct kset *drivers_kset;//負責連接它下面的所有driver的目錄
33     struct klist klist_devices;//負責連接它下面的所有device的鏈表,device->p->knode_bus的表頭
34     struct klist klist_drivers;//負責連接它下面的所有driver的鏈表,driver->p->knode_bus的表頭
35     struct blocking_notifier_head bus_notifier;
36     unsigned int drivers_autoprobe:1;
37     struct bus_type *bus;
38 
39     struct kset glue_dirs;
40     struct class *class;
41 };

 

每個bus_type都包含一個kset對象subsys,該kset在/sys/bus/目錄下有着對應的一個目錄,目錄名即為字段name。后面我們將看到platform總線的建立。
drivers_kset和devices_kset對應着兩個目錄,該兩個目錄下將包含該總線上的設備和相應的驅動程序。

總線有個私有數據subsys_private *p,只有該總線可以訪問它,其他不可訪問。同時總線上的設備和驅動將分別保存在兩個鏈表中:bus_type->p->klist_devices和bus_type->p->klist_drivers。

4.2 device
設備對象在driver-model中使用struct device來表示。代碼位於include/linux/device.h。不再重復。

device本身包含一個kobject,也就是說這個device在sysfs的某個地方有着一個對應的目錄。

device包含私有指針變量,用於連接bus和device_driver,只有device能訪問,其他不可訪問。

 1 /**
 2  * struct device_private - structure to hold the private to the driver core portions of the device structure.
 3  *
 4  * @klist_children - klist containing all children of this device
 5  * @knode_parent - node in sibling list
 6  * @knode_driver - node in driver list
 7  * @knode_bus - node in bus list
 8  * @deferred_probe - entry in deferred_probe_list which is used to retry the
 9  *    binding of drivers which were unable to get all the resources needed by
10  *    the device; typically because it depends on another driver getting
11  *    probed first.
12  * @driver_data - private pointer for driver specific info.  Will turn into a
13  * list soon.
14  * @device - pointer back to the struct class that this structure is
15  * associated with.
16  *
17  * Nothing outside of the driver core should ever touch these fields.
18  */
19 struct device_private {
20     struct klist klist_children;
21     struct klist_node knode_parent;
22     struct klist_node knode_driver;//一個driver可以支持多個device,該device就是靠這個節點加入到匹配到的driver的鏈表的
23     struct klist_node knode_bus;//所屬的bus,bus下所有device組成一個鏈表,表頭是bus的klist_devices
24     struct list_head deferred_probe;
25     void *driver_data;
26     struct device *device;
27 };

 

該device所掛載的bus由device->p->knode_bus指定。

該device所對應的設備驅動由device->p->knode_driver指定。

4.3 device_driver
設備設備對象在driver-model中使用struct device_driver來表示。結構體定義位於include/linux/device.h。device_driver本身包含一個kobject,也就是說這個device_driver在sysfs的某個地方也有着一個對應的目錄。

1 struct driver_private {
2     struct kobject kobj;
3     struct klist klist_devices;//一個driver可以支持多個device,用鏈表連接它所支持的device
4     struct klist_node knode_bus;//所屬的bus,bus下所有driver組成一個鏈表,表頭是bus的klist_drivers
5     struct module_kobject *mkobj;
6     struct device_driver *driver;
7 };

 

device_driver也有私有指針數據driver_private,同樣只有該driver可以訪問,其他不可訪問。

該設備驅動所支持的設備由driver->p->klist_devices指定,該設備驅動所掛載的總線由driver->p->knode_bus制定。

小總:這樣的話,bus、device和device_driver都有自己的私有指針數據,它們的功能就是負責連接對方。通過klist_node 強化版的鏈表,互相連接起來的。

5. platform bus實例
 接下來來分析/sys/bus/platform是如何建立的。定義位於drivers\base\platform.c

platform總線的注冊是由platform_bus_init函數完成的。該函數在內核啟動階段被調用,我們來簡單看下調用過程:

start_kernel() -> rest_init() ->kernel_init() -> do_basic_setup() -> driver_init() -> platform_bus_init()。

注:kernel_init()是在rest_init函數中創建內核線程來執行的。

 1 int __init platform_bus_init(void)
 2 {
 3     int error;
 4 
 5     early_platform_cleanup();
 6 
 7     error = device_register(&platform_bus);
 8     if (error)
 9         return error;
10     error =  bus_register(&platform_bus_type);
11     if (error)
12         device_unregister(&platform_bus);
13     return error;
14 }
15 
16 struct device platform_bus = {
17     .init_name    = "platform",
18 };
19 
20 struct bus_type platform_bus_type = {
21     .name        = "platform",
22     .dev_attrs    = platform_dev_attrs,
23     .match        = platform_match,
24     .uevent        = platform_uevent,
25     .pm        = &platform_dev_pm_ops,
26 };

從bus_type,我們看到該總線的名字為platform。
調用了device_register和bus_register兩個函數,我們只關注bus_register函數。

 1 int bus_register(struct bus_type *bus)
 2 {
 3     int retval;
 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     if (!priv)
 9         return -ENOMEM;
10 
11     priv->bus = bus;
12     bus->p = priv;
13 
14     BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
15 
16     retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
17     if (retval)
18         goto out;
19 
20     priv->subsys.kobj.kset = bus_kset;
21     priv->subsys.kobj.ktype = &bus_ktype;
22     priv->drivers_autoprobe = 1;
23 
24     retval = kset_register(&priv->subsys);
25     if (retval)
26         goto out;
27 
28     retval = bus_create_file(bus, &bus_attr_uevent);
29     if (retval)
30         goto bus_uevent_fail;
31 
32     priv->devices_kset = kset_create_and_add("devices", NULL,
33                          &priv->subsys.kobj);
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,
40                          &priv->subsys.kobj);
41     if (!priv->drivers_kset) {
42         retval = -ENOMEM;
43         goto bus_drivers_fail;
44     }
45 
46     INIT_LIST_HEAD(&priv->interfaces);
47     __mutex_init(&priv->mutex, "subsys mutex", key);
48     klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
49     klist_init(&priv->klist_drivers, NULL, NULL);
50 
51     retval = add_probe_files(bus);
52     if (retval)
53         goto bus_probe_files_fail;
54 
55     retval = bus_add_attrs(bus);
56     if (retval)
57         goto bus_attrs_fail;
58 
59     pr_debug("bus: '%s': registered\n", bus->name);
60     return 0;
61 
62 bus_attrs_fail:
63     remove_probe_files(bus);
64 bus_probe_files_fail:
65     kset_unregister(bus->p->drivers_kset);
66 bus_drivers_fail:
67     kset_unregister(bus->p->devices_kset);
68 bus_devices_fail:
69     bus_remove_file(bus, &bus_attr_uevent);
70 bus_uevent_fail:
71     kset_unregister(&bus->p->subsys);
72 out:
73     kfree(bus->p);
74     bus->p = NULL;
75     return retval;
76 }

 

函數中,首先調用kobject_set_name設置了bus對象的subsys.kobject->name 為 platform,也就是說會建立一個名為platform的目錄。kobject_set_name函數在3.1小節中已經給出。
在這里還用到了bus_kset這個變量,這個變量就是在第3節buses_init函數中建立bus目錄所對應的kset對象。

接着,priv->subsys.kobj.kset = bus_kset,設置subsys的kobj在bus_kset對象包含的集合中,也就是說bus目錄下將包含subsys對象所對應的目錄,即platform。

緊接着調用了kset_register,參數為&priv->subsys。該函數在3.2節中以給出。在該函數的調用過程中,將調用kobj_kset_join函數,該函數將kobject添加到kobject->kset的鏈表中。

 1 /**
 2  * kset_register - initialize and add a kset.
 3  * @k: kset.
 4  */
 5 int kset_register(struct kset *k)
 6 {
 7     int err;
 8 
 9     if (!k)
10         return -EINVAL;
11 
12     kset_init(k);
13     err = kobject_add_internal(&k->kobj);
14     if (err)
15         return err;
16     kobject_uevent(&k->kobj, KOBJ_ADD);
17     return 0;
18 }

 

kset_register函數執行完成后,將在/sys/bus/下建立目錄platform。此刻,我們先來看下kset和kobject之間的關系。

然后,調用了bus_create_file函數在/sys/bus/platform/下建立文件uevent。

 1 int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
 2 {
 3     int error;
 4     if (bus_get(bus)) {
 5         error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
 6         bus_put(bus);
 7     } else
 8         error = -EINVAL;
 9     return error;
10 }

有關底層的sysfs將在后面敘述,這里只要關注參數&bus->p->subsys.kobj,表示在該kset下建立文件,也就是platform下建立。
接着調用了2次kset_create_and_add,分別在/sys/bus/platform/下建立了文件夾devices和drivers。該函數位於第3節開始處。

這里和第3節調用kset_create_and_add時的最主要一個區別就是:此時的parent參數不為NULL,而是&priv->subsys.kobj。

也就是說,將要創建的kset的kobject->parent = &priv->subsys.kobj,也即新建的kset被包含在platform文件夾對應的kset中。

我們來看下關系圖:


隨后,bus_register接着調用了add_probe_files創建了屬性文件drivers_autoprobe和drivers_probe。

 1 static int add_probe_files(struct bus_type *bus)
 2 {
 3     int retval;
 4 
 5     retval = bus_create_file(bus, &bus_attr_drivers_probe);
 6     if (retval)
 7         goto out;
 8 
 9     retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
10     if (retval)
11         bus_remove_file(bus, &bus_attr_drivers_probe);
12 out:
13     return retval;
14 }

該函數只是簡單的調用了兩次bus_create_file,該函數已在前面敘述過。
最后調用bus_add_attrs創建總線相關的屬性文件。

 1 /**
 2  * bus_add_attrs - Add default attributes for this bus.
 3  * @bus: Bus that has just been registered.
 4  */
 5 
 6 static int bus_add_attrs(struct bus_type *bus)
 7 {
 8     int error = 0;
 9     int i;
10 
11     if (bus->bus_attrs) {
12         for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
13             error = bus_create_file(bus, &bus->bus_attrs[i]);
14             if (error)
15                 goto err;
16         }
17     }
18 done:
19     return error;
20 err:
21     while (--i >= 0)
22         bus_remove_file(bus, &bus->bus_attrs[i]);
23     goto done;
24 }

我們可以看到這個函數將根據bus_type->bus_arrts來創建屬性文件。不過,在本例中,bus_arrts從未給出定義,因此次函數不做任何工作。
好了,整個bus_register調用完成了,我們來看下sysfs中實際的情況。

[root1423 platform]#pwd
/sys/bus/platform
[root123 platform]#ls
devices drivers drivers_autoprobe drivers_probe uevent

最后,我們對整個bus_register的過程進行一個小結。

 

6. device舉例
本節將首先講述如何在/sys/devices下建立虛擬的platform設備,然后再講述如何在/sys/devices/platform/下建立子設備。

之所以叫虛擬是因為這個platform並不代表任何實際存在的設備,但是platform將是所有具體設備的父設備。

6.1虛擬的platform設備

在第5節,platform_bus_init函數中還調用了device_register,

詳解:linux設備驅動(2)device詳解

 1 int device_register(struct device *dev)
 2 {
 3     device_initialize(dev);    /*初始化dev的某些字段*/
 4     return device_add(dev); /*將設備添加到系統中*/
 5 }
 6 
 7 void device_initialize(struct device *dev)
 8 {
 9     dev->kobj.kset = devices_kset;        /*設置kobj屬於哪個kset,/sys/devices/*/
10     kobject_init(&dev->kobj, &device_ktype);/*初始化dev->kobj*/
11     INIT_LIST_HEAD(&dev->dma_pools);    /*初始化鏈表頭*/
12     init_MUTEX(&dev->sem);                /*初始化互斥體*/
13     spin_lock_init(&dev->devres_lock);    /*初始化自旋鎖*/
14     INIT_LIST_HEAD(&dev->devres_head);    /*初始化鏈表頭*/
15     device_init_wakeup(dev, 0);            /*設置該device不能喚醒*/
16     device_pm_init(dev);                /*設置該device的電源狀態*/
17     set_dev_node(dev, -1);              /*如果使用NUMA,則設置NUMA節點*/
18 }

 

6.1.1

首先其中用到了devices_kset對象,這個對象和第3節當中的bus_kset是同樣的性質,也就是說該對象表示一個目錄。

該對象的建立是在devices_init函數中完成的。

 1 int __init devices_init(void)
 2 {
 3     devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
 4     if (!devices_kset)
 5         return -ENOMEM;
 6     dev_kobj = kobject_create_and_add("dev", NULL);
 7     if (!dev_kobj)
 8         goto dev_kobj_err;
 9     sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
10     if (!sysfs_dev_block_kobj)
11         goto block_kobj_err;
12     sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
13     if (!sysfs_dev_char_kobj)
14         goto char_kobj_err;
15  
16     return 0;
17  
18  char_kobj_err:
19     kobject_put(sysfs_dev_block_kobj);
20  block_kobj_err:
21     kobject_put(dev_kobj);
22  dev_kobj_err:
23     kset_unregister(devices_kset);
24     return -ENOMEM;
25 }

由此可見,devices_kset對象表示的目錄為/sys下的devices目錄。

6.1.2kobject_init

 1 void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
 2 {
 3     char *err_str;
 4  
 5     if (!kobj) {
 6         err_str = "invalid kobject pointer!";
 7         goto error;
 8     }
 9     if (!ktype) {
10         err_str = "must have a ktype to be initialized properly!\n";
11         goto error;
12     }
13     if (kobj->state_initialized) {
14         /* do not error out as sometimes we can recover */
15         printk(KERN_ERR "kobject (%p): tried to init an initialized "
16                "object, something is seriously wrong.\n", kobj);
17         dump_stack();
18     }
19  
20     kobject_init_internal(kobj);
21     kobj->ktype = ktype;
22     return;
23  
24 error:
25     printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
26     dump_stack();
27 }
28 EXPORT_SYMBOL(kobject_init);
29  
30 static void kobject_init_internal(struct kobject *kobj)
31 {
32     if (!kobj)
33         return;
34     kref_init(&kobj->kref);            /*初始化引用基計數*/
35     INIT_LIST_HEAD(&kobj->entry);    /*初始化鏈表頭*/
36     kobj->state_in_sysfs = 0;
37     kobj->state_add_uevent_sent = 0;
38     kobj->state_remove_uevent_sent = 0;
39     kobj->state_initialized = 1;
40 }

 

該函數在做了一系列的必要檢查后,調用kobject_init_internal初始化了kobject的某些字段。

6.1.3device_init_wakeup

參數val為0,設置該device不能夠喚醒。

 1 #ifdef CONFIG_PM
 2  
 3 /* changes to device_may_wakeup take effect on the next pm state change.
 4  * by default, devices should wakeup if they can.
 5  */
 6 static inline void device_init_wakeup(struct device *dev, int val)
 7 {
 8     dev->power.can_wakeup = dev->power.should_wakeup = !!val;
 9 }
10 #else /* !CONFIG_PM */
11  
12 /* For some reason the next two routines work even without CONFIG_PM */
13 static inline void device_init_wakeup(struct device *dev, int val)
14 {
15     dev->power.can_wakeup = !!val;
16 }
17 #endif

 

6.2device_add

接下來是注冊的第二步:調用device_add。

  1 int device_add(struct device *dev)
  2 {
  3     struct device *parent = NULL;
  4     struct class_interface *class_intf;
  5     int error = -EINVAL;
  6  
  7     dev = get_device(dev);    /*增加引用計數*/
  8     if (!dev)
  9         goto done;
 10  
 11     dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);    /*分配device_private結構*/
 12     if (!dev->p) {
 13         error = -ENOMEM;
 14         goto done;
 15     }
 16     dev->p->device = dev;    /*保存dev*/
 17     klist_init(&dev->p->klist_children, klist_children_get,    /*初始化內核鏈表*/
 18            klist_children_put);
 19  
 20     /*
 21      * for statically allocated devices, which should all be converted
 22      * some day, we need to initialize the name. We prevent reading back
 23      * the name, and force the use of dev_name()
 24      */
 25     if (dev->init_name) {
 26         dev_set_name(dev, dev->init_name);     /*dev->kobject->name = dev->init_name*/
 27         dev->init_name = NULL;
 28     }
 29  
 30     if (!dev_name(dev))    /*檢查dev->kobject->name*/
 31         goto name_error;
 32  
 33     pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
 34  
 35     parent = get_device(dev->parent);    /*增加父設備引用計數*/
 36     setup_parent(dev, parent);            /*設置dev->kobject->parent*/
 37  
 38     /* use parent numa_node */
 39     if (parent)
 40         set_dev_node(dev, dev_to_node(parent));
 41  
 42     /* first, register with generic layer. */
 43     /* we require the name to be set before, and pass NULL */
 44     /* 執行完以后,將在/sys/devices/下建立目錄XXX,目錄名XXX為dev->kobj->name*/
 45     error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);    
 46     if (error)
 47         goto Error;
 48  
 49     /* notify platform of device entry */
 50     if (platform_notify)
 51         platform_notify(dev);
 52  
 53     /*在XXX下建立文件uevent*/
 54     error = device_create_file(dev, &uevent_attr);
 55     if (error)
 56         goto attrError;
 57  
 58     if (MAJOR(dev->devt)) {/*主設備號不為0*/
 59         error = device_create_file(dev, &devt_attr);/*創建屬性文件dev*/
 60         if (error)
 61             goto ueventattrError;
 62  
 63         /* 在sys/dev/char/下建立symlink,名字為主設備號:次設備號,該鏈接指向XXX */
 64         error = device_create_sys_dev_entry(dev); 
 65         if (error)
 66             goto devtattrError;
 67     }
 68  
 69     error = device_add_class_symlinks(dev);
 70     if (error)
 71         goto SymlinkError;
 72     error = device_add_attrs(dev);    /*添加類設備屬型文件和屬性組*/
 73     if (error)
 74         goto AttrsError;
 75     error = bus_add_device(dev);    /*添加3個symlink*/
 76     if (error)
 77         goto BusError;
 78     error = dpm_sysfs_add(dev);        /*創建power子目錄,並在其下添加電源管理的屬性組文件*/
 79     if (error)
 80         goto DPMError;
 81     device_pm_add(dev);                /*將該device添加到電源管理鏈表中*/
 82  
 83     /* Notify clients of device addition.  This call must come
 84      * after dpm_sysf_add() and before kobject_uevent().
 85      */
 86     if (dev->bus)
 87         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 88                          BUS_NOTIFY_ADD_DEVICE, dev);
 89  
 90     kobject_uevent(&dev->kobj, KOBJ_ADD);    /*通知用戶層*/
 91     bus_attach_device(dev);                    /*將設備添加到總線的設備鏈表中,並嘗試獲取驅動*/
 92     if (parent)
 93         klist_add_tail(&dev->p->knode_parent,    /*有父設備,則將該設備添加到父設備的兒子鏈表中*/
 94                    &parent->p->klist_children);
 95  
 96     if (dev->class) {                        /*該設備屬於某個設備類*/
 97         mutex_lock(&dev->class->p->class_mutex);
 98         /* tie the class to the device */
 99         klist_add_tail(&dev->knode_class,    /*將device添加到class的類設備鏈表中*/
100                    &dev->class->p->class_devices);
101  
102         /* notify any interfaces that the device is here */
103         list_for_each_entry(class_intf,
104                     &dev->class->p->class_interfaces, node)
105             if (class_intf->add_dev)
106                 class_intf->add_dev(dev, class_intf);
107         mutex_unlock(&dev->class->p->class_mutex);
108     }
109 done:
110     put_device(dev);
111     return error;
112  DPMError:
113     bus_remove_device(dev);
114  BusError:
115     device_remove_attrs(dev);
116  AttrsError:
117     device_remove_class_symlinks(dev);
118  SymlinkError:
119     if (MAJOR(dev->devt))
120         device_remove_sys_dev_entry(dev);
121  devtattrError:
122     if (MAJOR(dev->devt))
123         device_remove_file(dev, &devt_attr);
124  ueventattrError:
125     device_remove_file(dev, &uevent_attr);
126  attrError:
127     kobject_uevent(&dev->kobj, KOBJ_REMOVE);
128     kobject_del(&dev->kobj);
129  Error:
130     cleanup_device_parent(dev);
131     if (parent)
132         put_device(parent);
133 name_error:
134     kfree(dev->p);
135     dev->p = NULL;
136     goto done;
137 }

 

 

該函數是device的重要API,接下來以此設備為例,逐步分析

6.2.1setup_parent

下列代碼位於drivers/base/core.c。

 1 static void setup_parent(struct device *dev, struct device *parent)
 2 {
 3     struct kobject *kobj;
 4     kobj = get_device_parent(dev, parent);
 5     if (kobj)
 6         dev->kobj.parent = kobj;
 7 }
 8  
 9 static struct kobject *get_device_parent(struct device *dev,
10                      struct device *parent)
11 {
12     /* class devices without a parent live in /sys/class/<classname>/ */
13     if (dev->class && (!parent || parent->class != dev->class))
14         return &dev->class->p->class_subsys.kobj;
15     /* all other devices keep their parent */
16     else if (parent)
17         return &parent->kobj;
18  
19     return NULL;
20 }

該函數將設置dev對象的parent。在這里實際傳入的parent為NULL,同時dev->class也沒有定義過。因此這個函數什么都沒有做。

6.2.2 kobject_add

 1 int kobject_add(struct kobject *kobj, struct kobject *parent,
 2         const char *fmt, ...)
 3 {
 4     va_list args;
 5     int retval;
 6  
 7     if (!kobj)
 8         return -EINVAL;
 9  
10     if (!kobj->state_initialized) {
11         printk(KERN_ERR "kobject '%s' (%p): tried to add an "
12                "uninitialized object, something is seriously wrong.\n",
13                kobject_name(kobj), kobj);
14         dump_stack();
15         return -EINVAL;
16     }
17     va_start(args, fmt);
18     retval = kobject_add_varg(kobj, parent, fmt, args);
19     va_end(args);
20  
21     return retval;
22 }
23 EXPORT_SYMBOL(kobject_add);
24  
25 static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
26                 const char *fmt, va_list vargs)
27 {
28     int retval;
29  
30     retval = kobject_set_name_vargs(kobj, fmt, vargs);
31     if (retval) {
32         printk(KERN_ERR "kobject: can not set name properly!\n");
33         return retval;
34     }
35     kobj->parent = parent;
36     return kobject_add_internal(kobj);
37 }
38  
39 static int kobject_add_internal(struct kobject *kobj)
40 {
41     int error = 0;
42     struct kobject *parent;
43  
44     if (!kobj)
45         return -ENOENT;
46     /*檢查name字段是否存在*/
47     if (!kobj->name || !kobj->name[0]) {
48         WARN(1, "kobject: (%p): attempted to be registered with empty "
49              "name!\n", kobj);
50         return -EINVAL;
51     }
52  
53     parent = kobject_get(kobj->parent);    /*有父對象則增加父對象引用計數*/
54  
55     /* join kset if set, use it as parent if we do not already have one */
56     if (kobj->kset) {    
57         if (!parent)
58             /*kobj屬於某個kset,但是該kobj沒有父對象,則以kset的kobj作為父對象*/
59             parent = kobject_get(&kobj->kset->kobj);
60         kobj_kset_join(kobj);        /*將kojbect添加到kset結構中的鏈表當中*/
61         kobj->parent = parent;
62     }
63  
64     pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
65          kobject_name(kobj), kobj, __func__,
66          parent ? kobject_name(parent) : "<NULL>",
67          kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
68  
69     error = create_dir(kobj);    /*根據kobj->name在sys中建立目錄*/
70     if (error) {
71         kobj_kset_leave(kobj);    /*刪除鏈表項*/
72         kobject_put(parent);    /*減少引用計數*/
73         kobj->parent = NULL;
74  
75         /* be noisy on error issues */
76         if (error == -EEXIST)
77             printk(KERN_ERR "%s failed for %s with "
78                    "-EEXIST, don't try to register things with "
79                    "the same name in the same directory.\n",
80                    __func__, kobject_name(kobj));
81         else
82             printk(KERN_ERR "%s failed for %s (%d)\n",
83                    __func__, kobject_name(kobj), error);
84         dump_stack();
85     } else
86         kobj->state_in_sysfs = 1;
87  
88     return error;
89 }

在調用時,參數parent為NULL,且dev->kobj.kset在6.1節device_initialize函數中設置為devices_kset。
而devices_kset對應着/sys/devices目錄,因此該函數調用完成后將在/sys/devices目錄下生成目錄platform。

但是這里比較奇怪的是,為什么platform目錄沒有對應的kset對象?

6.2.3device_create_sys_dev_entry

在調用該函數之前,會在/sys/devices/platform/下生成屬性文件。接着如果該device的設備號不為0,則創建屬性文件dev,並調用本函數。

但是,在本例中設備號devt從未設置過,顯然為0,那么本函數實際並未執行。

 1 static int device_create_sys_dev_entry(struct device *dev)
 2 {
 3     struct kobject *kobj = device_to_dev_kobj(dev);
 4     int error = 0;
 5     char devt_str[15];
 6  
 7     if (kobj) {
 8         format_dev_t(devt_str, dev->devt);
 9         error = sysfs_create_link(kobj, &dev->kobj, devt_str);
10     }
11  
12     return error;
13 }
14 /**
15  * device_to_dev_kobj - select a /sys/dev/ directory for the device
16  * @dev: device
17  *
18  * By default we select char/ for new entries.  Setting class->dev_obj
19  * to NULL prevents an entry from being created.  class->dev_kobj must
20  * be set (or cleared) before any devices are registered to the class
21  * otherwise device_create_sys_dev_entry() and
22  * device_remove_sys_dev_entry() will disagree about the the presence
23  * of the link.
24  */
25 static struct kobject *device_to_dev_kobj(struct device *dev)
26 {
27     struct kobject *kobj;
28  
29     if (dev->class)
30         kobj = dev->class->dev_kobj;
31     else
32         kobj = sysfs_dev_char_kobj;
33  
34     return kobj;
35 }

 6.2.4device_add_class_symlinks

由於dev->class為NULL,本函數其實沒做任何工作。

下列代碼位於drivers/base/core.c。

 1 static int device_add_class_symlinks(struct device *dev)
 2 {
 3     int error;
 4  
 5     if (!dev->class)
 6         return 0;
 7  
 8     error = sysfs_create_link(&dev->kobj,
 9                   &dev->class->p->class_subsys.kobj,
10                   "subsystem");
11     if (error)
12         goto out;
13  
14 #ifdef CONFIG_SYSFS_DEPRECATED
15     /* stacked class devices need a symlink in the class directory */
16     if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
17         device_is_not_partition(dev)) {
18         error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
19                       &dev->kobj, dev_name(dev));
20         if (error)
21             goto out_subsys;
22     }
23  
24     if (dev->parent && device_is_not_partition(dev)) {
25         struct device *parent = dev->parent;
26         char *class_name;
27  
28         /*
29          * stacked class devices have the 'device' link
30          * pointing to the bus device instead of the parent
31          */
32         while (parent->class && !parent->bus && parent->parent)
33             parent = parent->parent;
34  
35         error = sysfs_create_link(&dev->kobj,
36                       &parent->kobj,
37                       "device");
38         if (error)
39             goto out_busid;
40  
41         class_name = make_class_name(dev->class->name,
42                         &dev->kobj);
43         if (class_name)
44             error = sysfs_create_link(&dev->parent->kobj,
45                         &dev->kobj, class_name);
46         kfree(class_name);
47         if (error)
48             goto out_device;
49     }
50     return 0;
51  
52 out_device:
53     if (dev->parent && device_is_not_partition(dev))
54         sysfs_remove_link(&dev->kobj, "device");
55 out_busid:
56     if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
57         device_is_not_partition(dev))
58         sysfs_remove_link(&dev->class->p->class_subsys.kobj,
59                   dev_name(dev));
60 #else
61     /* link in the class directory pointing to the device */
62     error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
63                   &dev->kobj, dev_name(dev));
64     if (error)
65         goto out_subsys;
66  
67     if (dev->parent && device_is_not_partition(dev)) {
68         error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
69                       "device");
70         if (error)
71             goto out_busid;
72     }
73     return 0;
74  
75 out_busid:
76     sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev));
77 #endif
78  
79 out_subsys:
80     sysfs_remove_link(&dev->kobj, "subsystem");
81 out:
82     return error;
83 }

 

 6.2.5

同樣dev->class為空,什么都沒干。

下列代碼位於drivers/base/core.c。

 

 1 static int device_add_attrs(struct device *dev)
 2 {
 3     struct class *class = dev->class;
 4     struct device_type *type = dev->type;
 5     int error;
 6  
 7     if (class) {
 8         error = device_add_attributes(dev, class->dev_attrs);
 9         if (error)
10             return error;
11     }
12  
13     if (type) {
14         error = device_add_groups(dev, type->groups);
15         if (error)
16             goto err_remove_class_attrs;
17     }
18  
19     error = device_add_groups(dev, dev->groups);
20     if (error)
21         goto err_remove_type_groups;
22  
23     return 0;
24  
25  err_remove_type_groups:
26     if (type)
27         device_remove_groups(dev, type->groups);
28  err_remove_class_attrs:
29     if (class)
30         device_remove_attributes(dev, class->dev_attrs);
31  
32     return error;
33 }

 

6.2.6bus_add_device 

由於dev->bus未指定,因此這個函數什么都沒干。

該函數將創建三個symlink,在sysfs中建立總線和設備間的關系。

下列代碼位於drivers/base/bus.c。

 

 1 int bus_add_device(struct device *dev)
 2 {
 3     struct bus_type *bus = bus_get(dev->bus);
 4     int error = 0;
 5  
 6     if (bus) {
 7         pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
 8         error = device_add_attrs(bus, dev);
 9         if (error)
10             goto out_put;
11         
12         /*在sys/bus/XXX/devices下建立symlink,名字為設備名,該鏈接指向/sys/devices/下的某個目錄*/
13         error = sysfs_create_link(&bus->p->devices_kset->kobj,
14                         &dev->kobj, dev_name(dev));
15         if (error)
16             goto out_id;
17         
18         /*在sys/devices/的某個目錄下建立symlink,名字為subsystem,該鏈接指向/sys/bus/下的某個目錄*/
19         error = sysfs_create_link(&dev->kobj,
20                 &dev->bus->p->subsys.kobj, "subsystem");
21         if (error)
22             goto out_subsys;
23         
24         /*在sys/devices/的某個目錄下建立symlink,名字為bus,該鏈接指向/sys/bus/下的某個目錄*/
25         error = make_deprecated_bus_links(dev);
26         if (error)
27             goto out_deprecated;
28     }
29     return 0;
30  
31 out_deprecated:
32     sysfs_remove_link(&dev->kobj, "subsystem");
33 out_subsys:
34     sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
35 out_id:
36     device_remove_attrs(bus, dev);
37 out_put:
38     bus_put(dev->bus);
39     return error;
40 }

 

 6.2.7

下列代碼位於drivers/base/power/sysfs.c。

 1 int dpm_sysfs_add(struct device * dev)
 2 {
 3     return sysfs_create_group(&dev->kobj, &pm_attr_group);
 4 }
 5  
 6 static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
 7  
 8  
 9 static struct attribute * power_attrs[] = {
10     &dev_attr_wakeup.attr,
11     NULL,
12 };
13 static struct attribute_group pm_attr_group = {
14     .name    = "power",
15     .attrs    = power_attrs,
16 };

該函數將在XXX目錄下建立power子目錄,並在該子目錄下建立屬性文件wakeup。

在本例中,將在/sys/bus/platform下建立子目錄power並在子目錄下建立wakeup文件。

6.2.8

下列代碼位於drivers/base/power/main.c。該函數只是將設備添加到電源管理鏈表中。

 1 /**
 2  *    device_pm_add - add a device to the list of active devices
 3  *    @dev:    Device to be added to the list
 4  */
 5 void device_pm_add(struct device *dev)
 6 {
 7     pr_debug("PM: Adding info for %s:%s\n",
 8          dev->bus ? dev->bus->name : "No Bus",
 9          kobject_name(&dev->kobj));
10     mutex_lock(&dpm_list_mtx);
11     if (dev->parent) {
12         if (dev->parent->power.status >= DPM_SUSPENDING)
13             dev_warn(dev, "parent %s should not be sleeping\n",
14                  dev_name(dev->parent));
15     } else if (transition_started) {
16         /*
17          * We refuse to register parentless devices while a PM
18          * transition is in progress in order to avoid leaving them
19          * unhandled down the road
20          */
21         dev_WARN(dev, "Parentless device registered during a PM transaction\n");
22     }
23  
24     list_add_tail(&dev->power.entry, &dpm_list); /*將該設備添加到鏈表中*/
25     mutex_unlock(&dpm_list_mtx);
26 }

6.2.9

 1 void bus_attach_device(struct device *dev)
 2 {
 3     struct bus_type *bus = dev->bus;
 4     int ret = 0;
 5  
 6     if (bus) {
 7         if (bus->p->drivers_autoprobe)
 8             ret = device_attach(dev);    /*嘗試獲取驅動*/
 9         WARN_ON(ret < 0);
10         if (ret >= 0)        /*將設備掛在到總線中*/
11             klist_add_tail(&dev->p->knode_bus,
12                        &bus->p->klist_devices);
13     }
14 }
15  
16 /**
17  * device_attach - try to attach device to a driver.
18  * @dev: device.
19  *
20  * Walk the list of drivers that the bus has and call
21  * driver_probe_device() for each pair. If a compatible
22  * pair is found, break out and return.
23  *
24  * Returns 1 if the device was bound to a driver;
25  * 0 if no matching device was found;
26  * -ENODEV if the device is not registered.
27  *
28  * When called for a USB interface, @dev->parent->sem must be held.
29  */
30 int device_attach(struct device *dev)
31 {
32     int ret = 0;
33  
34     down(&dev->sem);
35     if (dev->driver) {    /*如果已指定驅動,即已綁定*/
36         ret = device_bind_driver(dev);    /*在sysfs中建立鏈接關系*/
37         if (ret == 0)
38             ret = 1;
39         else {
40             dev->driver = NULL;
41             ret = 0;
42         }
43     } else {        /*尚未綁定,嘗試綁定,遍歷該總線上的所有驅動*/
44         ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
45     }
46     up(&dev->sem);
47     return ret;
48 }

 

如果bus存在的話,將會調用device_attach函數進行綁定工作。該函數首先判斷dev->driver,如果非0,表示該設備已經綁定了驅動,只要在sysfs中建立鏈接關系即可。

為0表示沒有綁定,接着調用bus_for_each_drv,注意作為參數傳入的__device_attach,這是個函數,后面會調用它。

我們來看下bus_for_each_drv:

 1 int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
 2              void *data, int (*fn)(struct device_driver *, void *))
 3 {
 4     struct klist_iter i;
 5     struct device_driver *drv;
 6     int error = 0;
 7  
 8     if (!bus)
 9         return -EINVAL;
10  
11     klist_iter_init_node(&bus->p->klist_drivers, &i,
12                  start ? &start->p->knode_bus : NULL);
13     while ((drv = next_driver(&i)) && !error)
14         error = fn(drv, data);
15     klist_iter_exit(&i);
16     return error;
17 }

 

該函數將遍歷總線的drivers目錄下的所有驅動,也就是/sys/bus/XXX/drivers/下的目錄,為該driver調用fn函數,也就是__device_attach。我們來看下:

 1 static int __device_attach(struct device_driver *drv, void *data)
 2 {
 3     struct device *dev = data;
 4  
 5     if (!driver_match_device(drv, dev))   /*進行匹配工作*/
 6         return 0;
 7  
 8     return driver_probe_device(drv, dev);
 9 }
10  
11 static inline int driver_match_device(struct device_driver *drv,
12                       struct device *dev)
13 {
14     return drv->bus->match ? drv->bus->match(dev, drv) : 1;
15 } 
16  
17 /**
18  * driver_probe_device - attempt to bind device & driver together
19  * @drv: driver to bind a device to
20  * @dev: device to try to bind to the driver
21  *
22  * This function returns -ENODEV if the device is not registered,
23  * 1 if the device is bound sucessfully and 0 otherwise.
24  *
25  * This function must be called with @dev->sem held.  When called for a
26  * USB interface, @dev->parent->sem must be held as well.
27  */
28 int driver_probe_device(struct device_driver *drv, struct device *dev)
29 {
30     int ret = 0;
31  
32     if (!device_is_registered(dev))    /*該device是否已在sysfs中*/
33         return -ENODEV;
34  
35     pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
36          drv->bus->name, __func__, dev_name(dev), drv->name);
37  
38     ret = really_probe(dev, drv);/*device已在sysfs,調用really_probe*/    
39  
40     return ret;
41 }

 

該函數首先調用driver_match_device函數,后者將會調用總線的match方法,如果有的話,來進行匹配工作。如果沒有該方法,則返回1,表示匹配成功。

我們這里是針對platform總線,該總線的方法將在7.6.2節中看到。

隨后,又調用了driver_probe_device函數。該函數將首先判斷該device是否已在sysfs中,如果在則調用really_probe,否則返回出錯。

really_probe將會調用驅動的probe並完成綁定的工作。該函數將在7.6.2節中分析。

6.3.10總結

在本例中,當device_register調用完成以后,將在/sys/devices/下建立目錄platform,並在platfrom下建立屬性文件uevent和子目錄power,最后在power子目錄下建立wakeup屬性文件。

整個調用流程如下:

 

6.3 spi主控制器的平台設備
本節對一個特定的platform設備進行講解,那就是spi主控制器的平台設備。

在內核的啟動階段,platform設備將被注冊進內核。我們來看下。

下列代碼位於arch/arm/mach-s3c2440/mach-smdk2440.c

 1 static struct resource s3c_spi0_resource[] = {
 2     [0] = {
 3         .start = S3C24XX_PA_SPI,
 4         .end   = S3C24XX_PA_SPI + 0x1f,
 5         .flags = IORESOURCE_MEM,
 6     },
 7     [1] = {
 8         .start = IRQ_SPI0,
 9         .end   = IRQ_SPI0,
10         .flags = IORESOURCE_IRQ,
11     }
12  
13 };
14  
15 static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
16  
17 struct platform_device s3c_device_spi0 = {
18     .name          = "s3c2410-spi",
19     .id          = 0,
20     .num_resources      = ARRAY_SIZE(s3c_spi0_resource),
21     .resource      = s3c_spi0_resource,
22         .dev              = {
23                 .dma_mask = &s3c_device_spi0_dmamask,
24                 .coherent_dma_mask = 0xffffffffUL
25         }
26 };
27  
28 static struct platform_device *smdk2440_devices[] __initdata = {
29     &s3c_device_usb,
30     &s3c_device_lcd,
31     &s3c_device_wdt,
32     &s3c_device_i2c0,
33     &s3c_device_iis,
34     &s3c_device_spi0,
35 };
36  
37 static void __init smdk2440_machine_init(void)
38 {
39     s3c24xx_fb_set_platdata(&smdk2440_fb_info);
40     s3c_i2c0_set_platdata(NULL);
41  
42     platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
43     smdk_machine_init();
44 }

在smdk2440_machine_init函數中,通過調用platform_add_devices將設備注冊到內核中。接着來看下該函數。
6.3.1 platform_add_devices

 1 /**
 2  * platform_add_devices - add a numbers of platform devices
 3  * @devs: array of platform devices to add
 4  * @num: number of platform devices in array
 5  */
 6 int platform_add_devices(struct platform_device **devs, int num)
 7 {
 8     int i, ret = 0;
 9  
10     for (i = 0; i < num; i++) {
11         ret = platform_device_register(devs[i]);
12         if (ret) {
13             while (--i >= 0)
14                 platform_device_unregister(devs[i]);
15             break;
16         }
17     }
18  
19     return ret;
20 }

 

該函數將根據devs指針數組,調用platform_device_register將platform設備逐一注冊進內核。
6.3.2  platform_device_register

1 /**
2  * platform_device_register - add a platform-level device
3  * @pdev: platform device we're adding
4  */
5 int platform_device_register(struct platform_device *pdev)
6 {
7     device_initialize(&pdev->dev);
8     return platform_device_add(pdev);
9 }

調用了兩個函數,第一個函數在6.1節已經分析過。我們來看下第二個函數。
6.3.2  platform_device_register

 1 /**
 2  * platform_device_add - add a platform device to device hierarchy
 3  * @pdev: platform device we're adding
 4  *
 5  * This is part 2 of platform_device_register(), though may be called
 6  * separately _iff_ pdev was allocated by platform_device_alloc().
 7  */
 8 int platform_device_add(struct platform_device *pdev)
 9 {
10     int i, ret = 0;
11  
12     if (!pdev)
13         return -EINVAL;
14  
15     if (!pdev->dev.parent)
16         pdev->dev.parent = &platform_bus;    /*該設備的父設備是platform設備,/sys/devices/platform*/
17  
18     pdev->dev.bus = &platform_bus_type;        /*設備掛載到platform總線上*/
19  
20     if (pdev->id != -1)
21         dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
22     else
23         dev_set_name(&pdev->dev, pdev->name);/*pdev->dev->kobj->name = pdev->name*/
24  
25     /*遍歷平台設備的資源,並將資源添加到資源樹中*/
26     for (i = 0; i < pdev->num_resources; i++) {
27         struct resource *p, *r = &pdev->resource[i];
28  
29         if (r->name == NULL)
30             r->name = dev_name(&pdev->dev);    /*獲取dev->kobject->name*/
31  
32         p = r->parent;
33         if (!p) {    /*p空*/
34             if (resource_type(r) == IORESOURCE_MEM)
35                 p = &iomem_resource;
36             else if (resource_type(r) == IORESOURCE_IO)
37                 p = &ioport_resource;
38         }
39  
40         if (p && insert_resource(p, r)) {    /*將資源添加到資源樹中*/
41             printk(KERN_ERR
42                    "%s: failed to claim resource %d\n",
43                    dev_name(&pdev->dev), i);
44             ret = -EBUSY;
45             goto failed;
46         }
47     }
48  
49     pr_debug("Registering platform device '%s'. Parent at %s\n",
50          dev_name(&pdev->dev), dev_name(pdev->dev.parent));
51  
52     ret = device_add(&pdev->dev);    /*添加設備*/
53     if (ret == 0)
54         return ret;
55  
56  failed:
57     while (--i >= 0) {
58         struct resource *r = &pdev->resource[i];
59         unsigned long type = resource_type(r);
60  
61         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
62             release_resource(r);
63     }
64  
65     return ret;
66 }

在這個函數的最后赫然出現了device_add函數。我們回憶下在6.1節中device_register的注冊過程,該函數只調用了兩個函數,一個是device_initialize函數,另一個就是device_add。
本節的platform_device_register函數,首先也是調用了device_initialize,但是隨后他做了一些其他的工作,最后調用了device_add。

那么這個"其他的工作"干了些什么呢?

首先,它將該SPI主控制對應的平台設備的父設備設為虛擬的platform設備(platform_bus),然后將該平台設備掛在至platform總線(platform_bus_type)上,這兩步尤為重要,后面我們將看到。
然后,調用了dev_set_name設置了pdev->dev-kobj.name,也就是該設備對象的名字,這里的名字為s3c2410-spi.0,這個名字將被用來建立一個目錄。

最后,將平台的相關資源添加到資源樹中。這不是本篇文章討論的重點所在,所以不做過多說明。

在"其他的工作""干完之后,調用了device_add函數。那么后面的函數調用過程將和6.2小結的一致。

由於“其他的工作”的原因,實際執行的過程和結果將有所區別。我們來分析下。

6.3.3 不一樣device_add調用結果
首先,在device_add被調用之前,有若干個非常重要的條件已經被設置了。如下:

pdev->dev->kobj.kset = devices_kset

pdev->dev-.parent = &platform_bus

pdev->dev.bus = &platform_bus_type

set_up函數執行時,由於參數parent為&platform_bus,因此最后將設置pdev->dev->kobj.parent = platform_bus.kobj。平台設備對象的父對象為虛擬的platform設備。

kobject_add函數執行時,由於參數parent的存在,將在parent對象所對應的目錄下創建另一個目錄。parent對象代表目錄/sys/devices/下的platform,因此將在/sys/devices/platform下建立目錄s3c2410-spi.0。

device_create_file建立屬性文件uevent。
bus_add_device函數執行時,由於dev.bus 為&platform_bus_type,因此將建立三個symlink。

            /sys/devices/platform/s3c2410-spi.0下建立鏈接subsystem和bus,他們指向/sys/bus/platform。

           /sys/bus/platform/devices/下建立鏈接s3c2410-spi.0,指向/sys/devices/platform/s3c2410-spi.0。

dpm_sysfs_add函數在/sys/devices/platform/s3c2410-spi.0下建立子目錄power,並在該子目錄下建立屬性文件wakeup。

執行到這里時,sysfs已將內核中新添加的SPI主控制器平台設備呈現出來了,我們來驗證下。

[root@yj423 s3c2410-spi.0]#pwd
/sys/devices/platform/s3c2410-spi.0
[root@yj423 s3c2410-spi.0]#ll
lrwxrwxrwx    1 root     root             0 Jan  1 00:29 bus -> ../../../bus/platform
lrwxrwxrwx    1 root     root             0 Jan  1 00:29 driver -> ../../../bus/platform/drivers/s3c2410-spi
-r--r--r--    1 root     root          4096 Jan  1 00:29 modalias
drwxr-xr-x    2 root     root             0 Jan  1 00:29 power
drwxr-xr-x    3 root     root             0 Jan  1 00:00 spi0.0
drwxr-xr-x    3 root     root             0 Jan  1 00:00 spi0.1
lrwxrwxrwx    1 root     root             0 Jan  1 00:29 spi_master:spi0 -> ../../../class/spi_master/spi0
lrwxrwxrwx    1 root     root             0 Jan  1 00:29 subsystem -> ../../../bus/platform
-rw-r--r--    1 root     root          4096 Jan  1 00:29 uevent

[root@yj423 devices]#pwd
/sys/bus/platform/devices
[root@yj423 devices]#ll s3c2410-spi.0
lrwxrwxrwx    1 root     root             0 Jan  1 00:44 s3c2410-spi.0 -> ../../../devices/platform/s3c2410-spi.0

通過sysfs將設備驅動的模型層次呈現在用戶空間以后,將更新內核的設備模型之間的關系,這是通過修改鏈表的指向來完成的。

bus_attach_device函數執行時,將設備添加到總線的設備鏈表中,同時也會嘗試綁定驅動,不過會失敗。

接着,由於dev->parent的存在,將SPI主控制器設備添加到父設備platform虛擬設備的兒子鏈表中。

7. driver舉例
我們已經介紹過platform總線的注冊,也講述了SPI主控制器設備作為平台設備的注冊過程,在本節,將描述SPI主控制器的platform驅動是如何注冊的。

7.1 s3c24xx_spi_init
下列代碼位於drivers/spi/spi_s3c24xx.c。

 1 MODULE_ALIAS("platform:s3c2410-spi");
 2 static struct platform_driver s3c24xx_spi_driver = {
 3     .remove        = __exit_p(s3c24xx_spi_remove),
 4     .suspend    = s3c24xx_spi_suspend,
 5     .resume        = s3c24xx_spi_resume,
 6     .driver        = {
 7         .name    = "s3c2410-spi",
 8         .owner    = THIS_MODULE,
 9     },
10 };
11  
12 static int __init s3c24xx_spi_init(void)
13 {
14         return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);//設備不可熱插拔,所以使用該函數,而不是platform_driver_register
15

驅動注冊通過調用platform_driver_probe來完成。
注意:driver.name字段使用來匹配設備的,該字段必須和6.3節一開始給出的pdev.name字段相同。
7.2  platform_driver_probe
下列代碼位於drivers/base/platform.c。

 1 /**
 2  * platform_driver_probe - register driver for non-hotpluggable device
 3  * @drv: platform driver structure
 4  * @probe: the driver probe routine, probably from an __init section
 5  *
 6  * Use this instead of platform_driver_register() when you know the device
 7  * is not hotpluggable and has already been registered, and you want to
 8  * remove its run-once probe() infrastructure from memory after the driver
 9  * has bound to the device.
10  *
11  * One typical use for this would be with drivers for controllers integrated
12  * into system-on-chip processors, where the controller devices have been
13  * configured as part of board setup.
14  *
15  * Returns zero if the driver registered and bound to a device, else returns
16  * a negative error code and with the driver not registered.
17  */
18 int __init_or_module platform_driver_probe(struct platform_driver *drv,
19         int (*probe)(struct platform_device *))
20 {
21     int retval, code;
22  
23     /* temporary section violation during probe() */
24     drv->probe = probe;
25     retval = code = platform_driver_register(drv); /*注冊platform驅動*/
26  
27     /* Fixup that section violation, being paranoid about code scanning
28      * the list of drivers in order to probe new devices.  Check to see
29      * if the probe was successful, and make sure any forced probes of
30      * new devices fail.
31      */
32     spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
33     drv->probe = NULL;
34     if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
35         retval = -ENODEV;
36     drv->driver.probe = platform_drv_probe_fail;
37     spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);
38  
39     if (code != retval)
40         platform_driver_unregister(drv);
41     return retval;
42 }

這里的重點是platform_driver_register,由它來完成了platform驅動的注冊。
7.3 platform_driver_register

 1 /**
 2  * platform_driver_register
 3  * @drv: platform driver structure
 4  */
 5 int platform_driver_register(struct platform_driver *drv)
 6 {
 7     drv->driver.bus = &platform_bus_type;//驅動掛載的總線
 8     if (drv->probe)
 9         drv->driver.probe = platform_drv_probe;
10     if (drv->remove)
11         drv->driver.remove = platform_drv_remove;
12     if (drv->shutdown)
13         drv->driver.shutdown = platform_drv_shutdown;
14     if (drv->suspend)
15         drv->driver.suspend = platform_drv_suspend;
16     if (drv->resume)
17         drv->driver.resume = platform_drv_resume;
18     return driver_register(&drv->driver); /*驅動注冊*/
19 }

7.4driver_register

driver_register以及其他driver的api詳解:linux設備驅動(3)devive_driver 詳解

driver_register函數就是driver注冊的核心函數。需要注意的是,在調用函數之前,將該驅動所掛載的總線設置為platform總線(platform_bus_type)。

 1 /**
 2  * driver_register - register driver with bus
 3  * @drv: driver to register
 4  *
 5  * We pass off most of the work to the bus_add_driver() call,
 6  * since most of the things we have to do deal with the bus
 7  * structures.
 8  */
 9 int driver_register(struct device_driver *drv)
10 {
11     int ret;
12     struct device_driver *other;
13  
14     BUG_ON(!drv->bus->p);
15  
16     if ((drv->bus->probe && drv->probe) ||
17         (drv->bus->remove && drv->remove) ||
18         (drv->bus->shutdown && drv->shutdown))
19         printk(KERN_WARNING "Driver '%s' needs updating - please use "
20             "bus_type methods\n", drv->name);
21  
22     other = driver_find(drv->name, drv->bus);/*用驅動名字來搜索在該總線上驅動是否已經存在*/
23     if (other) {    /*存在則報錯*/
24         put_driver(other);
25         printk(KERN_ERR "Error: Driver '%s' is already registered, "
26             "aborting...\n", drv->name);
27         return -EEXIST;
28     }
29  
30     ret = bus_add_driver(drv);    /*將驅動添加到一個總線中*/
31     if (ret)
32         return ret;
33     ret = driver_add_groups(drv, drv->groups); /*建立屬性組文件*/
34     if (ret)
35         bus_remove_driver(drv);
36     return ret;
37 }

這里主要調用兩個函數driver_find和bus_add_driver。前者將通過總線來搜索該驅動是否存在,后者將添加驅動到總線中。

接下來就分析這兩個函數。

7.5driver_find

 1 /**
 2  * driver_find - locate driver on a bus by its name.
 3  * @name: name of the driver.
 4  * @bus: bus to scan for the driver.
 5  *
 6  * Call kset_find_obj() to iterate over list of drivers on
 7  * a bus to find driver by name. Return driver if found.
 8  *
 9  * Note that kset_find_obj increments driver's reference count.
10  */
11 struct device_driver *driver_find(const char *name, struct bus_type *bus)
12 {
13     struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
14     struct driver_private *priv;
15  
16     if (k) {
17         priv = to_driver(k);
18         return priv->driver;
19     }
20     return NULL;
21 }
22 
23 
24 /**
25  * kset_find_obj - search for object in kset.
26  * @kset: kset we're looking in.
27  * @name: object's name.
28  *
29  * Lock kset via @kset->subsys, and iterate over @kset->list,
30  * looking for a matching kobject. If matching object is found
31  * take a reference and return the object.
32  */
33 struct kobject *kset_find_obj(struct kset *kset, const char *name)
34 {
35     struct kobject *k;
36     struct kobject *ret = NULL;
37  
38     spin_lock(&kset->list_lock);
39     list_for_each_entry(k, &kset->list, entry) {
40         if (kobject_name(k) && !strcmp(kobject_name(k), name)) {
41             ret = kobject_get(k);
42             break;
43         }
44     }
45     spin_unlock(&kset->list_lock);
46     return ret;
47 }

這里調用了kset_find_obj函數,傳入的實參bus->p->drivers_kset,它對應的就是/sys/bus/platform/下的drivers目錄,然后通過鏈表,它將搜索該目錄下的所有文件,來尋找是否有名為s3c2410-spi的文件。還記得嗎? kobject就是一個文件對象。如果沒有找到將返回NULL,接着將調用bus_add_driver把驅動注冊進內核。
7.6bus_add_driver

 1 /**
 2  * bus_add_driver - Add a driver to the bus.
 3  * @drv: driver.
 4  */
 5 int bus_add_driver(struct device_driver *drv)
 6 {
 7     struct bus_type *bus;
 8     struct driver_private *priv;
 9     int error = 0;
10  
11     bus = bus_get(drv->bus);    /*增加引用計數獲取bus_type*/
12     if (!bus)
13         return -EINVAL;
14  
15     pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
16  
17     priv = kzalloc(sizeof(*priv), GFP_KERNEL);    /*分配driver_private結構體*/
18     if (!priv) {
19         error = -ENOMEM;
20         goto out_put_bus;
21     }
22     /*初始化內核鏈表*/
23     klist_init(&priv->klist_devices, NULL, NULL);
24     /*相互保存*/
25     priv->driver = drv;
26     drv->p = priv;
27     /*設置該kobj屬於那個kset*/
28     priv->kobj.kset = bus->p->drivers_kset;
29     error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,    /*parent=NULL*/
30                      "%s", drv->name);    /*執行完以后,會在bus/總線名/drivers/下建立名為drv->name的目錄*/
31     if (error)
32         goto out_unregister;
33  
34     if (drv->bus->p->drivers_autoprobe) {
35         error = driver_attach(drv);    /*嘗試綁定驅動和設備*/
36         if (error)
37             goto out_unregister;
38     }
39     /*添加該驅動到bus的內核鏈表中*/
40     klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
41     module_add_driver(drv->owner, drv);/*?????????*/
42  
43     /*創建屬性,在bus/總線名/drivers/驅動名/下建立文件uevent*/
44     error = driver_create_file(drv, &driver_attr_uevent);
45     if (error) {
46         printk(KERN_ERR "%s: uevent attr (%s) failed\n",
47             __func__, drv->name);
48     }
49     /*利用bus->drv_attrs創建屬性,位於bus/總線名/drivers/驅動名/*/
50     error = driver_add_attrs(bus, drv);
51     if (error) {
52         /* How the hell do we get out of this pickle? Give up */
53         printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
54             __func__, drv->name);
55     }
56     /*創建屬性,在bus/總線名/drivers/驅動名/下建立文件bind和unbind*/
57     error = add_bind_files(drv);
58     if (error) {
59         /* Ditto */
60         printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
61             __func__, drv->name);
62     }
63     /*通知用戶空間???*/
64     kobject_uevent(&priv->kobj, KOBJ_ADD);
65     return 0;
66 out_unregister:
67     kfree(drv->p);
68     drv->p = NULL;
69     kobject_put(&priv->kobj);
70 out_put_bus:
71     bus_put(bus);
72     return error;
73 }

7.6.1

下列代碼位於lib/kobject.c。

 1 /**
 2  * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
 3  * @kobj: pointer to the kobject to initialize
 4  * @ktype: pointer to the ktype for this kobject.
 5  * @parent: pointer to the parent of this kobject.
 6  * @fmt: the name of the kobject.
 7  *
 8  * This function combines the call to kobject_init() and
 9  * kobject_add().  The same type of error handling after a call to
10  * kobject_add() and kobject lifetime rules are the same here.
11  */
12 int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
13              struct kobject *parent, const char *fmt, ...)
14 {
15     va_list args;
16     int retval;
17  
18     kobject_init(kobj, ktype);
19  
20     va_start(args, fmt);
21     retval = kobject_add_varg(kobj, parent, fmt, args);
22     va_end(args);
23  
24     return retval;
25 }

該函數中調用了兩個函數,這兩個函數分別在6.1.2和6.2.2中講述過,這里不再贅述。
調用該函數時由於parent為NULL,但kobj.kset為drivers目錄,所以將在/sys/bus/platform/drivers/下建立目錄,名為s3c2410-spi。

我們來驗證下:

[root@yj423 s3c2410-spi]#pwd
/sys/bus/platform/drivers/s3c2410-spi

接着由於drivers_autoprobe在bus_register執行的時候已經置1,將調用driver_attach。

7.6.2

下列代碼位於drivers/base/dd.c。

 1 /**
 2  * driver_attach - try to bind driver to devices.
 3  * @drv: driver.
 4  *
 5  * Walk the list of devices that the bus has on it and try to
 6  * match the driver with each one.  If driver_probe_device()
 7  * returns 0 and the @dev->driver is set, we've found a
 8  * compatible pair.
 9  */
10 int driver_attach(struct device_driver *drv)
11 {
12     return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
13 }

該函數將調用bus_for_each_dev來尋找總線上的每個設備,這里的總線即為platform總線,然后嘗試綁定設備。

這里需要注意的是最后一個參數__driver_attach,這是一個函數名,后面將會調用它。

 1 int bus_for_each_dev(struct bus_type *bus, struct device *start,
 2              void *data, int (*fn)(struct device *, void *))
 3 {
 4     struct klist_iter i;
 5     struct device *dev;
 6     int error = 0;
 7  
 8     if (!bus)
 9         return -EINVAL;
10  
11     klist_iter_init_node(&bus->p->klist_devices, &i,
12                  (start ? &start->p->knode_bus : NULL));
13     while ((dev = next_device(&i)) && !error)
14         error = fn(dev, data);
15     klist_iter_exit(&i);
16     return error;
17 }

通過klist將遍歷該總線上的所有設備,並為其調用__driver_attach函數。

 1 static int __driver_attach(struct device *dev, void *data)
 2 {
 3     struct device_driver *drv = data;
 4  
 5     /*
 6      * Lock device and try to bind to it. We drop the error
 7      * here and always return 0, because we need to keep trying
 8      * to bind to devices and some drivers will return an error
 9      * simply if it didn't support the device.
10      *
11      * driver_probe_device() will spit a warning if there
12      * is an error.
13      */
14  
15     if (!driver_match_device(drv, dev))
16         return 0;
17  
18     if (dev->parent)    /* Needed for USB */
19         down(&dev->parent->sem);
20     down(&dev->sem);
21     if (!dev->driver)
22         driver_probe_device(drv, dev);
23     up(&dev->sem);
24     if (dev->parent)
25         up(&dev->parent->sem);
26  
27     return 0;
28 }

首先調用了driver_match_device函數,該函數進會進行匹配,如果匹配成功將返回1。我們看下這個函數:

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 }

這里直接調用了platform總線的match方法,我們來看下這個方法。

 

 1 /**
 2  * platform_match - bind platform device to platform driver.
 3  * @dev: device.
 4  * @drv: driver.
 5  *
 6  * Platform device IDs are assumed to be encoded like this:
 7  * "<name><instance>", where <name> is a short description of the type of
 8  * device, like "pci" or "floppy", and <instance> is the enumerated
 9  * instance of the device, like '0' or '42'.  Driver IDs are simply
10  * "<name>".  So, extract the <name> from the platform_device structure,
11  * and compare it against the name of the driver. Return whether they match
12  * or not.
13  */
14 static int platform_match(struct device *dev, struct device_driver *drv)
15 {
16     struct platform_device *pdev = to_platform_device(dev);
17     struct platform_driver *pdrv = to_platform_driver(drv);
18  
19     /* match against the id table first */
20     if (pdrv->id_table)
21         return platform_match_id(pdrv->id_table, pdev) != NULL;
22  
23     /* fall-back to driver name match */
24     return (strcmp(pdev->name, drv->name) == 0);
25 }

該方法的核心其實就是使用stcmp進行字符匹配,判斷pdev->name和drv->name是否相等。

 在本例中兩者同為s3c2410-spi。因此匹配完成,返回1。

返回后,由於dev->driver為NULL,將調用driver_probe_device函數。我們來看下:

 1 int driver_probe_device(struct device_driver *drv, struct device *dev)
 2 {
 3     int ret = 0;
 4  
 5     if (!device_is_registered(dev))
 6         return -ENODEV;
 7  
 8     pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 9          drv->bus->name, __func__, dev_name(dev), drv->name);
10  
11     ret = really_probe(dev, drv);
12  
13     return ret;
14 }
15 static inline int device_is_registered(struct device *dev)
16 {
17     return dev->kobj.state_in_sysfs;
18 }

該函數將調用really_probe來綁定設備和它的驅動。

 1 static int really_probe(struct device *dev, struct device_driver *drv)
 2 {
 3     int ret = 0;
 4  
 5     atomic_inc(&probe_count);
 6     pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
 7          drv->bus->name, __func__, drv->name, dev_name(dev));
 8     WARN_ON(!list_empty(&dev->devres_head));
 9  
10     dev->driver = drv;
11     if (driver_sysfs_add(dev)) {    /*創建兩個symlink,更新sysfs*/
12         printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
13             __func__, dev_name(dev));
14         goto probe_failed;
15     }
16  
17     if (dev->bus->probe) {
18         ret = dev->bus->probe(dev);/*調用總線的probe方法*/
19         if (ret)
20             goto probe_failed;
21     } else if (drv->probe) {
22         ret = drv->probe(dev);    /*調用驅動的probe方法*/
23         if (ret)
24             goto probe_failed;
25     }
26  
27     driver_bound(dev);              /*綁定設備和驅動*/
28     ret = 1;
29     pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
30          drv->bus->name, __func__, dev_name(dev), drv->name);
31     goto done;
32  
33 probe_failed:
34     devres_release_all(dev);
35     driver_sysfs_remove(dev);
36     dev->driver = NULL;
37  
38     if (ret != -ENODEV && ret != -ENXIO) {
39         /* driver matched but the probe failed */
40         printk(KERN_WARNING
41                "%s: probe of %s failed with error %d\n",
42                drv->name, dev_name(dev), ret);
43     }
44     /*
45      * Ignore errors returned by ->probe so that the next driver can try
46      * its luck.
47      */
48     ret = 0;
49 done:
50     atomic_dec(&probe_count);
51     wake_up(&probe_waitqueue);
52     return ret;
53 }

在這個函數中調用4個函數。

第一個函數driver_sysfs_add將更新sysfs。

 1 static int driver_sysfs_add(struct device *dev)
 2 {
 3     int ret;
 4     /* 在/sys/bus/XXX/drivers/XXX目錄下建立symlink,鏈接名為kobj->name,
 5        鏈接指向/sys/devices/platform/XXX */
 6     ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
 7               kobject_name(&dev->kobj));
 8     if (ret == 0) {
 9         /* 在/sys/devices/platform/XXX/下建立symlink,鏈接名為driver,
10           指向/sys/bus/xxx/drivers目錄下的某個目錄*/
11         ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
12                     "driver");
13         if (ret)
14             sysfs_remove_link(&dev->driver->p->kobj,
15                     kobject_name(&dev->kobj));
16     }
17     return ret;
18 }

執行完以后,建立了兩個鏈接。
在/sys/bus/platform/drivers/s3c2410-spi下建立鏈接,指向/sys/devices/platform/s3c2410-spi.0
在/sys/devices/platform/s3c2410-spi.0下建立鏈接,指向/sys/devices/platform/s3c2410-spi.0。
這樣就在用戶空間呈現出驅動和設備的關系了。我們來驗證下。

[root@yj423 s3c2410-spi]#pwd
/sys/bus/platform/drivers/s3c2410-spi
[root@yj423 s3c2410-spi]#ll s3c2410-spi.0
lrwxrwxrwx    1 root     root             0 Jan  1 02:28 s3c2410-spi.0 -> ../../../../devices/platform/s3c2410-spi.0
[root@yj423 s3c2410-spi.0]#pwd
/sys/devices/platform/s3c2410-spi.0
[root@yj423 s3c2410-spi.0]#ll driver
lrwxrwxrwx    1 root     root             0 Jan  1 02:26 driver -> ../../../bus/platform/drivers/s3c2410-spi

第2個函數執行總線的probe方法,由於platform總線沒有提供probe方法,因此不執行。

第3個函數執行驅動的probe方法,驅動提供了probe,因此調用它,該函數的細節超過了本文的討論內容,所以略過。

第4個函數執行driver_bound,用來綁定設備和驅動,來看下這個函數。

 1 static void driver_bound(struct device *dev)
 2 {
 3     if (klist_node_attached(&dev->p->knode_driver)) {
 4         printk(KERN_WARNING "%s: device %s already bound\n",
 5             __func__, kobject_name(&dev->kobj));
 6         return;
 7     }
 8  
 9     pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
10          __func__, dev->driver->name);
11  
12     if (dev->bus)
13         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
14                          BUS_NOTIFY_BOUND_DRIVER, dev);
15  
16     klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
17 }

其實,所謂的綁定,就是將設備的驅動節點添加到驅動支持的設備鏈表中。
至此,通過內核鏈表,這個platform device 和platform driver 已經綁定完成,將繼續遍歷內核鏈表嘗試匹配和綁定,直到鏈表結束。

在driver_attach執行完畢以后,bus_add_driver函數還有些剩余工作要完成。

首先,將驅動添加到總線的驅動列表中。
接着,如果定義了驅動屬性文件,則創建。
最后,在/sys/bus/platform/drivers/s3c2410-spi/下建立屬性文件uevent,並在同一目錄下建立文件bind和unbind。

我們來驗證下:

[root@yj423 s3c2410-spi]#pwd
/sys/bus/platform/drivers/s3c2410-spi
[root@yj423 s3c2410-spi]#ls
bind           s3c2410-spi.0  uevent         unbind

 

7.7小結

在本節中,我們看到了platform driver是如何注冊到內核中,在注冊過程中,通過更新了sysfs,向用戶空間展示總線,設備和驅動之間的關系。

同時,還更新了鏈表的指向,在內核中體現了同樣的關系。

最后以platform driver的注冊過程總結如下:

 

參考博文:https://blog.csdn.net/yj4231/java/article/details/7799245


免責聲明!

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



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