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

﹏ヽ暗。殇╰゛Y 2022-03-26 17:41 221阅读 0赞

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

{

  1. int err = 0;
  2. return err;

}

static int test_remove(struct platform_device *pdev)

{

  1. return 0;

}

static struct platform_device test_device = {

  1. .name = "test\_ts",
  2. .id = -1,

};

static struct platform_driver test_driver = {

  1. .probe = test\_probe,
  2. .remove = test\_remove,
  3. .driver = \{
  4. .name = "test\_ts",
  5. .owner = THIS\_MODULE,
  6. \},

};

static int __devinit test_init(void)

{

  1. platform\_device\_register(&test\_device);
  2. return platform\_driver\_register(&test\_driver);

}

static void __exit test_exit(void)

{

  1. platform\_device\_unregister(&test\_device);
  2. 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:

  1. $(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图,我也叫他层次图
![Image 1][]

不看大号绿色箭头右边的内容的话是不是发现两个架构相同?

对的,kobject的层次决定了目录的结构

kobeject图很大,但也不要担心,里面的内容其实不多,基础框架涉及3个主要结构kset kobject和ktype

在说明test_ts的注册之前,先让我们看一下sys下的两个基础目录bus,devices

首先是bus

bus的注册在/drivers/base/bus.c里

int __init buses_ini

t(void)

{

  1. bus\_kset = kset\_create\_and\_add("bus", &bus\_uevent\_ops, NULL);
  2. if (!bus\_kset)
  3. return -ENOMEM;
  4. return 0;

}

先看bus_uevent_ops,这是一个uevent的操作集(我也还没清楚uevent的用途,所以uevent的内容先放着)

然后到kset_create_and_add

struct kset *kset_create_and_add(const char *name,

  1. struct kset\_uevent\_ops \*uevent\_ops,
  2. struct kobject \*parent\_kobj)

//传递进来的参数为(“bus”, &bus_uevent_ops, NULL)

{

  1. struct kset \*kset;
  2. int error;
  3. //创建一个kset容器
  4. kset = kset\_create(name, uevent\_ops, parent\_kobj);
  5. if (!kset)
  6. return NULL;
  7. //注册创建的kset容器
  8. error = kset\_register(kset);
  9. if (error) \{
  10. kfree(kset);
  11. return NULL;
  12. \}
  13. return kset;

}

首先需要创建一个kset容器

static struct kset *kset_create(const char *name,

  1. struct kset\_uevent\_ops \*uevent\_ops,
  2. struct kobject \*parent\_kobj)

//传递进来的参数为(“bus”, &bus_uevent_ops, NULL)

{

  1. struct kset \*kset;
  2. //为kset分配内存
  3. kset = kzalloc(sizeof(\*kset), GFP\_KERNEL);
  4. if (!kset)
  5. return NULL;
  6. //设置kset中kobject的名字,这里为bus
  7. kobject\_set\_name(&kset->kobj, name);
  8. //设置uevent操作集,这里为bus\_uevent\_ops
  9. kset->uevent\_ops = uevent\_ops;
  10. //设置父对象,这里为NULL
  11. kset->kobj.parent = parent\_kobj;
  12. //设置容器操作集
  13. kset->kobj.ktype = &kset\_ktype;
  14. //设置父容器
  15. kset->kobj.kset = NULL;
  16. return kset;

}

这里的ktype,也就是kset_ktype是一个操作集,用于为sys下文件的实时反馈做服务,例如我们cat name的时候就要通过ktype提供的show函数,具体什么怎么运用,将在后面讲解

现在回到kset_create_and_add中的kset_register,将建立好的kset添加进sys里

int kset_register(struct kset *k)

{

  1. int err;
  2. if (!k)
  3. return -EINVAL;
  4. //初始化
  5. kset\_init(k);
  6. //添加该容器
  7. err = kobject\_add\_internal(&k->kobj);
  8. if (err)
  9. return err;
  10. kobject\_uevent(&k->kobj, KOBJ\_ADD);
  11. return 0;

}

kset_init进行一些固定的初始化操作,里面没有我们需要关心的内容

kobject_add_internal为重要的一个函数,他对kset里kobj的从属关系进行解析,搭建正确的架构

static int kobject_add_internal(struct kobject *kobj)

{

  1. int error = 0;
  2. struct kobject \*parent;
  3. //检测kobj是否为空
  4. if (!kobj)
  5. return -ENOENT;
  6. //检测kobj名字是否为空
  7. if (!kobj->name || !kobj->name\[0\]) \{
  8. pr\_debug("kobject: (%p): attempted to be registered with empty "
  9. "name!\\n", kobj);
  10. WARN\_ON(1);
  11. return -EINVAL;
  12. \}
  13. //提取父对象
  14. parent = kobject\_get(kobj->parent);
  15. /\* join kset if set, use it as parent if we do not already have one \*/
  16. //父容器存在则设置父对象
  17. if (kobj->kset) \{//在bus的kset中为空,所以不会进入到下面的代码
  18. //检测是否已经设置父对象
  19. if (!parent)
  20. //无则使用父容器为父对象
  21. parent = kobject\_get(&kobj->kset->kobj);
  22. //添加该kobj到父容器的链表中
  23. kobj\_kset\_join(kobj);
  24. //设置父对象
  25. kobj->parent = parent;
  26. \}
  27. pr\_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\\n",
  28. kobject\_name(kobj), kobj, \_\_func\_\_,
  29. parent ? kobject\_name(parent) : "<NULL>",
  30. kobj->kset ? kobject\_name(&kobj->kset->kobj) : "<NULL>");
  31. //建立相应的目录
  32. error = create\_dir(kobj);
  33. if (error) \{
  34. kobj\_kset\_leave(kobj);
  35. kobject\_put(parent);
  36. kobj->parent = NULL;
  37. if (error == -EEXIST)
  38. printk(KERN\_ERR "%s failed for %s with "
  39. "-EEXIST, don't try to register things with "
  40. "the same name in the same directory.\\n",
  41. \_\_func\_\_, kobject\_name(kobj));
  42. else
  43. printk(KERN\_ERR "%s failed for %s (%d)\\n",
  44. \_\_func\_\_, kobject\_name(kobj), error);
  45. dump\_stack();
  46. \} else
  47. kobj->state\_in\_sysfs = 1;
  48. return error;

}

至此bus的目录就建立起来了

模型如下
![Image 1][]

接下来是devices,在/drivers/base/core.c里

int __init devices_init(void)

{

  1. devices\_kset = kset\_create\_and\_add("devices", &device\_uevent\_ops, NULL);
  2. if (!devices\_kset)
  3. return -ENOMEM;
  4. return 0;

}

过程和bus的注册一致,我就不复述了~

模型如下
![Image 1][]

然后是platform的注册

在platform的注册中,分为两个部分,一部分是注册到devices中,另一部分是注册到bus中,代码在/drivers/base/platform.c中

int __init platform_bus_init(void)

{

  1. int error;
  2. //注册到devices目录中
  3. error = device\_register(&platform\_bus);
  4. if (error)
  5. return error;
  6. //注册到bus目录中
  7. error = bus\_register(&platform\_bus\_type);

if (error)

  1. device\_unregister(&platform\_bus);
  2. return error;

}

首先是device_register,注册的参数为platform_bus,如下所示

struct device platform_bus = {

  1. .bus\_id = "platform",

};

很简单,只有一个参数,表明了目录名

int device_register(struct device *dev)

{

  1. //初始化dev结构
  2. device\_initialize(dev);
  3. //添加dev至目录
  4. return device\_add(dev);

}

void device_initialize(struct device *dev)

{

  1. //重要的一步,指明了父容器为devices\_kset,而devices\_kset的注册在前面已经介绍过了
  2. dev->kobj.kset = devices\_kset;
  3. //初始化kobj的ktype为device\_ktype
  4. kobject\_init(&dev->kobj, &device\_ktype);
  5. klist\_init(&dev->klist\_children, klist\_children\_get,
  6. klist\_children\_put);
  7. INIT\_LIST\_HEAD(&dev->dma\_pools);
  8. INIT\_LIST\_HEAD(&dev->node);
  9. init\_MUTEX(&dev->sem);
  10. spin\_lock\_init(&dev->devres\_lock);
  11. INIT\_LIST\_HEAD(&dev->devres\_head);
  12. device\_init\_wakeup(dev, 0);
  13. set\_dev\_node(dev, -1);

}

int device_add(struct device *dev)

{

  1. struct device \*parent = NULL;
  2. struct class\_interface \*class\_intf;
  3. int error;
  4. dev = get\_device(dev);
  5. if (!dev || !strlen(dev->bus\_id)) \{
  6. error = -EINVAL;
  7. goto Done;
  8. \}
  9. pr\_debug("device: '%s': %s\\n", dev->bus\_id, \_\_func\_\_);
  10. parent = get\_device(dev->parent);
  11. setup\_parent(dev, parent);
  12. if (parent)
  13. set\_dev\_node(dev, dev\_to\_node(parent));
  14. //设置dev->kobj的名字和父对象,并建立相应的目录
  15. error = kobject\_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus\_id);
  16. if (error)
  17. goto Error;
  18. if (platform\_notify)
  19. platform\_notify(dev);
  20. if (dev->bus)
  21. blocking\_notifier\_call\_chain(&dev->bus->p->bus\_notifier,
  22. BUS\_NOTIFY\_ADD\_DEVICE, dev);
  23. //建立uevent文件
  24. error = device\_create\_file(dev, &uevent\_attr);
  25. if (error)
  26. goto attrError;
  27. if (MAJOR(dev->devt)) \{
  28. error = device\_create\_file(dev, &devt\_attr);
  29. if (error)
  30. goto ueventattrError;
  31. \}
  32. //建立subsystem连接文件连接到所属class,这里没有设置class对象所以不会建立
  33. error = device\_add\_class\_symlinks(dev);
  34. if (error)
  35. goto SymlinkError;
  36. //建立dev的描述文件,这里没有设置描述文件所以不会建立
  37. error = device\_add\_attrs(dev);
  38. if (error)
  39. goto AttrsError;
  40. //建立链接文件至所属bus,这里没有设置所属bus所以不会建立
  41. error = bus\_add\_device(dev);
  42. if (error)
  43. goto BusError;
  44. //添加power文件,因为platform不属于设备,所以不会建立power文件
  45. error = device\_pm\_add(dev);
  46. if (error)
  47. goto PMError;
  48. kobject\_uevent(&dev->kobj, KOBJ\_ADD);
  49. //检测驱动中有无适合的设备进行匹配,但没有设置bus,所以不会进行匹配
  50. bus\_attach\_device(dev);
  51. if (parent)
  52. klist\_add\_tail(&dev->knode\_parent, &parent->klist\_children);
  53. if (dev->class) \{
  54. down(&dev->class->sem);
  55. list\_add\_tail(&dev->node, &dev->class->devices);
  56. list\_for\_each\_entry(class\_intf, &dev->class->interfaces, node)
  57. if (class\_intf->add\_dev)
  58. class\_intf->add\_dev(dev, class\_intf);
  59. up(&dev->class->sem);
  60. \}

Done:

  1. put\_device(dev);
  2. return error;

PMError:

  1. bus\_remove\_device(dev);

BusError:

  1. if (dev->bus)
  2. blocking\_notifier\_call\_chain(&dev->bus->p->bus\_notifier,
  3. BUS\_NOTIFY\_DEL\_DEVICE, dev);
  4. device\_remove\_attrs(dev);

AttrsError:

  1. device\_remove\_class\_symlinks(dev);

SymlinkError:

  1. if (MAJOR(dev->devt))
  2. device\_remove\_file(dev, &devt\_attr);

ueventattrError:

  1. device\_remove\_file(dev, &uevent\_attr);

attrError:

  1. kobject\_uevent(&dev->kobj, KOBJ\_REMOVE);
  2. kobject\_del(&dev->kobj);

Error:

  1. cleanup\_device\_parent(dev);
  2. if (parent)
  3. put\_device(parent);
  4. 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) {

//检测是否已经设置父对象

  1. if (!parent)
  2. //无则使用父容器为父对象
  3. parent = kobject\_get(&kobj->kset->kobj);

//添加该kobj到父容器的链表中

  1. kobj\_kset\_join(kobj);
  2. //设置父对象
  3. kobj->parent = parent;

}

