个人对kobject的一点研究(6)

来源:互联网 发布:学生p2p网络投资 编辑:程序博客网 时间:2024/05/17 23:18
然后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的机制了


写到这里,不知道大家对内核驱动架构中的注册和对kobject的了解有无加深呢?

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