天天看點

個人對kobject的一點研究(6)

然後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這兩者之前的關系較少,讓我畫一個圖,是這樣的

個人對kobject的一點研究(6)

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操作中

個人對kobject的一點研究(6)

整個流程如上圖所示,如何進入到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);

                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的一點研究(6)

容器提供了一種預設的層次~  但也可以人工設定層次

對于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的機制了

寫到這裡,不知道大家對核心驅動架構中的注冊和對kobject的了解有無加深呢?

轉自BLOG http://blog.chinaunix.net/u1/57901/