现在devices下的platform目录建立好了,模型如下,其中红线描绘了目录关系
![Image 1][]

现在到bus_register了

注册的参数platform_bus_type如下所示

struct bus_type platform_bus_type = {

  1. .name = "platform",
  2. .dev\_attrs = platform\_dev\_attrs,
  3. .match = platform\_match,
  4. .uevent = platform\_uevent,
  5. .suspend = platform\_suspend,
  6. .suspend\_late = platform\_suspend\_late,
  7. .resume\_early = platform\_resume\_early,
  8. .resume = platform\_resume,

};

int bus_register(struct bus_type *bus)

{

  1. int retval;
  2. //声明一个总线私有数据并分配空间
  3. struct bus\_type\_private \*priv;
  4. priv = kzalloc(sizeof(struct bus\_type\_private), GFP\_KERNEL);
  5. if (!priv)
  6. return -ENOMEM;
  7. //互相关联
  8. priv->bus = bus;
  9. bus->p = priv;
  10. BLOCKING\_INIT\_NOTIFIER\_HEAD(&priv->bus\_notifier);
  11. //设置私有数据中kobj对象的名字
  12. retval = kobject\_set\_name(&priv->subsys.kobj, "%s", bus->name);
  13. if (retval)
  14. goto out;
  15. //设置父容器为bus\_kset,操作集为bus\_ktype
  16. priv->subsys.kobj.kset = bus\_kset;
  17. priv->subsys.kobj.ktype = &bus\_ktype;
  18. priv->drivers\_autoprobe = 1;
  19. //注册bus容器
  20. retval = kset\_register(&priv->subsys);
  21. if (retval)
  22. goto out;
  23. //建立uevent属性文件
  24. retval = bus\_create\_file(bus, &bus\_attr\_uevent);
  25. if (retval)
  26. goto bus\_uevent\_fail;
  27. //建立devices目录
  28. priv->devices\_kset = kset\_create\_and\_add("devices", NULL,
  29. &priv->subsys.kobj);
  30. if (!priv->devices\_kset) \{
  31. retval = -ENOMEM;
  32. goto bus\_devices\_fail;
  33. \}
  34. //建立drivers目录
  35. priv->drivers\_kset = kset\_create\_and\_add("drivers", NULL,
  36. &priv->subsys.kobj);
  37. if (!priv->drivers\_kset) \{
  38. retval = -ENOMEM;
  39. goto bus\_drivers\_fail;
  40. \}
  41. //初始化klist\_devices和klist\_drivers链表
  42. klist\_init(&priv->klist\_devices, klist\_devices\_get, klist\_devices\_put);
  43. klist\_init(&priv->klist\_drivers, NULL, NULL);
  44. //增加probe属性文件
  45. retval = add\_probe\_files(bus);
  46. if (retval)
  47. goto bus\_probe\_files\_fail;
  48. //增加总线的属性文件
  49. retval = bus\_add\_attrs(bus);
  50. if (retval)
  51. goto bus\_attrs\_fail;
  52. pr\_debug("bus: '%s': registered\\n", bus->name);
  53. return 0;

bus_attrs_fail:

  1. remove\_probe\_files(bus);

bus_probe_files_fail:

  1. kset\_unregister(bus->p->drivers\_kset);

bus_drivers_fail:

  1. kset\_unregister(bus->p->devices\_kset);

bus_devices_fail:

  1. bus\_remove\_file(bus, &bus\_attr\_uevent);

bus_uevent_fail:

  1. kset\_unregister(&bus->p->subsys);
  2. kfree(bus->p);

out:

  1. return retval;

}

