一、kobject的定義:
kobject是Linux2.6引入的設備管理機制,在內核中由struct kobject結構表示,這個結構使所有設備在底層都具有統一的接口.kobject提供了基本的對象管理能力,是構成Linux2.6設備模型的核心結構,它與sysfs文件系統緊密聯系,每個在內核中注冊kobject對象都對應與sysfs文件系統中的一個目錄;kobject--->sysfs.dir;其結構定義為:
struct kobject
{
? const char*??????? k_name;????????????? //指向設備名稱的指針
? char?????????????? name[KOBJ_NAME_LEN]; //設備名稱
? struct kref??????? kref;??????????????? //內核對象的引用計數
? struct list_head?? entry;?????????????? //掛接到當前內核對象所在kset中的單元
? struct kobject*??? parent;????????????? //指向父對象的指針
? struct kset*?????? kset;??????????????? //內核對象所屬kset的指針
? struct kobj_type*? ktype;?????????????? //指向內核對象類型描述符的指針
? struct dentry*???? dentry;????????????? //sysfs文件系統中與該內核對象對應的文件節點路徑的指針
? wait_queue_head_t? poll;??????????????? //IO等待隊列;
};
二、kobject相關函數:
1、void kobject_init(struct kobject* kobj);
?? 該函數用于初始化kobject對象,它設置kobject對象的引用計數為1,entry字段指向自身,其所屬kset對象的引用計數加1;
2、void kobject_cleanup(struct kobject* kobj);
?? void kobject_release(struct kref* ref);
?? 這兩個函數用于清除kobject對象,當其引用計數為0時,釋放對象所占用的資源;
3、int kobject_set_name(struct kobject* kobj, const char* format, ...);
?? 該函數用于設置指定kobject對象的名稱;
4、const char* kobject_name(const struct kobject* kobj);
?? 該函數用于返回指定kobject的名稱;
5、int kobject_rename(struct kobject* kobj, const char* new_name);
?? 該函數用于為指定kobject對象重命名;
6、struct kobject* kobject_get(struct kobject* kobj);
?? 該函數用于將kobject對象的引用計數加1,相當于申請了一個kobject對象資源,同時返回該kobject對象的指針;
7、void kobject_put(struct kobject* kobj);
?? 該函數用于將kobject對象的引用計數減1,相當于釋放了一個kobject對象資源;當引用計數為0時,則調用kobject_release()釋放該kobject對象的資源;
8、int kobject_add(struct kobject* kobj);
?? 該函數用于注冊kobject對象,即:加入到Linux的設備層次中,它會掛接該kobject對象到kset的list鏈中,增加父目錄各級kobject對象的引用計數,在其parent字段指向的目錄下創建對應的文件節點,并啟動該類型kobject對象的hotplug()函數;
9、void kobject_del(struct kobject* kobj);
?? 該函數與kobject_add()相反,用于注銷kobject對象,即:中止該kobject對象的hotplug()函數,從Linux的設備層次中刪除該kobject對象,刪除該kobject對象在sysfs文件系統中對應的文件節點;
10、int kobject_register(struct kobject* obj);
??? 該函數用于注冊kobject對象,它首先會調用kobject_init()初始化kobj,然后再調用kobject_add()完成該內核對象的添加;
11、void kobject_unregister(struct kobject* kobj);
??? 該函數與kobject_register()相反,用于注銷kobject對象,它首先調用kobject_del()從Linux的設備層次中刪除kobject對象,再調用kobject_put()減少該kobject對象的引用計數,當引用計數為0時,則釋放該kobject對象的資源;
12、struct kobject* kobject_add_dir(struct kobject*, const char* path);
??? 該函數用于在sysfs文件系統中為該kobject對象創建對應的目錄;
13、char* kobject_get_path(struct kobject* kobj);
??? 該函數用于返回該kobject對象在sysfs文件系統中的對應目錄路徑;
三、kobject的行為:
typedef int __bitwise kobject_action_t;
enum kobject_action
{
? KOBJ_ADD???? = (__force kobject_action_t) 0x01, //exclusive to core
? KOBJ_REMOVE? = (__force kobject_action_t) 0x02, //exclusive to core
? KOBJ_CHANGE? = (__force kobject_action_t) 0x03, //device state change
? KOBJ_MOUNT?? = (__force kobject_action_t) 0x04, //mount event for block devices (broken)
? KOBJ_UMOUNT? = (__force kobject_action_t) 0x05, //umount event for block devices (broken)
? KOBJ_OFFLINE = (__force kobject_action_t) 0x06, //device offline
? KOBJ_ONLINE? = (__force kobject_action_t) 0x07, //device online
};
該枚舉類型用于定義kobject對象的狀態更新消息碼,也就是熱插拔事件碼;
備注:struct kobject結構定義于文件include/linux/kobject.h
下面轉自:/u1/57901/showart_1803248.html
在LINUX中最讓人不解的大概就是/sys下面的內容了
下面首先讓我們來創建一個簡單的platform設備,并從這個設備的視角進行深入,在此篇文章的深入過程中,我們只看kobeject的模型我所使用的內核版本號為2.6.26,操作系統的內核版本號為2.6.27-7,暫未發現2.6.27-7與2.6.26的重大不同
首先寫一個簡單的模塊
#include
#include
#include
static int __init test_probe(struct platform_device *pdev)
{
int err = 0;
return err;
}
static int test_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_device test_device = {
.name = "test_ts",
.id = -1,
};
static struct platform_driver test_driver = {
.probe ? ? ? ? ? ? ? ?= test_probe,
.remove ? ? ? ? ? ? ? ?= test_remove,
.driver ? ? ? ? ? ? ? ?= {
.name ? ? ? ?= "test_ts",
.owner ? ? ? ?= THIS_MODULE,
},
};
static int __devinit test_init(void)
{
platform_device_register(&test_device); ? ? ? ?
return platform_driver_register(&test_driver);
}
static void __exit test_exit(void)
{
platform_device_unregister(&test_device);
platform_driver_unregister(&test_driver);
}
module_init(test_init);
module_exit(test_exit);
MODULE_AUTHOR("zwolf");
MODULE_DESCRIPTION("Module test");
MODULE_LICENSE("GPL");
MODULE_ALIAS("test");
接下來是makefile
#Makefile
obj-m:=test.o
KDIR:=/lib/modules/2.6.27-7-generic/build
PWD:=$(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
KDIR中的目錄請改為各位實際運行中的內核目錄make之后進行模塊的加載 sudo insmod ./test.ko
現在到sys目錄中查看我們的設備是否已經加載上了
首先是/sys/bus/platform/devices/在devices下,每一個連接文件都代表了一個設備ls可看見test_ts,進入test_ts,ls可發現driver這個鏈接文件,ls-l查看,發現這個文件是連到/sys/bus/platform/drivers/test_ts的
這里需要說明的是連接的含義,并不是driver驅動存在于test_ts這個設備中,而是test_ts使用的驅動為/sys/bus/platform/drivers/test_ts
現在換到/sys/bus/platform/drivers這個目錄下
ls查看會發現這里的文件都為目錄,而非連接文件,說明這是驅動真正放置的位置
現在進入test_ts目錄,然后ls,發現有一個test_ts的連接文件,ls –l查看可發現該文件連接到/sys/devices/platform/test_ts下
回到/sys/bus/platform/devices/下ls –l也會發現test_ts連接到/sys/devices/platform/test_ts
為什么test_ts這個設備放置于/sys/devices/platform下,而不是/sys/bus/platform/devices下呢
我認為和直觀性有關,在sys下有這么幾個目錄block ?bus ?class ?dev ?devices ?firmware ?kernel ?module ?fs power?
devices很直觀的說明了設備在這個目錄下
再來看組成這個目錄圖的核心,kobject圖,我也叫他層次圖
?
不看大號綠色箭頭右邊的內容的話是不是發現兩個架構相同?
對的,kobject的層次決定了目錄的結構
kobeject圖很大,但也不要擔心,里面的內容其實不多,基礎框架涉及3個主要結構kset kobject和ktype
在說明test_ts的注冊之前,先讓我們看一下sys下的兩個基礎目錄bus,devices
首先是bus
bus的注冊在/drivers/base/bus.c里
int __init buses_ini?
t(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}
先看bus_uevent_ops,這是一個uevent的操作集(我也還沒清楚uevent的用途,所以uevent的內容先放著)
然后到kset_create_and_add
struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
//傳遞進來的參數為("bus", &bus_uevent_ops, NULL)
{
struct kset *kset;
int error;
//創建一個kset容器
kset = kset_create(name, uevent_ops, parent_kobj);
if (!kset)
return NULL;
//注冊創建的kset容器
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
首先需要創建一個kset容器
static struct kset *kset_create(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
//傳遞進來的參數為("bus", &bus_uevent_ops, NULL)
{
struct kset *kset;
//為kset分配內存
kset = kzalloc(sizeof(*kset), GFP_KERNEL);
if (!kset)
return NULL;
//設置kset中kobject的名字,這里為bus
kobject_set_name(&kset->kobj, name);
//設置uevent操作集,這里為bus_uevent_ops
kset->uevent_ops = uevent_ops;
//設置父對象,這里為NULL
kset->kobj.parent = parent_kobj;
//設置容器操作集
kset->kobj.ktype = &kset_ktype;
//設置父容器
kset->kobj.kset = NULL;
return kset;
}
這里的ktype,也就是kset_ktype是一個操作集,用于為sys下文件的實時反饋做服務,例如我們cat name的時候就要通過ktype提供的show函數,具體什么怎么運用,將在后面講解
現在回到kset_create_and_add中的kset_register,將建立好的kset添加進sys里
int kset_register(struct kset *k)
{
int err;
if (!k)
return -EINVAL;
//初始化
kset_init(k);
//添加該容器
err = kobject_add_internal(&k->kobj);
if (err)
return err;
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
kset_init進行一些固定的初始化操作,里面沒有我們需要關心的內容
kobject_add_internal為重要的一個函數,他對kset里kobj的從屬關系進行解析,搭建正確的架構
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
//檢測kobj是否為空
if (!kobj)
return -ENOENT;
//檢測kobj名字是否為空
if (!kobj->name || !kobj->name[0]) {
pr_debug("kobject: (%p): attempted to be registered with empty "
"name!\n", kobj);
WARN_ON(1);
return -EINVAL;
}
//提取父對象
parent = kobject_get(kobj->parent);
/* join kset if set, use it as parent if we do not already have one */
//父容器存在則設置父對象
if (kobj->kset) {//在bus的kset中為空,所以不會進入到下面的代碼
//檢測是否已經設置父對象
if (!parent)
//無則使用父容器為父對象
parent = kobject_get(&kobj->kset->kobj);
//添加該kobj到父容器的鏈表中
kobj_kset_join(kobj);
//設置父對象
kobj->parent = parent;
}
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
kobject_name(kobj), kobj, __func__,
parent ? kobject_name(parent) : "",
kobj->kset ? kobject_name(&kobj->kset->kobj) : "");
//建立相應的目錄
error = create_dir(kobj);
if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL;
if (error == -EEXIST)
printk(KERN_ERR "%s failed for %s with "
"-EEXIST, don't try to register things with "
"the same name in the same directory.\n",
__func__, kobject_name(kobj));
else
printk(KERN_ERR "%s failed for %s (%d)\n",
__func__, kobject_name(kobj), error);
dump_stack();
} else
kobj->state_in_sysfs = 1;
return error;
}
至此bus的目錄就建立起來了
模型如下
?
接下來是devices,在/drivers/base/core.c里
int __init devices_init(void)
{
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
return 0;
}
過程和bus的注冊一致,我就不復述了~
模型如下
然后是platform的注冊
在platform的注冊中,分為兩個部分,一部分是注冊到devices中,另一部分是注冊到bus中,代碼在/drivers/base/platform.c中
int __init platform_bus_init(void)
{
int error;
//注冊到devices目錄中
error = device_register(&platform_bus);
if (error)
return error;
//注冊到bus目錄中
error = ?bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}
首先是device_register,注冊的參數為platform_bus,如下所示
struct device platform_bus = {
.bus_id ? ? ? ? ? ? ? ?= "platform",
};
很簡單,只有一個參數,表明了目錄名
int device_register(struct device *dev)
{
//初始化dev結構
device_initialize(dev);
//添加dev至目錄
return device_add(dev);
}
void device_initialize(struct device *dev)
{
//重要的一步,指明了父容器為devices_kset,而devices_kset的注冊在前面已經介紹過了
dev->kobj.kset = devices_kset;
//初始化kobj的ktype為device_ktype
kobject_init(&dev->kobj, &device_ktype);
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
set_dev_node(dev, -1);
}
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error;
dev = get_device(dev);
if (!dev || !strlen(dev->bus_id)) {
error = -EINVAL;
goto Done;
}
pr_debug("device: '%s': %s\n", dev->bus_id, __func__);
parent = get_device(dev->parent);
setup_parent(dev, parent);
if (parent)
set_dev_node(dev, dev_to_node(parent));
//設置dev->kobj的名字和父對象,并建立相應的目錄
error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
if (error)
goto Error;
if (platform_notify)
platform_notify(dev);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
//建立uevent文件
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
}
//建立subsystem連接文件連接到所屬class,這里沒有設置class對象所以不會建立
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
//建立dev的描述文件,這里沒有設置描述文件所以不會建立
error = device_add_attrs(dev);
if (error)
goto AttrsError;
//建立鏈接文件至所屬bus,這里沒有設置所屬bus所以不會建立
error = bus_add_device(dev);
if (error)
goto BusError;
//添加power文件,因為platform不屬于設備,所以不會建立power文件
error = device_pm_add(dev);
if (error)
goto PMError;
kobject_uevent(&dev->kobj, KOBJ_ADD);
//檢測驅動中有無適合的設備進行匹配,但沒有設置bus,所以不會進行匹配
bus_attach_device(dev);
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) {
down(&dev->class->sem);
list_add_tail(&dev->node, &dev->class->devices);
list_for_each_entry(class_intf, &dev->class->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
up(&dev->class->sem);
}
Done:
put_device(dev);
return error;
PMError:
bus_remove_device(dev);
BusError:
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev);
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
goto Done;
}
在kobject_add-> kobject_add_varg-> kobject_add_internal中
//提取父對象,因為沒有設置,所以為空
parent = kobject_get(kobj->parent);
//父容器存在則設置父對象,在前面的dev->kobj.kset = devices_kset中設為了devices_kset
if (kobj->kset) {
//檢測是否已經設置父對象
if (!parent)
//無則使用父容器為父對象
parent = kobject_get(&kobj->kset->kobj);
//添加該kobj到父容器的鏈表中
kobj_kset_join(kobj);
//設置父對象
kobj->parent = parent;
}
現在devices下的platform目錄建立好了,模型如下,其中紅線描繪了目錄關系
現在到bus_register了
注冊的參數platform_bus_type如下所示
struct bus_type platform_bus_type = {
.name ? ? ? ? ? ? ? ?= "platform",
.dev_attrs ? ? ? ?= platform_dev_attrs,
.match ? ? ? ? ? ? ? ?= platform_match,
.uevent ? ? ? ? ? ? ? ?= platform_uevent,
.suspend ? ? ? ? ? ? ? ?= platform_suspend,
.suspend_late ? ? ? ?= platform_suspend_late,
.resume_early ? ? ? ?= platform_resume_early,
.resume ? ? ? ? ? ? ? ?= platform_resume,
};
int bus_register(struct bus_type *bus)
{
int retval;
//聲明一個總線私有數據并分配空間
struct bus_type_private *priv;
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
//互相關聯
priv->bus = bus;
bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
//設置私有數據中kobj對象的名字
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
//設置父容器為bus_kset,操作集為bus_ktype
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
//注冊bus容器
retval = kset_register(&priv->subsys);
if (retval)
goto out;
//建立uevent屬性文件
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
//建立devices目錄
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
//建立drivers目錄
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
//初始化klist_devices和klist_drivers鏈表
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
//增加probe屬性文件
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
//增加總線的屬性文件
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
kfree(bus->p);
out:
return retval;
}
在kset_register-> kobject_add_internal中
//提取父對象,因為沒有設置父對象,所以為空
parent = kobject_get(kobj->parent);
//父容器存在則設置父對象,在上文中設置了父容器priv->subsys.kobj.kset = bus_kset
if (kobj->kset) {
//檢測是否已經設置父對象
if (!parent)
//無則使用父容器為父對象
parent = kobject_get(&kobj->kset->kobj);
//添加該kobj到父容器的鏈表中
kobj_kset_join(kobj);
//設置父對象
kobj->parent = parent;
}
在retval = kset_register(&priv->subsys)完成之后platform在bus下的模型如下圖
有印象的話大家還記得在platform下面有兩個目錄devices和drivers吧~
現在就到這兩個目錄的注冊了
priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj);
priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);
注意這兩條語句的頭部
priv->devices_kset = kset_create_and_add
priv->drivers_kset = kset_create_and_add
可以清楚的看到bus_type_private下的devices_kset, drivers_kset分別連接到了devices,drivers的kset上
現在來看kset_create_and_add("devices", NULL,&priv->subsys.kobj);
struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
//參數為"devices", NULL,&priv->subsys.kobj
{
struct kset *kset;
int error;
//創建一個kset容器
kset = kset_create(name, uevent_ops, parent_kobj);
if (!kset)
return NULL;
//注冊創建的kset容器
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
在kset_create 中比較重要的操作為
kset->kobj.ktype = &kset_ktype //設置了ktype,為kset_ktype
kset->kobj.parent = parent_kobj; //設置了父對象,為priv->subsys.kobj,也就是platform_bus_type->p->subsys.kobj
kset->kobj.kset = NULL; ? ?//設置父容器為空
在kset_register中
//提取父對象
parent = kobject_get(kobj->parent); //在之前設置為了
//父容器存在則設置父對象,由于父容器為空,不執行以下代碼
if (kobj->kset) {
//檢測是否已經設置父對象
if (!parent)
//無則使用父容器為父對象
parent = kobject_get(&kobj->kset->kobj);
//添加該kobj到父容器的鏈表中
kobj_kset_join(kobj);
//設置父對象
kobj->parent = parent;
}
至此, devices的模型就建立好了,drivers模型的建立和devices是一致的,只是名字不同而已,我就不復述了,建立好的模型如下
好了~ ?到了這里,bus,devices和platform的基礎模型就就建立好了,就等設備來注冊了
?
在platform模型設備的建立中,需要2個部分的注冊,驅動的注冊和設備的注冊
platform_device_register(&test_device); ? ? ? ?
platform_driver_register(&test_driver);
首先看platform_device_register
注冊參數為test_device,結構如下
static struct platform_device test_device = {
.name = "test_ts",
.id = -1,
//. resource
//.dev
};
這個結構主要描述了設備的名字,ID和資源和私有數據,其中資源和私有數據我們在這里不使用,將在別的文章中進行講解
int platform_device_register(struct platform_device *pdev)
{
//設備屬性的初始化
device_initialize(&pdev->dev);
//將設備添加進platform里
return platform_device_add(pdev);
}
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset; ? ? ? ? ? ? ? ? ? //設置kset為devices_kset,則將設備掛接上了devices目錄
kobject_init(&dev->kobj, &device_ktype); ? ? ? ? ? ? ? ? ? ?//初始化kobeject,置ktype為device_ktype
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
set_dev_node(dev, -1);
}
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
//檢測是否設置了dev中的parent,無則賦為platform_bus
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
//設置dev中的bus為platform_bus_type
pdev->dev.bus = &platform_bus_type;
//檢測id,id為-1表明該設備只有一個,用設備名為bus_id
//不為1則表明該設備有數個,需要用序號標明bus_id
if (pdev->id != -1)
snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
pdev->id);
else
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
//增加資源到資源樹中
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource;
if (r->name == NULL)
r->name = pdev->dev.bus_id;
p = r->parent;
if (!p) {
if (r->flags & IORESOURCE_MEM)
p = &iomem_resource;
else if (r->flags & IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
printk(KERN_ERR "%s: failed to claim resource %d\n",pdev->dev.bus_id, i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",pdev->dev.bus_id, pdev->dev.parent->bus_id);
//添加設備到設備層次中
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
while (--i >= 0)
if (pdev->resource.flags & (IORESOURCE_MEM|IORESOURCE_IO))
release_resource(&pdev->resource);
return ret;
}
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error;
dev = get_device(dev);
if (!dev || !strlen(dev->bus_id)) {
error = -EINVAL;
goto Done;
}
pr_debug("device: '%s': %s\n", dev->bus_id, __func__);
//取得上層device,而dev->parent的賦值是在platform_device_add中的pdev->dev.parent = &platform_bus完成的
parent = get_device(dev->parent);
//以上層devices為準重設dev->kobj.parent
setup_parent(dev, parent);
if (parent)
set_dev_node(dev, dev_to_node(parent));
//設置dev->kobj的名字和父對象,并建立相應目錄
error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
if (error)
goto Error;
if (platform_notify)
platform_notify(dev);
//一種新型的通知機制,但是platform中沒有設置相應的結構,所以在這里跳過
/* notify clients of device entry (new way) */
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);
//建立uevent文件
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
//設備有設備號則建立dev文件
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
}
//建立subsystem連接文件連接到所屬class
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
//添加dev的描述文件
error = device_add_attrs(dev);
if (error)
goto AttrsError;
//添加鏈接文件至所屬bus
error = bus_add_device(dev);
if (error)
goto BusError;
//添加power文件
error = device_pm_add(dev);
if (error)
goto PMError;
kobject_uevent(&dev->kobj, KOBJ_ADD);
//檢測驅動中有無適合的設備進行匹配,現在只添加了設備,還沒有加載驅動,所以不會進行匹配
bus_attach_device(dev);
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) {
down(&dev->class->sem);
list_add_tail(&dev->node, &dev->class->devices);
list_for_each_entry(class_intf, &dev->class->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
up(&dev->class->sem);
}
Done:
put_device(dev);
return error;
PMError:
bus_remove_device(dev);
BusError:
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_DEL_DEVICE, dev);
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
goto Done;
}
static void setup_parent(struct device *dev, struct device *parent)
{
struct kobject *kobj;
//取得上層device的kobj
kobj = get_device_parent(dev, parent);
//kobj不為空則重設dev->kobj.parent
if (kobj)
dev->kobj.parent = kobj;
}
static struct kobject *get_device_parent(struct device *dev,
struct device *parent)
{
int retval;
//因為dev->class為空,所以跳過這段代碼
if (dev->class) {
struct kobject *kobj = NULL;
struct kobject *parent_kobj;
struct kobject *k;
if (parent == NULL)
parent_kobj = virtual_device_parent(dev);
else if (parent->class)
return &parent->kobj;
else
parent_kobj = &parent->kobj;
spin_lock(&dev->class->class_dirs.list_lock);
list_for_each_entry(k, &dev->class->class_dirs.list, entry)
if (k->parent == parent_kobj) {
kobj = kobject_get(k);
break;
}
spin_unlock(&dev->class->class_dirs.list_lock);
if (kobj)
return kobj;
k = kobject_create();
if (!k)
return NULL;
k->kset = &dev->class->class_dirs;
retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
if (retval < 0) {
kobject_put(k);
return NULL;
}
return k;
}
if (parent)
//返回上層device的kobj
return &parent->kobj;
return NULL;
}
在bus_attach_device中雖然沒有成功進行匹配,但是有很重要的一步為之后正確的匹配打下基礎
void bus_attach_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
int ret = 0;
if (bus) {
if (bus->p->drivers_autoprobe)
ret = device_attach(dev);
WARN_ON(ret < 0);
if (ret >= 0)
klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
}
}
klist_add_tail(&dev->knode_bus, &bus->p->klist_devices)就是這一行
在這一行代碼中將設備掛載到了bus下的devices鏈表下,這樣,當驅動請求匹配的時候,platform總線就會歷遍devices鏈表為驅動尋找合適的設備
現在來看一下test_device的模型
然后platform_driver_unregister,他的參數 test_driver的結構如下
static struct platform_driver test_driver = {
.probe ? ? ? ? ? ? ? ?= test_probe,
.remove ? ? ? ? ? ? ? ?= test_remove,
.driver ? ? ? ? ? ? ? ?= {
.name ? ? ? ?= "test_ts",
.owner ? ? ? ?= THIS_MODULE,
},
};
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}
從上面代碼可以看出,在platform_driver中設置了probe, remove, shutdown, suspend或resume函數的話
則drv->driver也會設置成platform對應的函數
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
//檢測總線的操作函數和驅動的操作函數是否同時存在,同時存在則提示使用總線提供的操作函數
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use ""bus_type methods\n", drv->name);
//檢測是否已經注冊過
other = driver_find(drv->name, drv->bus);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, “"aborting...\n", drv->name);
return -EEXIST;
}
//添加驅動到總線上
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
//取bus結構
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
//分配驅動私有數據
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
//初始化klist_devices鏈表
klist_init(&priv->klist_devices, NULL, NULL);
//互相關聯
priv->driver = drv;
drv->p = priv;
//設置私有數據的父容器,在這一步中,設置了kset為platform下的drivers_kset結構,也就是drivers呢個目錄
priv->kobj.kset = bus->p->drivers_kset;
//初始化kobj對象,設置容器操作集并建立相應的目錄,這里由于沒有提供parent,所以會使用父容器中的kobj為父對象
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
//檢測所屬總線的drivers_autoprobe屬性是否為真
//為真則進行與設備的匹配,到這里,就會與我們之前注冊的test_device連接上了,至于如何連接,進行了什么操作,將在別的文章中詳細描述
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
//掛載到所屬總線驅動鏈表上
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver(drv->owner, drv);
//建立uevent屬性文件
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
//建立設備屬性文件
error = driver_add_attrs(bus, drv);
if (error) {
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",__func__, drv->name);
}
error = add_bind_files(drv);
if (error) {
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",__func__, drv->name);
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
return error;
out_unregister:
kobject_put(&priv->kobj);
out_put_bus:
bus_put(bus);
return error;
}
到這里test_driver的模型就建立好了,圖就是最上面的層次圖,我就不再貼了
到這里一個基本的框架就建立起來了~
下面,我開始對kobject kset和ktype做分析
先說說關系,ktype與kobject和kset這兩者之前的關系較少,讓我畫一個圖,是這樣的
ktype依賴于kobject,kset也依賴于kobject,而kobject有時需要kset(所以用了一個白箭頭),不一定需要ktype(真可憐,連白箭頭都沒有)
首先先說一下這個可有可無的ktype
到/sys/bus/platform下面可以看見一個drivers_autoprobe的文件
cat drivers_autoprobe可以查看這個文件的值
echo 0 > drivers_autoprobe則可以改變這個文件的值
drivers_autoprobe這個文件表示的是是否自動進行初始化
在
void bus_attach_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
int ret = 0;
if (bus) {
if (bus->p->drivers_autoprobe)
ret = device_attach(dev);
WARN_ON(ret < 0);
if (ret >= 0)
klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
}
}
中可以看見這么一段代碼
if (bus->p->drivers_autoprobe)
ret = device_attach(dev);
bus->p->drivers_autoprobe的值為真則進行匹配
而drivers_autoprobe這個文件則可以動態的修改這個值選擇是否進行匹配
使用外部文件修改內核參數,ktype就是提供了這么一種方法
現在讓我們看看ktype是怎么通過kobject進行運作的
首先是ktype及通過ktype進行運作的drivers_autoprobe的注冊
ktype的掛載十分簡單,因為他是和kobject是一體的
只有這么下面一句 ? ? ? ?
priv->subsys.kobj.ktype = &bus_ktype;
這樣就將bus_ktype掛載到了platform_bus_type的kobject上
drivers_autoprobe的注冊如下
retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
bus_attr_drivers_autoprobe這個結構由一系列的宏進行組裝
static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
show_drivers_autoprobe, store_drivers_autoprobe);
#define BUS_ATTR(_name, _mode, _show, _store) ? ? ? ?\
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, ? ? ? ?\
.show ? ? ? ?= _show, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
.store ? ? ? ?= _store, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
}
最后bus_attr_drivers_autoprobe的模型如下
struct bus_attribute ?bus_attr_drivers_autoprobe?
{
.attr = {
.name = “drivers_autoprobe”,
.mode = S_IWUSR | S_IRUGO?
}, ? ? ? ?
.show ? ? ? ?= show_drivers_autoprobe, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
.store ? ? ? ?= store_drivers_autoprobe, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
}
進入到bus_create_file中
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
//參數為(bus, &bus_attr_drivers_autoprobe)
{
int error;
if (bus_get(bus)) {
error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
bus_put(bus);
} else
error = -EINVAL;
return error;
}
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
//參數為(&bus->p->subsys.kobj, &attr->attr)
{
BUG_ON(!kobj || !kobj->sd || !attr);
return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
}
int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,int type)
//參數為(&bus->p->subsys.kobj ->sd, &attr->attr, SYSFS_KOBJ_ATTR)
{
return sysfs_add_file_mode(dir_sd, attr, type, attr->mode);
}
int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
const struct attribute *attr, int type, mode_t amode)
//整理一下參數,現在應該為
//(&platform_bus_type->p->subsys.kobj ->sd, &bus_attr_drivers_autoprobe->attr, SYSFS_KOBJ_ATTR, &bus_attr_drivers_autoprobe->attr->mode)
{
umode_t mode = (amode & S_IALLUGO) | S_IFREG;
struct sysfs_addrm_cxt acxt;
struct sysfs_dirent *sd;
int rc;
//在這一步中可以看出新建了一個節點
sd = sysfs_new_dirent(attr->name, mode, type);
if (!sd)
return -ENOMEM;
//這一步掛載了&bus_attr_drivers_autoprobe->attr到節點中,為以后提取attr及上層結構做準備
sd->s_attr.attr = (void *)attr;
// dir_sd也就是上層目錄,在這里為platform_bus_type->p->subsys.kobj ->sd
//也就是/sys/bus/platform這個目錄
sysfs_addrm_start(&acxt, dir_sd);
rc = sysfs_add_one(&acxt, sd);
sysfs_addrm_finish(&acxt);
if (rc)
sysfs_put(sd);
return rc;
}
struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
{
char *dup_name = NULL;
struct sysfs_dirent *sd;
if (type & SYSFS_COPY_NAME) {
name = dup_name = kstrdup(name, GFP_KERNEL);
if (!name)
return NULL;
}
sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);
if (!sd)
goto err_out1;
if (sysfs_alloc_ino(&sd->s_ino))
goto err_out2;
atomic_set(&sd->s_count, 1);
atomic_set(&sd->s_active, 0);
sd->s_name = name; ? //節點的名字為&bus_attr_drivers_autoprobe->attr->name ?也就是drivers_autoprobe
sd->s_mode = mode;
sd->s_flags = type; ? //節點的type為SYSFS_KOBJ_ATTR
return sd;
err_out2:
kmem_cache_free(sysfs_dir_cachep, sd);
err_out1:
kfree(dup_name);
return NULL;
}
現在一切準備就緒,來看看怎么讀取吧
首先是open,大概流程可以看我的另一篇文章<從文件到設備>,一直看到ext3_lookup
這里和ext3_lookup不同的是,sys的文件系統是sysfs文件系統,所以應該使用的lookup函數為sysfs_lookup(/fs/sysfs/dir.c)
static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
struct dentry *ret = NULL;
struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
struct sysfs_dirent *sd;
struct inode *inode;
mutex_lock(&sysfs_mutex);
sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);
if (!sd) {
ret = ERR_PTR(-ENOENT);
goto out_unlock;
}
//節點的初始化在這里
inode = sysfs_get_inode(sd);
if (!inode) {
ret = ERR_PTR(-ENOMEM);
goto out_unlock;
}
dentry->d_op = &sysfs_dentry_ops;
dentry->d_fsdata = sysfs_get(sd);
d_instantiate(dentry, inode);
d_rehash(dentry);
out_unlock:
mutex_unlock(&sysfs_mutex);
return ret;
}
struct inode * sysfs_get_inode(struct sysfs_dirent *sd)
{
struct inode *inode;
inode = iget_locked(sysfs_sb, sd->s_ino);
if (inode && (inode->i_state & I_NEW))
//為節點賦值
sysfs_init_inode(sd, inode);
return inode;
}
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
{
struct bin_attribute *bin_attr;
inode->i_blocks = 0;
inode->i_mapping->a_ops = &sysfs_aops;
inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
inode->i_op = &sysfs_inode_operations;
inode->i_ino = sd->s_ino;
lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
if (sd->s_iattr) {
set_inode_attr(inode, sd->s_iattr);
} else
set_default_inode_attr(inode, sd->s_mode);
//判斷類型
switch (sysfs_type(sd)) {
case SYSFS_DIR:
inode->i_op = &sysfs_dir_inode_operations;
inode->i_fop = &sysfs_dir_operations;
inode->i_nlink = sysfs_count_nlink(sd);
break;
//還記得在注冊的時候有一個參數為SYSFS_KOBJ_ATTR賦到了sd->s_flags上面吧
case SYSFS_KOBJ_ATTR:
inode->i_size = PAGE_SIZE;
inode->i_fop = &sysfs_file_operations;
break;
case SYSFS_KOBJ_BIN_ATTR:
bin_attr = sd->s_bin_attr.bin_attr;
inode->i_size = bin_attr->size;
inode->i_fop = &bin_fops;
break;
case SYSFS_KOBJ_LINK:
inode->i_op = &sysfs_symlink_inode_operations;
break;
default:
BUG();
}
unlock_new_inode(inode);
}
sysfs_file_operations的結構如下,之后open和read,write都明了了
const struct file_operations sysfs_file_operations = {
.read ? ? ? ? ? ? ? ?= sysfs_read_file,
.write ? ? ? ? ? ? ? ?= sysfs_write_file,
.llseek ? ? ? ? ? ? ? ?= generic_file_llseek,
.open ? ? ? ? ? ? ? ?= sysfs_open_file,
.release ? ? ? ?= sysfs_release,
.poll ? ? ? ? ? ? ? ?= sysfs_poll,
};
有關在哪調用open還是請查閱我的另一篇文章<從文件到設備>中 nameidata_to_filp之后的操作
好的~ ?現在進入到了sysfs_open_file中
static int sysfs_open_file(struct inode *inode, struct file *file)
{
struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
//要重的取值,在這里取得了drivers_autoprobe的目錄platform的kproject
struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
struct sysfs_buffer *buffer;
struct sysfs_ops *ops;
int error = -EACCES;
if (!sysfs_get_active_two(attr_sd))
return -ENODEV;
if (kobj->ktype && kobj->ktype->sysfs_ops)
//這里可謂是ktype實現中的核心,在這里ops設置成了platform_bus_type中kobject->ktype的sysfs_ops
ops = kobj->ktype->sysfs_ops;
else {
printk(KERN_ERR "missing sysfs attribute operations for ""kobject: %s\n", kobject_name(kobj));
WARN_ON(1);
goto err_out;
}
if (file->f_mode & FMODE_WRITE) {
if (!(inode->i_mode & S_IWUGO) || !ops->store)
goto err_out;
}
if (file->f_mode & FMODE_READ) {
if (!(inode->i_mode & S_IRUGO) || !ops->show)
goto err_out;
}
error = -ENOMEM;
buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);
if (!buffer)
goto err_out;
mutex_init(&buffer->mutex);
buffer->needs_read_fill = 1;
//然后將設置好的ops掛載到buffer上
buffer->ops = ops;
//再將buffer掛載到file->private_data中
file->private_data = buffer;
error = sysfs_get_open_dirent(attr_sd, buffer);
if (error)
goto err_free;
sysfs_put_active_two(attr_sd);
return 0;
err_free:
kfree(buffer);
err_out:
sysfs_put_active_two(attr_sd);
return error;
}
現在已經為read和write操作準備好了
馬上進入到read操作中
整個流程如上圖所示,如何進入到sysfs_read_file在上面open的操作中已經說明了
我們就從sysfs_read_file開始分析(該文件在/fs/sysfs/file.c中)
sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct sysfs_buffer * buffer = file->private_data;
ssize_t retval = 0;
mutex_lock(&buffer->mutex);
if (buffer->needs_read_fill || *ppos == 0) {
//主要操作在fill_read_buffer中
retval = fill_read_buffer(file->f_path.dentry,buffer);
if (retval)
goto out;
}
pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",__func__, count, *ppos, buffer->page);
retval = simple_read_from_buffer(buf, count, ppos, buffer->page,
buffer->count);
out:
mutex_unlock(&buffer->mutex);
return retval;
}
static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)
{
struct sysfs_dirent *attr_sd = dentry->d_fsdata;
//取得父目錄的kobject,也就是platform的kobject
struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
//還記得這個buffer->ops在什么時候進行賦值的么?
struct sysfs_ops * ops = buffer->ops;
int ret = 0;
ssize_t count;
if (!buffer->page)
buffer->page = (char *) get_zeroed_page(GFP_KERNEL);
if (!buffer->page)
return -ENOMEM;
if (!sysfs_get_active_two(attr_sd))
return -ENODEV;
buffer->event = atomic_read(&attr_sd->s_attr.open->event);
//調用ops->show ?也就是bus_sysfs_ops->show ? ?具體就是bus_attr_show了
//參數為父目錄的kobject, bus_attr_drivers_autoprobe->attr,和一段char信息
count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page);
sysfs_put_active_two(attr_sd);
if (count >= (ssize_t)PAGE_SIZE) {
print_symbol("fill_read_buffer: %s returned bad count\n",
(unsigned long)ops->show);
/* Try to struggle along */
count = PAGE_SIZE - 1;
}
if (count >= 0) {
buffer->needs_read_fill = 0;
buffer->count = count;
} else {
ret = count;
}
return ret;
}
現在進入bus_attr_show中
static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,char *buf)
{
//提取attr的上層結構,也就是bus_attr_drivers_autoprobe
struct bus_attribute *bus_attr = to_bus_attr(attr);
//提取kobj的上上層結構,也就是bus_type_private
struct bus_type_private *bus_priv = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->show)
//終于到了這里,最后的調用,調用bus_attr_drivers_autoprobe.show ,也就是show_drivers_autoprobe
//參數為bus_priv->bus,也就是platform_bus_type , 及一段char信息
ret = bus_attr->show(bus_priv->bus, buf);
return ret;
}
static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
{
return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
}
沒什么好介紹了就是打印 buf + bus->p->drivers_autoprobe ? 從結果來看~ buf是空的
到這里,終于把內核的信息給打印出來了,千辛萬苦,層層調用,就是為了取得上層kobject結構,逆運算再取得kobject的上層結構
大家是否對kobject有所了解了呢?~ ?
在對kobject進行介紹之前 ?還是先把write操作講完吧 哈哈~
write操作和read操作重要的步驟基本是一致的,只不過在最后的調用中
static ssize_t store_drivers_autoprobe(struct bus_type *bus,
const char *buf, size_t count)
{
if (buf[0] == '0')
bus->p->drivers_autoprobe = 0;
else
bus->p->drivers_autoprobe = 1;
return count;
}
不進行打印而對內核的參數進行了修改而已
好~ 現在讓我們來看看kobject吧
kobject的結構如下
struct kobject {
const char ? ? ? ? ? ? ? ?*name; ? ? ? ? ?//kobject的名字
struct kref ? ? ? ? ? ? ? ?kref; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//kobject的原子操作
struct list_head ? ? ? ?entry;
struct kobject ? ? ? ? ? ? ? ?*parent; ? ? ? ? ? ? ? ? ? ? ? ?//父對象
struct kset ? ? ? ? ? ? ? ?*kset; ? ? ? ? ? ? ? ? ? ? ? ?//父容器
struct kobj_type ? ? ? ?*ktype; ? ? ? ? ? ? ? ? ? ? ? ?//ktype
struct sysfs_dirent ? ? ? ?*sd; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//文件節點
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
};
kobject描述的是較具體的對象,一個設備,一個驅動,一個總線,一類設備
在層次圖上可以看出,每個存在于層次圖中的設備,驅動,總線,類別都有自己的kobject
kobject與kobject之間的層次由kobject中的parent指針決定
而kset指針則表明了kobject的容器
像platform_bus 和test_device的kset都是devices_kset
呢parent和kset有什么不同呢
我認為是人工和默認的區別,看下面這張圖 ,藍框為kset,紅框為kobject
容器提供了一種默認的層次~ ?但也可以人工設置層次
對于kobject現在我只理解了這么多,歡迎大家指出有疑問的地方
最后是kset,kset比較簡單,看下面的結構
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
};
對于kset的描述,文檔里也有介紹
/**
* struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
*
* A kset defines a group of kobjects. ?They can be individually
* different "types" but overall these kobjects all want to be grouped
* together and operated on in the same manner. ?ksets are used to
* define the attribute callbacks and other common events that happen to
* a kobject.
翻譯過來大概就是
結構kset,一個指定類型的kobject的集合,屬于某一個指定的子系統
kset定義了一組kobject,它們可以是不同類型組成但卻希望捆在一起有一個統一的操作
kset通常被定義為回調屬性和其他通用的事件發生在kobject上
可能翻譯的不是很好,望大家見諒
從結構中能看出kset比kobject多了3個屬性
list_head ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//列表
spinlock_t ? ? ? ? ? ? ? ? ? ? ? ?//共享鎖
kset_uevent_ops ? ? ? ? ? ? ? ?//uevent操作集
list_head ? ? ? ?連接了所有kobject中kset屬性指向自己的kobject
而kset_uevent_ops則用于通知機制,由于uevent的作用我也沒接觸過,所以暫不解析uevent的機制了
?
評論