本文將對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,
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