在kset_register-> kobject_add_internal中

//提取父对象,因为没有设置父对象,所以为空

parent = kobject_get(kobj->parent);

//父容器存在则设置父对象,在上文中设置了父容器priv->subsys.kobj.kset = bus_kset

if (kobj->kset) {

  1. //检测是否已经设置父对象
  2. if (!parent)
  3. //无则使用父容器为父对象
  4. parent = kobject\_get(&kobj->kset->kobj);
  5. //添加该kobj到父容器的链表中
  6. kobj\_kset\_join(kobj);
  7. //设置父对象
  8. kobj->parent = parent;

}

在retval = kset_register(&priv->subsys)完成之后platform在bus下的模型如下图

![Image 1][]

有印象的话大家还记得在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,

  1. struct kset\_uevent\_ops \*uevent\_ops,
  2. struct kobject \*parent\_kobj)

//参数为”devices”, NULL,&priv->subsys.kobj

{

  1. struct kset \*kset;
  2. int error;
  3. //创建一个kset容器
  4. kset = kset\_create(name, uevent\_ops, parent\_kobj);
  5. if (!kset)
  6. return NULL;
  7. //注册创建的kset容器
  8. error = kset\_register(kset);
  9. if (error) \{
  10. kfree(kset);
  11. return NULL;
  12. \}
  13. 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) {

  1. //检测是否已经设置父对象
  2. if (!parent)
  3. //无则使用父容器为父对象
  4. parent = kobject\_get(&kobj->kset->kobj);
  5. //添加该kobj到父容器的链表中
  6. kobj\_kset\_join(kobj);
  7. //设置父对象
  8. kobj->parent = parent;

}

至此, devices的模型就建立好了,drivers模型的建立和devices是一致的,只是名字不同而已,我就不复述了,建立好的模型如下

![Image 1][]

好了~ 到了这里,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 = {

  1. .name = "test\_ts",
  2. .id = -1,
  3. //. resource
  4. //.dev

};

这个结构主要描述了设备的名字,ID和资源和私有数据,其中资源和私有数据我们在这里不使用,将在别的文章中进行讲解

