linux下bus、devices和platform的基础模型 【转】

转自:http://blog.chinaunix.net/uid-20672257-id-3147337.html

一、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

下面转自:http://blog.chinaunix.net/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的机制了

时间: 2024-09-26 11:58:32

linux下bus、devices和platform的基础模型 【转】的相关文章

[c/c++]关于linux下动态库/静态库的基础问题

问题描述 [c/c++]关于linux下动态库/静态库的基础问题 本人小白,自学没多久,有几个问题一直没搞太明白,望高手解答! 假如我写了一个动态库libmylib.so(我有函数声明mylib.h),里面用到了A同学写的动态库liba.so(我有声明a.h),现在我要在一个新的程序test.cpp里调用我写的mylib.so 问题: 1.test.cpp的头文件需要两个.h都包含还是只要mylib.h? 2.用g++链接时 -lmylib -la都需要吗? 3.假如有一天liba.so文件丢失

Linux 下网络协议分析器 Wireshark 使用基础

Wireshark 是 Kali 中预置的众多有价值工具中的一种.与其它工具一样,它可以被用于正面用途,同样也可以被用于不良目的.当然,本文将会介绍如何追踪你自己的网络流量来发现潜在的非正常活动. Wireshark 相当的强大,当你第一次见到它的时候可能会被它吓到,但是它的目的始终就只有一个,那就是追踪网络流量,并且它所实现的所有选项都只为了加强它追踪流量的能力. 安装 Kali 中预置了 Wireshark .不过,wireshark-gtk 包提供了一个更好的界面使你在使用 Wiresha

Linux下网络协议分析器Wireshark使用基础

Wireshark 是 Kali 中预置的众多有价值工具中的一种.与其它工具一样,它可以被用于正面用途,同样也可以被用于不良目的.当然,本文将会介绍如何追踪你自己的网络流量来发现潜在的非正常活动. Wireshark 相当的强大,当你第一次见到它的时候可能会被它吓到,但是它的目的始终就只有一个,那就是追踪网络流量,并且它所实现的所有选项都只为了加强它追踪流量的能力. 安装 Kali 中预置了 Wireshark .不过,wireshark-gtk 包提供了一个更好的界面使你在使用 Wiresha

linux下bluetooth编程(一)基础概念

  一:Bluetooth基本概念:   Bluetooth是爱立信.诺基亚.东芝.IBM和Intel 5家公司在1998年联合推出的一项无线网络技术.其宗旨是提供一种短距离.低成本的无线传输应用技术.在行业协会筹备阶段,需要一个极具有表现力的名字来命名这项高新技术.行业组织人员,在经过一夜关于欧洲历史和未来无限技术发展的讨论后,有些人认为用Blatand国王的名字命名再合适不过了.Blatand国王将现在的挪威,瑞典和丹麦统一起来:就如同这项即将面世的技术,将标准不一的短距离无线传输技术统一起

如何在Linux下写无线网卡的驱动【转】

转自:http://www.crifan.com/files/doc/docbook/linux_wireless/release/html/linux_wireless.html   版本:v0.3 How to write wireless network card driver under Linux Crifan Li 摘要 本文主要介绍了Linux下的无线网络相关的基础知识,从网络到无线网络再到802.11的无线网络,然后再介绍Linux无线网络的框架,最后介绍如何在Linux的框架下

linux基础-linux下wall命令的使用

问题描述 linux下wall命令的使用 用wall命令通知其他用户时,发现其他用户接收不了信息,求大神们帮帮忙.

Linux下 php5 MySQL5 Apache2 phpMyAdmin ZendOptimizer安装与配置[图文]_php基础

需要大家细心和耐心.这些在安装过程中大家将会体会到.不正确的地方大家指正批评.本例是在red hat 9下 php5.2.1 apache2.2.4 MySQL5.0.37 phpmyadmin2.10.0.2 ZendOptimizer-3.2.6 libxml2-2.6.11 的安装与配置 其中 libxml2-2.6.11 我下载在 /usr/ 目录下 其他都下载在 /usr/down 目录下.这些目录在下面安装过程中要用到 关于windows下的php环境配置可以参考 http://ww

零基础学习教程之Linux下搭建android开发环境_Android

不得已重新配置,这里记下详细步骤,分享给大家. 一.安装jdk,具体步骤如下: 1.将jdk-7u4-linux-i586.tar拷贝到linux下得/home/yan/download目录,新建一个目录/home/yan/android.所以和android相关的都安装到/home/yan/android这个目录,所有的源安装文件都放到download下.切换到root,在download目录下输入命令:tar -zxvf  jdk-7u4-linux-i586.tar -C ../andro

linux基础-Linux下blas库安装问题,麻烦啦

问题描述 Linux下blas库安装问题,麻烦啦 Linux小白,刚上手,安装一个程序,需要blas库,但是一直装不上.想咨询一下大家,帮忙搞定的,给发微信红包.麻烦啦- 解决方案 可以加个微信聊,小白问题多 解决方案二: 如果你有安装包的话,就直接解压,然后进入解压后的文件夹 然后 ./configure make make install就ok了 解决方案三: ./configure make make install 解决方案四: 你是用的啥系统.用的源码还是啥? ubuntu 我用 ap