int platform_device_register(struct platform_device *pdev)

{

  1. //设备属性的初始化
  2. device\_initialize(&pdev->dev);
  3. //将设备添加进platform里
  4. return platform\_device\_add(pdev);

}

void device_initialize(struct device *dev)

{

  1. dev->kobj.kset = devices\_kset; //设置kset为devices\_kset,则将设备挂接上了devices目录
  2. kobject\_init(&dev->kobj, &device\_ktype); //初始化kobeject,置ktype为device\_ktype
  3. klist\_init(&dev->klist\_children, klist\_children\_get,
  4. klist\_children\_put);
  5. INIT\_LIST\_HEAD(&dev->dma\_pools);
  6. INIT\_LIST\_HEAD(&dev->node);
  7. init\_MUTEX(&dev->sem);
  8. spin\_lock\_init(&dev->devres\_lock);
  9. INIT\_LIST\_HEAD(&dev->devres\_head);
  10. device\_init\_wakeup(dev, 0);
  11. set\_dev\_node(dev, -1);

}

int platform_device_add(struct platform_device *pdev)

{

  1. int i, ret = 0;
  2. if (!pdev)
  3. return -EINVAL;
  4. //检测是否设置了dev中的parent,无则赋为platform\_bus
  5. if (!pdev->dev.parent)
  6. pdev->dev.parent = &platform\_bus;
  7. //设置dev中的bus为platform\_bus\_type
  8. pdev->dev.bus = &platform\_bus\_type;
  9. //检测id,id为-1表明该设备只有一个,用设备名为bus\_id
  10. //不为1则表明该设备有数个,需要用序号标明bus\_id
  11. if (pdev->id != -1)
  12. snprintf(pdev->dev.bus\_id, BUS\_ID\_SIZE, "%s.%d", pdev->name,
  13. pdev->id);
  14. else
  15. strlcpy(pdev->dev.bus\_id, pdev->name, BUS\_ID\_SIZE);
  16. //增加资源到资源树中
  17. for (i = 0; i < pdev->num\_resources; i++) \{
  18. struct resource \*p, \*r = &pdev->resource;
  19. if (r->name == NULL)
  20. r->name = pdev->dev.bus\_id;
  21. p = r->parent;
  22. if (!p) \{
  23. if (r->flags & IORESOURCE\_MEM)
  24. p = &iomem\_resource;
  25. else if (r->flags & IORESOURCE\_IO)
  26. p = &ioport\_resource;
  27. \}
  28. if (p && insert\_resource(p, r)) \{
  29. printk(KERN\_ERR "%s: failed to claim resource %d\\n",pdev->dev.bus\_id, i);
  30. ret = -EBUSY;
  31. goto failed;
  32. \}
  33. \}
  34. pr\_debug("Registering platform device '%s'. Parent at %s\\n",pdev->dev.bus\_id, pdev->dev.parent->bus\_id);
  35. //添加设备到设备层次中
  36. ret = device\_add(&pdev->dev);
  37. if (ret == 0)
  38. return ret;

failed:

  1. while (--i >= 0)
  2. if (pdev->resource.flags & (IORESOURCE\_MEM|IORESOURCE\_IO))
  3. release\_resource(&pdev->resource);
  4. return ret;

}

int device_add(struct device *dev)

{

  1. struct device \*parent = NULL;
  2. struct class\_interface \*class\_intf;
  3. int error;
  4. dev = get\_device(dev);
  5. if (!dev || !strlen(dev->bus\_id)) \{
  6. error = -EINVAL;
  7. goto Done;
  8. \}
  9. pr\_debug("device: '%s': %s\\n", dev->bus\_id, \_\_func\_\_);
  10. //取得上层device,而dev->parent的赋值是在platform\_device\_add中的pdev->dev.parent = &platform\_bus完成的
  11. parent = get\_device(dev->parent);
  12. //以上层devices为准重设dev->kobj.parent
  13. setup\_parent(dev, parent);
  14. if (parent)
  15. set\_dev\_node(dev, dev\_to\_node(parent));
  16. //设置dev->kobj的名字和父对象,并建立相应目录
  17. error = kobject\_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus\_id);
  18. if (error)
  19. goto Error;
  20. if (platform\_notify)
  21. platform\_notify(dev);
  22. //一种新型的通知机制,但是platform中没有设置相应的结构,所以在这里跳过
  23. /\* notify clients of device entry (new way) \*/
  24. if (dev->bus)
  25. blocking\_notifier\_call\_chain(&dev->bus->p->bus\_notifier,BUS\_NOTIFY\_ADD\_DEVICE, dev);
  26. //建立uevent文件
  27. error = device\_create\_file(dev, &uevent\_attr);
  28. if (error)
  29. goto attrError;
  30. //设备有设备号则建立dev文件
  31. if (MAJOR(dev->devt)) \{
  32. error = device\_create\_file(dev, &devt\_attr);
  33. if (error)
  34. goto ueventattrError;
  35. \}
  36. //建立subsystem连接文件连接到所属class
  37. error = device\_add\_class\_symlinks(dev);
  38. if (error)
  39. goto SymlinkError;
  40. //添加dev的描述文件
  41. error = device\_add\_attrs(dev);
  42. if (error)
  43. goto AttrsError;
  44. //添加链接文件至所属bus
  45. error = bus\_add\_device(dev);
  46. if (error)
  47. goto BusError;
  48. //添加power文件
  49. error = device\_pm\_add(dev);
  50. if (error)
  51. goto PMError;
  52. kobject\_uevent(&dev->kobj, KOBJ\_ADD);
  53. //检测驱动中有无适合的设备进行匹配,现在只添加了设备,还没有加载驱动,所以不会进行匹配
  54. bus\_attach\_device(dev);
  55. if (parent)
  56. klist\_add\_tail(&dev->knode\_parent, &parent->klist\_children);
  57. if (dev->class) \{
  58. down(&dev->class->sem);
  59. list\_add\_tail(&dev->node, &dev->class->devices);
  60. list\_for\_each\_entry(class\_intf, &dev->class->interfaces, node)
  61. if (class\_intf->add\_dev)
  62. class\_intf->add\_dev(dev, class\_intf);
  63. up(&dev->class->sem);
  64. \}

Done:

  1. put\_device(dev);
  2. return error;

PMError:

  1. bus\_remove\_device(dev);

BusError:

  1. if (dev->bus)
  2. blocking\_notifier\_call\_chain(&dev->bus->p->bus\_notifier,BUS\_NOTIFY\_DEL\_DEVICE, dev);
  3. device\_remove\_attrs(dev);

AttrsError:

  1. device\_remove\_class\_symlinks(dev);

SymlinkError:

  1. if (MAJOR(dev->devt))
  2. device\_remove\_file(dev, &devt\_attr);

ueventattrError:

  1. device\_remove\_file(dev, &uevent\_attr);

attrError:

  1. kobject\_uevent(&dev->kobj, KOBJ\_REMOVE);
  2. kobject\_del(&dev->kobj);

Error:

  1. cleanup\_device\_parent(dev);
  2. if (parent)
  3. put\_device(parent);
  4. goto Done;

}

static void setup_parent(struct device *dev, struct device *parent)

{

  1. struct kobject \*kobj;
  2. //取得上层device的kobj
  3. kobj = get\_device\_parent(dev, parent);
  4. //kobj不为空则重设dev->kobj.parent
  5. if (kobj)
  6. dev->kobj.parent = kobj;

}

static struct kobject *get_device_parent(struct device *dev,

  1. struct device \*parent)

{

  1. int retval;
  2. //因为dev->class为空,所以跳过这段代码
  3. if (dev->class) \{
  4. struct kobject \*kobj = NULL;
  5. struct kobject \*parent\_kobj;
  6. struct kobject \*k;
  7. if (parent == NULL)
  8. parent\_kobj = virtual\_device\_parent(dev);
  9. else if (parent->class)
  10. return &parent->kobj;
  11. else
  12. parent\_kobj = &parent->kobj;
  13. spin\_lock(&dev->class->class\_dirs.list\_lock);
  14. list\_for\_each\_entry(k, &dev->class->class\_dirs.list, entry)
  15. if (k->parent == parent\_kobj) \{
  16. kobj = kobject\_get(k);
  17. break;
  18. \}
  19. spin\_unlock(&dev->class->class\_dirs.list\_lock);
  20. if (kobj)
  21. return kobj;
  22. k = kobject\_create();
  23. if (!k)
  24. return NULL;
  25. k->kset = &dev->class->class\_dirs;
  26. retval = kobject\_add(k, parent\_kobj, "%s", dev->class->name);
  27. if (retval < 0) \{
  28. kobject\_put(k);
  29. return NULL;
  30. \}
  31. return k;
  32. \}
  33. if (parent)
  34. //返回上层device的kobj
  35. return &parent->kobj;
  36. return NULL;

}

在bus_attach_device中虽然没有成功进行匹配,但是有很重要的一步为之后正确的匹配打下基础

void bus_attach_device(struct device *dev)

{

  1. struct bus\_type \*bus = dev->bus;
  2. int ret = 0;
  3. if (bus) \{
  4. if (bus->p->drivers\_autoprobe)
  5. ret = device\_attach(dev);
  6. WARN\_ON(ret < 0);
  7. if (ret >= 0)
  8. klist\_add\_tail(&dev->knode\_bus, &bus->p->klist\_devices);
  9. \}

}

klist_add_tail(&dev->knode_bus, &bus->p->klist_devices)就是这一行

在这一行代码中将设备挂载到了bus下的devices链表下,这样,当驱动请求匹配的时候,platform总线就会历遍devices链表为驱动寻找合适的设备

现在来看一下test_device的模型
![Image 1][]

然后platform_driver_unregister,他的参数 test_driver的结构如下

static struct platform_driver test_driver = {

  1. .probe = test\_probe,
  2. .remove = test\_remove,
  3. .driver = \{
  4. .name = "test\_ts",
  5. .owner = THIS\_MODULE,
  6. \},

};

int platform_driver_register(struct platform_driver *drv)

{

  1. drv->driver.bus = &platform\_bus\_type;
  2. if (drv->probe)
  3. drv->driver.probe = platform\_drv\_probe;
  4. if (drv->remove)
  5. drv->driver.remove = platform\_drv\_remove;
  6. if (drv->shutdown)
  7. drv->driver.shutdown = platform\_drv\_shutdown;
  8. if (drv->suspend)
  9. drv->driver.suspend = platform\_drv\_suspend;
  10. if (drv->resume)
  11. drv->driver.resume = platform\_drv\_resume;
  12. return driver\_register(&drv->driver);

}

从上面代码可以看出,在platform_driver中设置了probe, remove, shutdown, suspend或resume函数的话

则drv->driver也会设置成platform对应的函数

int driver_register(struct device_driver *drv)

{

  1. int ret;
  2. struct device\_driver \*other;
  3. //检测总线的操作函数和驱动的操作函数是否同时存在,同时存在则提示使用总线提供的操作函数
  4. if ((drv->bus->probe && drv->probe) ||
  5. (drv->bus->remove && drv->remove) ||
  6. (drv->bus->shutdown && drv->shutdown))
  7. printk(KERN\_WARNING "Driver '%s' needs updating - please use ""bus\_type methods\\n", drv->name);
  8. //检测是否已经注册过
  9. other = driver\_find(drv->name, drv->bus);
  10. if (other) \{
  11. put\_driver(other);
  12. printk(KERN\_ERR "Error: Driver '%s' is already registered, “"aborting...\\n", drv->name);
  13. return -EEXIST;
  14. \}
  15. //添加驱动到总线上
  16. ret = bus\_add\_driver(drv);
  17. if (ret)
  18. return ret;
  19. ret = driver\_add\_groups(drv, drv->groups);
  20. if (ret)
  21. bus\_remove\_driver(drv);
  22. return ret;

}

int bus_add_driver(struct device_driver *drv)

{

  1. struct bus\_type \*bus;
  2. struct driver\_private \*priv;
  3. int error = 0;
  4. //取bus结构
  5. bus = bus\_get(drv->bus);
  6. if (!bus)
  7. return -EINVAL;
  8. pr\_debug("bus: '%s': add driver %s\\n", bus->name, drv->name);
  9. //分配驱动私有数据
  10. priv = kzalloc(sizeof(\*priv), GFP\_KERNEL);
  11. if (!priv) \{
  12. error = -ENOMEM;
  13. goto out\_put\_bus;
  14. \}
  15. //初始化klist\_devices链表
  16. klist\_init(&priv->klist\_devices, NULL, NULL);
  17. //互相关联
  18. priv->driver = drv;
  19. drv->p = priv;
  20. //设置私有数据的父容器,在这一步中,设置了kset为platform下的drivers\_kset结构,也就是drivers呢个目录
  21. priv->kobj.kset = bus->p->drivers\_kset;
  22. //初始化kobj对象,设置容器操作集并建立相应的目录,这里由于没有提供parent,所以会使用父容器中的kobj为父对象
  23. error = kobject\_init\_and\_add(&priv->kobj, &driver\_ktype, NULL,
  24. "%s", drv->name);
  25. if (error)
  26. goto out\_unregister;
  27. //检测所属总线的drivers\_autoprobe属性是否为真
  28. //为真则进行与设备的匹配,到这里,就会与我们之前注册的test\_device连接上了,至于如何连接,进行了什么操作,将在别的文章中详细描述
  29. if (drv->bus->p->drivers\_autoprobe) \{
  30. error = driver\_attach(drv);
  31. if (error)
  32. goto out\_unregister;
  33. \}
  34. //挂载到所属总线驱动链表上
  35. klist\_add\_tail(&priv->knode\_bus, &bus->p->klist\_drivers);
  36. module\_add\_driver(drv->owner, drv);
  37. //建立uevent属性文件
  38. error = driver\_create\_file(drv, &driver\_attr\_uevent);
  39. if (error) \{
  40. printk(KERN\_ERR "%s: uevent attr (%s) failed\\n",
  41. \_\_func\_\_, drv->name);
  42. \}
  43. //建立设备属性文件
  44. error = driver\_add\_attrs(bus, drv);
  45. if (error) \{
  46. printk(KERN\_ERR "%s: driver\_add\_attrs(%s) failed\\n",\_\_func\_\_, drv->name);
  47. \}
  48. error = add\_bind\_files(drv);
  49. if (error) \{
  50. printk(KERN\_ERR "%s: add\_bind\_files(%s) failed\\n",\_\_func\_\_, drv->name);
  51. \}
  52. kobject\_uevent(&priv->kobj, KOBJ\_ADD);
  53. return error;

out_unregister:

  1. kobject\_put(&priv->kobj);

out_put_bus:

  1. bus\_put(bus);
  2. return error;

}

到这里test_driver的模型就建立好了,图就是最上面的层次图,我就不再贴了

到这里一个基本的框架就建立起来了~

下面,我开始对kobject kset和ktype做分析

先说说关系,ktype与kobject和kset这两者之前的关系较少,让我画一个图,是这样的
![Image 1][]

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)

{

  1. struct bus\_type \*bus = dev->bus;
  2. int ret = 0;
  3. if (bus) \{
  4. if (bus->p->drivers\_autoprobe)
  5. ret = device\_attach(dev);
  6. WARN\_ON(ret < 0);
  7. if (ret >= 0)
  8. klist\_add\_tail(&dev->knode\_bus, &bus->p->klist\_devices);
  9. \}

}

中可以看见这么一段代码

if (bus->p->drivers_autoprobe)

  1. 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,

  1. 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) { \

  1. .attr = \{.name = \_\_stringify(\_name), .mode = \_mode \}, \\
  2. .show = \_show, \\
  3. .store = \_store, \\

}

最后bus_attr_drivers_autoprobe的模型如下

struct bus_attribute bus_attr_drivers_autoprobe

{

  1. .attr = \{

.name = “drivers_autoprobe”,

.mode = S_IWUSR | S_IRUGO

},

  1. .show = show\_drivers\_autoprobe,
  2. .store = store\_drivers\_autoprobe,

}

进入到bus_create_file中

int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)

//参数为(bus, &bus_attr_drivers_autoprobe)

{

  1. int error;
  2. if (bus\_get(bus)) \{
  3. error = sysfs\_create\_file(&bus->p->subsys.kobj, &attr->attr);
  4. bus\_put(bus);
  5. \} else
  6. error = -EINVAL;
  7. return error;

}

int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)

//参数为(&bus->p->subsys.kobj, &attr->attr)

{

  1. BUG\_ON(!kobj || !kobj->sd || !attr);
  2. 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)

{

  1. return sysfs\_add\_file\_mode(dir\_sd, attr, type, attr->mode);

}

int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,

  1. 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)

{

  1. umode\_t mode = (amode & S\_IALLUGO) | S\_IFREG;
  2. struct sysfs\_addrm\_cxt acxt;
  3. struct sysfs\_dirent \*sd;
  4. int rc;
  5. //在这一步中可以看出新建了一个节点
  6. sd = sysfs\_new\_dirent(attr->name, mode, type);
  7. if (!sd)
  8. return -ENOMEM;
  9. //这一步挂载了&bus\_attr\_drivers\_autoprobe->attr到节点中,为以后提取attr及上层结构做准备
  10. sd->s\_attr.attr = (void \*)attr;
  11. // dir\_sd也就是上层目录,在这里为platform\_bus\_type->p->subsys.kobj ->sd
  12. //也就是/sys/bus/platform这个目录
  13. sysfs\_addrm\_start(&acxt, dir\_sd);
  14. rc = sysfs\_add\_one(&acxt, sd);
  15. sysfs\_addrm\_finish(&acxt);
  16. if (rc)
  17. sysfs\_put(sd);
  18. return rc;

}

struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)

{

  1. char \*dup\_name = NULL;
  2. struct sysfs\_dirent \*sd;
  3. if (type & SYSFS\_COPY\_NAME) \{
  4. name = dup\_name = kstrdup(name, GFP\_KERNEL);
  5. if (!name)
  6. return NULL;
  7. \}
  8. sd = kmem\_cache\_zalloc(sysfs\_dir\_cachep, GFP\_KERNEL);
  9. if (!sd)
  10. goto err\_out1;
  11. if (sysfs\_alloc\_ino(&sd->s\_ino))
  12. goto err\_out2;
  13. atomic\_set(&sd->s\_count, 1);
  14. atomic\_set(&sd->s\_active, 0);
  15. sd->s\_name = name; //节点的名字为&bus\_attr\_drivers\_autoprobe->attr->name 也就是drivers\_autoprobe
  16. sd->s\_mode = mode;

sd->s_flags = type; //节点的type为SYSFS_KOBJ_ATTR

  1. return sd;

err_out2:

  1. kmem\_cache\_free(sysfs\_dir\_cachep, sd);

err_out1:

  1. kfree(dup\_name);
  2. 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,

  1. struct nameidata \*nd)

{

  1. struct dentry \*ret = NULL;
  2. struct sysfs\_dirent \*parent\_sd = dentry->d\_parent->d\_fsdata;
  3. struct sysfs\_dirent \*sd;
  4. struct inode \*inode;
  5. mutex\_lock(&sysfs\_mutex);
  6. sd = sysfs\_find\_dirent(parent\_sd, dentry->d\_name.name);
  7. if (!sd) \{
  8. ret = ERR\_PTR(-ENOENT);
  9. goto out\_unlock;
  10. \}
  11. //节点的初始化在这里
  12. inode = sysfs\_get\_inode(sd);
  13. if (!inode) \{
  14. ret = ERR\_PTR(-ENOMEM);
  15. goto out\_unlock;
  16. \}
  17. dentry->d\_op = &sysfs\_dentry\_ops;
  18. dentry->d\_fsdata = sysfs\_get(sd);
  19. d\_instantiate(dentry, inode);
  20. d\_rehash(dentry);

out_unlock:

  1. mutex\_unlock(&sysfs\_mutex);
  2. return ret;

}

struct inode * sysfs_get_inode(struct sysfs_dirent *sd)

{

  1. struct inode \*inode;
  2. inode = iget\_locked(sysfs\_sb, sd->s\_ino);
  3. if (inode && (inode->i\_state & I\_NEW))
  4. //为节点赋值
  5. sysfs\_init\_inode(sd, inode);
  6. return inode;

}

static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)

{

  1. struct bin\_attribute \*bin\_attr;
  2. inode->i\_blocks = 0;
  3. inode->i\_mapping->a\_ops = &sysfs\_aops;
  4. inode->i\_mapping->backing\_dev\_info = &sysfs\_backing\_dev\_info;
  5. inode->i\_op = &sysfs\_inode\_operations;
  6. inode->i\_ino = sd->s\_ino;
  7. lockdep\_set\_class(&inode->i\_mutex, &sysfs\_inode\_imutex\_key);
  8. if (sd->s\_iattr) \{
  9. set\_inode\_attr(inode, sd->s\_iattr);
  10. \} else
  11. set\_default\_inode\_attr(inode, sd->s\_mode);
  12. //判断类型
  13. switch (sysfs\_type(sd)) \{
  14. case SYSFS\_DIR:
  15. inode->i\_op = &sysfs\_dir\_inode\_operations;
  16. inode->i\_fop = &sysfs\_dir\_operations;
  17. inode->i\_nlink = sysfs\_count\_nlink(sd);
  18. break;
  19. //还记得在注册的时候有一个参数为SYSFS\_KOBJ\_ATTR赋到了sd->s\_flags上面吧
  20. case SYSFS\_KOBJ\_ATTR:
  21. inode->i\_size = PAGE\_SIZE;
  22. inode->i\_fop = &sysfs\_file\_operations;
  23. break;
  24. case SYSFS\_KOBJ\_BIN\_ATTR:
  25. bin\_attr = sd->s\_bin\_attr.bin\_attr;
  26. inode->i\_size = bin\_attr->size;
  27. inode->i\_fop = &bin\_fops;
  28. break;
  29. case SYSFS\_KOBJ\_LINK:
  30. inode->i\_op = &sysfs\_symlink\_inode\_operations;
  31. break;
  32. default:
  33. BUG();
  34. \}
  35. unlock\_new\_inode(inode);

}

sysfs_file_operations的结构如下,之后open和read,write都明了了

const struct file_operations sysfs_file_operations = {

  1. .read = sysfs\_read\_file,
  2. .write = sysfs\_write\_file,
  3. .llseek = generic\_file\_llseek,
  4. .open = sysfs\_open\_file,
  5. .release = sysfs\_release,
  6. .poll = sysfs\_poll,

};

有关在哪调用open还是请查阅我的另一篇文章<从文件到设备>中 nameidata_to_filp之后的操作

好的~ 现在进入到了sysfs_open_file中

static int sysfs_open_file(struct inode *inode, struct file *file)

{

  1. struct sysfs\_dirent \*attr\_sd = file->f\_path.dentry->d\_fsdata;
  2. //要重的取值,在这里取得了drivers\_autoprobe的目录platform的kproject
  3. struct kobject \*kobj = attr\_sd->s\_parent->s\_dir.kobj;
  4. struct sysfs\_buffer \*buffer;
  5. struct sysfs\_ops \*ops;
  6. int error = -EACCES;
  7. if (!sysfs\_get\_active\_two(attr\_sd))
  8. return -ENODEV;
  9. if (kobj->ktype && kobj->ktype->sysfs\_ops)
  10. //这里可谓是ktype实现中的核心,在这里ops设置成了platform\_bus\_type中kobject->ktype的sysfs\_ops
  11. ops = kobj->ktype->sysfs\_ops;
  12. else \{
  13. printk(KERN\_ERR "missing sysfs attribute operations for ""kobject: %s\\n", kobject\_name(kobj));
  14. WARN\_ON(1);
  15. goto err\_out;
  16. \}
  17. if (file->f\_mode & FMODE\_WRITE) \{
  18. if (!(inode->i\_mode & S\_IWUGO) || !ops->store)
  19. goto err\_out;
  20. \}
  21. if (file->f\_mode & FMODE\_READ) \{
  22. if (!(inode->i\_mode & S\_IRUGO) || !ops->show)
  23. goto err\_out;
  24. \}
  25. error = -ENOMEM;
  26. buffer = kzalloc(sizeof(struct sysfs\_buffer), GFP\_KERNEL);
  27. if (!buffer)
  28. goto err\_out;
  29. mutex\_init(&buffer->mutex);
  30. buffer->needs\_read\_fill = 1;
  31. //然后将设置好的ops挂载到buffer上
  32. buffer->ops = ops;
  33. //再将buffer挂载到file->private\_data中
  34. file->private\_data = buffer;
  35. error = sysfs\_get\_open\_dirent(attr\_sd, buffer);
  36. if (error)
  37. goto err\_free;
  38. sysfs\_put\_active\_two(attr\_sd);
  39. return 0;

err_free:

  1. kfree(buffer);

err_out:

  1. sysfs\_put\_active\_two(attr\_sd);
  2. return error;

}

现在已经为read和write操作准备好了

马上进入到read操作中
![Image 1][]
整个流程如上图所示,如何进入到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)

{

  1. struct sysfs\_buffer \* buffer = file->private\_data;
  2. ssize\_t retval = 0;
  3. mutex\_lock(&buffer->mutex);
  4. if (buffer->needs\_read\_fill || \*ppos == 0) \{
  5. //主要操作在fill\_read\_buffer中
  6. retval = fill\_read\_buffer(file->f\_path.dentry,buffer);
  7. if (retval)
  8. goto out;
  9. \}
  10. pr\_debug("%s: count = %zd, ppos = %lld, buf = %s\\n",\_\_func\_\_, count, \*ppos, buffer->page);
  11. retval = simple\_read\_from\_buffer(buf, count, ppos, buffer->page,
  12. buffer->count);

out:

  1. mutex\_unlock(&buffer->mutex);
  2. return retval;

}

static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)

{

  1. struct sysfs\_dirent \*attr\_sd = dentry->d\_fsdata;
  2. //取得父目录的kobject,也就是platform的kobject
  3. struct kobject \*kobj = attr\_sd->s\_parent->s\_dir.kobj;
  4. //还记得这个buffer->ops在什么时候进行赋值的么?
  5. struct sysfs\_ops \* ops = buffer->ops;
  6. int ret = 0;
  7. ssize\_t count;
  8. if (!buffer->page)
  9. buffer->page = (char \*) get\_zeroed\_page(GFP\_KERNEL);
  10. if (!buffer->page)
  11. return -ENOMEM;
  12. if (!sysfs\_get\_active\_two(attr\_sd))
  13. return -ENODEV;
  14. buffer->event = atomic\_read(&attr\_sd->s\_attr.open->event);
  15. //调用ops->show 也就是bus\_sysfs\_ops->show 具体就是bus\_attr\_show了
  16. //参数为父目录的kobject, bus\_attr\_drivers\_autoprobe->attr,和一段char信息
  17. count = ops->show(kobj, attr\_sd->s\_attr.attr, buffer->page);
  18. sysfs\_put\_active\_two(attr\_sd);
  19. if (count >= (ssize\_t)PAGE\_SIZE) \{
  20. print\_symbol("fill\_read\_buffer: %s returned bad count\\n",
  21. (unsigned long)ops->show);
  22. /\* Try to struggle along \*/
  23. count = PAGE\_SIZE - 1;
  24. \}
  25. if (count >= 0) \{
  26. buffer->needs\_read\_fill = 0;
  27. buffer->count = count;
  28. \} else \{
  29. ret = count;
  30. \}
  31. return ret;

}

现在进入bus_attr_show中

static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,char *buf)

{

  1. //提取attr的上层结构,也就是bus\_attr\_drivers\_autoprobe
  2. struct bus\_attribute \*bus\_attr = to\_bus\_attr(attr);
  3. //提取kobj的上上层结构,也就是bus\_type\_private
  4. struct bus\_type\_private \*bus\_priv = to\_bus(kobj);
  5. ssize\_t ret = 0;
  6. if (bus\_attr->show)
  7. //终于到了这里,最后的调用,调用bus\_attr\_drivers\_autoprobe.show ,也就是show\_drivers\_autoprobe
  8. //参数为bus\_priv->bus,也就是platform\_bus\_type , 及一段char信息
  9. ret = bus\_attr->show(bus\_priv->bus, buf);
  10. return ret;

}

static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)

{

  1. 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,

  1. const char \*buf, size\_t count)

{

  1. if (buf\[0\] == '0')
  2. bus->p->drivers\_autoprobe = 0;
  3. else
  4. bus->p->drivers\_autoprobe = 1;
  5. return count;

}

不进行打印而对内核的参数进行了修改而已

好~ 现在让我们来看看kobject吧

kobject的结构如下

struct kobject {

  1. const char \*name; //kobject的名字
  2. struct kref kref; //kobject的原子操作
  3. struct list\_head entry;
  4. struct kobject \*parent; //父对象
  5. struct kset \*kset; //父容器
  6. struct kobj\_type \*ktype; //ktype
  7. struct sysfs\_dirent \*sd; //文件节点
  8. unsigned int state\_initialized:1;
  9. unsigned int state\_in\_sysfs:1;
  10. unsigned int state\_add\_uevent\_sent:1;
  11. 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
![Image 1][]

容器提供了一种默认的层次~ 但也可以人工设置层次

对于kobject现在我只理解了这么多,欢迎大家指出有疑问的地方

最后是kset,kset比较简单,看下面的结构

struct kset {

  1. struct list\_head list;
  2. spinlock\_t list\_lock;
  3. struct kobject kobj;
  4. 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的机制了

[Image 1]:

发表评论

表情:
评论列表 (有 0 条评论,221人围观)

还没有评论,来说两句吧...

相关阅读