- 论坛徽章:
- 0
|
2009.9.14
The Linux Device Model
这个抽象模型支持:
Power 管理和系统 shut down
电源管理需要知道系统的拓扑,必须USB HC不能在有设备连接的时候shut down. Linux现有设备模型(从2.6开始)可以提供遍历系统设备的方法.
和user sapce 交互
sysfs, 越来越多的设备使用sysfs来改变参数,而抛弃原来的dev 模式.
Hotpluggable devices
和user space交互 plug/unplugging消息. (uevent)
Device classes
设备分类.
Object lifecycles
各种设备生命周期/相顾关系的抽象管理
有图为例
![]()
![]()
![]()
Kobjects, Ksets, and Subsystems
struct kobject {
//最初只是引用计数,但是现在复杂多了.
Reference counting of objects:
提供引用计数
Sysfs representation
提供sysfs接口
Data structure glue
提供'设备关系'管理的各种工具,数据结构
Hotplug event handling
uevent
}
Kobject Basics
Embedding kobjects
kobject 就是要嵌入在其他的设备结构中,以使其和linux提供的设备模型关联起来. 比如cdev
struct cdev {
struct kobject kobj;
struct module *owner;
struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
将指向kobject的指针kp转换成cdev:
struct cdev *device = container_of(kp, struct cdev, kobj);
Kobject initialization
1. 将所有域清零.
2. 调用kobject_init , 引用计数被置成1
3. 至少要设置名子: int kobject_set_name(struct kobject *kobj, const char *format, ...);
4. 其他应该设置的域: ktype, kset, parent.
Reference count manipulation
struct kobject *kobject_get(struct kobject *kobj); //必须检查返回值, 在销毁中的kobj就会返回NULL
void kobject_put(struct kobject *kobj);
对应模块的应用技术也得保持好.如cdev:
struct kobject *cdev_get(struct cdev *p)
{
struct module *owner = p->owner;
struct kobject *kobj;
if (owner && !try_module_get(owner))
return NULL;
kobj = kobject_get(&p->kobj);
if (!kobj)
module_put(owner);
return kobj;
}
Release functions and kobject types
因为, sysfs, 以及open和close, driver是不知道自己什么时候引用技术到0的, 故而, kobj提供了call back: release. 就下下面这样:
void my_object_release(struct kobject *kobj)
{
struct my_object *mine = container_of(kobj, struct my_object, kobj);
.....
/* Perform any additional cleanup on this object, then... */
kfree(mine);
}
release函数是必须有的, 调用release之前是不能释放kobj的..
这个relase 函数是存储在ktype中的:
struct kobj_type {
void (*release)(struct kobject *);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
kobj需要关连一个ktype, 有两种方式: 直接用kobj内的ktype指针, 但是如果kobj是属于一个kset的,那么, ktype将由kset来提供, 函数
struct kobj_type *get_ktype(struct kobject *kobj);
用于正确的获取ktype.
Kobject Hierarchies, Ksets, and Subsystems
体系中有两种kobj之间的连接关系: parent指针, 和kset. parent的例子: usb设备的parent或许是一个hub. parent指针用于指导设备在sysfs中的位置.
Ksets
kset用于代表一组相同type的kobj, 他自己也是kboject. kset就是提供给sysfs使用的,别无他用.(一个目录,kobject出现的时候都是目录,只有attr是文件). koject无须出现自sysfs中,但是kset管理的obj都通过kset出现在sysfs里.
一般在kobject创建的时候加入kset. 两个步骤:
1.) 填充kobject的kset指针
2) 调用 int kobject_add(struct kobject *kobj);
extern int kobject_register(struct kobject *kobj);是kobject_init and kobject_add的组合.
从kset删除: void kobject_del(struct kobject *kobj);
典型的kset和他聚合的kobject关系如下图:
![]()
1. 每个kobject都是嵌入在一个具体的type内的
2. 其parent不一定是那个kset容器, 但是这种情况很少.
kset 操作函数
void kset_init(struct kset *kset);
int kset_add(struct kset *kset);
int kset_register(struct kset *kset);
void kset_unregister(struct kset *kset);
这些函数一般是操作kset内的kobject的.
struct kset *kset_get(struct kset *kset);
void kset_put(struct kset *kset);
kobject_set_name(&my_set->kobj, "The name");
上面提到了, kset内的ktype指针指向共同的ktype. 这些被聚合的kobject的ktype指针保持为NULL.(一般情况) . kset 还有一个指向subsystem的指针.
kset 补充说明:
Subsystems
在/sys目录下的直接子目录一般就是subsystem了. 一个subsysem其实就是一个kset.
struct subsystem {
struct kset kset;
struct rw_semaphore rwsem;
};
kset必须属于一个subsystem, kset内有指针直接找到所属susbsystem, 但是从subsys不能简单找到属于他的所有的kset. subsys内的rwsem 保护kset的内部链表.
声明一个subsystem的方式:
decl_subsys(name, struct kobj_type *type, struct kset_hotplug_ops *hotplug_ops);
subsystem的操作函数大部分也四操作其内嵌的kset:
void subsystem_init(struct subsystem *subsys);
int subsystem_register(struct subsystem *subsys);
void subsystem_unregister(struct subsystem *subsys);
struct subsystem *subsys_get(struct subsystem *subsys)
void subsys_put(struct subsystem *subsys);
kset/subsystem的补充说明
注: 新的内核中, 采用下面的函数动态创建subsystem居多,而不是用上面提到的宏.
kset_create_and_add
通过这种方式创建的kset还有: classes_init. devices_init, slab_sysfs_init, param_sysfs_init /*module*/
另外的几个目录, fs, kernel 就是一个kobject, 而非一个kset, 搜索方法:
bash#> grep kobject_create_and_add . -r | grep "NULL"
./arch/powerpc/platforms/pseries/power.c: power_kobj = kobject_create_and_add("power", NULL);
./kernel/power/main.c: power_kobj = kobject_create_and_add("power", NULL);
./kernel/ksysfs.c: kernel_kobj = kobject_create_and_add("kernel", NULL);
./fs/namespace.c: fs_kobj = kobject_create_and_add("fs", NULL);
./block/genhd.c: block_depr = kobject_create_and_add("block", NULL);
./drivers/base/firmware.c: firmware_kobj = kobject_create_and_add("firmware", NULL);
./drivers/base/core.c: dev_kobj = kobject_create_and_add("dev", NULL);
./drivers/base/hypervisor.c: hypervisor_kobj = kobject_create_and_add("hypervisor", NULL);
struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error;
kset = kset_create(name, uevent_ops, parent_kobj); /*kset 内嵌的obj在 buses_init时, 父obj是NULL*/
/*同时, kset.kobj.kset是NULL, kset自己不属于kset*/
if (!kset)
return NULL;
error = kset_register(kset);/*[color="#0000ff"]kset.kobj的其parent和kset都是NULL,所以创建的目录在sysfs_root下,见kobject_add_internal*/
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
在kobject_add_internal里有这样的策略: 如果一个kobj属于一个kset, 并且没有指定这个kobj的parent, 则这个kobj就位于kset.obj所对应的那个目录下.
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
......
parent = kobject_get(kobj->parent);
/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj); /*挂入kset链表而已*/
kobj->parent = parent;
}
......
error = create_dir(kobj);
}
Low-Level Sysfs Operations
* 每个kobject,kset 对应sysfs的一个目录
* kobject的attr对应目录内的一个文件
* 应该包含
* kobject_add 复杂将kobject加入sysfs,上面kobject加入kset的时候就调用了这个函数
*上面的补充说明已经说过了: 如果没有parent也没有kset,出现在sysfs根目录, 有parent就出现在parent内, 如果不指定parent, 一般指向所属的kset(的内嵌kobject)
Default Attributes
属性在ktype中指定.
struct kobj_type {
void (*release)(struct kobject *);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
struct attribute {
char *name;
struct module *owner;
mode_t mode; //S_IRUGO for read-only, |S_IWUSR
};
struct sysfs_ops { //提供属性的操作函数
ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buffer); //输出buffer 是 page size,返回输出总字节数
ssize_t (*store)(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size);//返回已经解码的字节数
};
Nondefault Attributes
int sysfs_create_file(struct kobject *kobj, struct attribute *attr); //成功返回0
int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
//逻辑删除(对应文件就不再出现在/sys目录了),已经打开的还可以继续用,就是有user正在访问的话,直到user close才能真正的删除
Binary Attributes
比如下载firmeware, 就要binary了.
struct bin_attribute {
struct attribute attr;
size_t size; //最大大小
ssize_t (*read)(struct kobject *kobj, char *buffer,loff_t pos, size_t size);
ssize_t (*write)(struct kobject *kobj, char *buffer, loff_t pos, size_t size);
};
只能动态注册,需要有办法确定何时数据发送完毕. 一次一个page的数据.
int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr);
int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);
Symbolic Links
int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name); 给kobj创建一个属性, 一个名字为name,指向target的符号连接.
void sysfs_remove_link(struct kobject *kobj, char *name);
Hotplug Event Generation
就是从kernel调用user space 的/sbin/hotplug, 这个功能由设备模型中的uevent来完成. 事件产生的时机是kobject_add or kobject_del. 的时候.
Hotplug Operations
struct kset_hotplug_ops {
int (*filter)(struct kset *kset, struct kobject *kobj); /*如果返回0, 对应事件不报给user space*/
char *(*name)(struct kset *kset, struct kobject *kobj); /*产生传递给user space程序的唯一一个参数:子系统名称*/
int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp, int num_envp, char *buffer, int buffer_size);
//其他参数,通过环境变量传递,这里产生所有需要的环境变量 : NAME=value , 正常情况返回0, 否则事件abort
};
如果当前object 的kset内没有 hotplug操作,就顺着parent向上找,知道有个kset设置了hotplug操作.
就调用这个找到的hotplug函数. 比如硬盘, 至少有block设备, 分区,和request queue几个kobject.
但是请求队列是没人在意的, 故而可以如下写fileter:
static int block_hotplug_filter(struct kset *kset, struct kobject *kobj)
{
struct kobj_type *ktype = get_ktype(kobj);
return ((ktype = = &ktype_block) || (ktype = = &ktype_part));
}
Buses, Devices, and Drivers
尝试建立lddbus, 将scullp driver连接到这个bus.
Buses
bus可以连接到另一个bus,比如usb基本都是连接到PCI的, 而soc系统, usb一般连接到虚拟的platform bus.
*
*
struct bus_type { //bus本身属于子系统 /sys/bus
char *name; //bus 名字
struct subsystem subsys; //bus本身是一个子系统(就是sysfs中的一个目录)
struct kset drivers; //属于这个bus的driver (新的内核中移入bus_type_private)
struct kset devices; //属于这个bus的device (新的内核中移入bus_type_private)
int (*match)(struct device *dev, struct device_driver *drv);
struct device *(*add)(struct device * parent, char * bus_id);
int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
/* Some fields omitted */
}
Bus registration
struct bus_type ldd_bus_type = {
.name = "ldd",
.match = ldd_match,
.hotplug = ldd_hotplug,
};
ret = bus_register(&ldd_bus_type);
if (ret)
return ret;
void bus_unregister(struct bus_type *bus); //删除bus前,driver和设备当然得都被正确的卸载掉.
Bus methods
int (*match)(struct device *device, struct device_driver *driver); //入果driver可以处理这个device,返回非零值.
int (*hotplug) (struct device *device, char **envp, int num_envp, char *buffer, int buffer_size);
//bus层的hotplug给uevent添加环境变量, 如
static int ldd_hotplug(struct device *dev, char **envp, int num_envp,char *buffer, int buffer_size)
{
envp[0] = buffer;
if (snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s", Version) >= buffer_size)
return -ENOMEM;
envp[1] = NULL;
return 0;
}
Iterating over devices and drivers
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *));
如果fn返回非0值,遍历终止,这个值被传递给bus_for_each_dev的返回值. data传递给fn.
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *));
这两个函数都会对bus的rwsem加锁.
Bus attributes
struct bus_attribute { //和kobject类似,只是每个属性都有单独的show,store
struct attribute attr;
ssize_t (*show)(struct bus_type *bus, char *buf);
ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};
BUS_ATTR(name, mode, show, store); 用于帮助定义属性,
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr); //所有属性必须手动调用注册函数.
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr); //反之依然
如lddbus的属性
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
.....
if (bus_create_file(&ldd_bus_type, &bus_attr_version))
printk(KERN_NOTICE "Unable to create version attribute\n");
....
Devices
struct device {
struct device *parent; //所连接到的设备/总线/hub
struct kobject kobj; //这个设备本身的kobject,device->kobj->parent 应该等于 &device->parent->kobj.
char bus_id[BUS_ID_SIZE]; //设备唯一标识,比如PCI和usb的那个标准ID字符串
struct bus_type *bus; //连接到的bus
struct device_driver *driver; //谁管理这个设备
void *driver_data; //自己存的cookie
void (*release)(struct device *dev); //实际上是从内嵌的kobj的release内调用的.
/* Several fields omitted */
};
Device registration
int device_register(struct device *dev);
void device_unregister(struct device *dev);
ldd bus 本身也是一个设备,看他的注册:
static void ldd_bus_release(struct device *dev)
{
printk(KERN_DEBUG "lddbus release\n");
}
struct device ldd_bus = { //top level的bus, 所以parent和bus指针为空.
.bus_id = "ldd0",
.release = ldd_bus_release
};
ret = device_register(&ldd_bus);
if (ret)
printk(KERN_NOTICE "Unable to register ldd0\n");
注册完成后, 设备目录出现在/sys/devices/ldd0, 以后的设备也出现在这里.
Device attributes
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, char *buf);
ssize_t (*store)(struct device *dev, const char *buf, size_t count);
};
DEVICE_ATTR(name, mode, show, store);
int device_create_file(struct device *device, struct device_attribute *entry);
void device_remove_file(struct device *dev, struct device_attribute *attr);
Device structure embedding
一般, 设备也是作为内嵌对象使用的, 供第三级设备模型使用,如pci_dev, usb_dev. ldd也可以创建自己的设备类型:
struct ldd_device {
char *name;
struct ldd_driver *driver;
struct device dev;
};
#define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);
int register_ldd_device(struct ldd_device *ldddev)
{
ldddev->dev.bus = &ldd_bus_type;
ldddev->dev.parent = &ldd_bus;
ldddev->dev.release = ldd_dev_release;
strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE);
return device_register(&ldddev->dev);
}
EXPORT_SYMBOL(register_ldd_device);
Device Drivers
struct device_driver {
char *name; //driver 名称
struct bus_type *bus; //驱动的设备所属bus, 含有此bus上driver的共有属性
struct kobject kobj; //driver的kobj,
struct list_head devices; //用这个driver的设备
int (*probe)(struct device *dev); //探测设备是否存在
int (*remove)(struct device *dev); //设备从系统移除时调用
void (*shutdown) (struct device *dev); //系统shutdown时调用
....
};
int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *drv, char *buf);
ssize_t (*store)(struct device_driver *drv, const char *buf, size_t count);
};
DRIVER_ATTR(name, mode, show, store);
int driver_create_file(struct device_driver *drv, struct driver_attribute *attr);
void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr);
Driver structure embedding
driver上面也是还有包装的. 以ldd为例
struct ldd_driver {
char *version;
struct module *module;
struct device_driver driver;
struct driver_attribute version_attr;
};
#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);
int register_ldd_driver(struct ldd_driver *driver)
{
int ret;
driver->driver.bus = &ldd_bus_type;
ret = driver_register(&driver->driver);
if (ret)
return ret;
driver->version_attr.attr.name = "version";
driver->version_attr.attr.owner = driver->module;
driver->version_attr.attr.mode = S_IRUGO;
driver->version_attr.show = show_version;
driver->version_attr.store = NULL;
return driver_create_file(&driver->driver, &driver->version_attr);
}
sculld 创建的 ldd_driver:
static struct ldd_driver sculld_driver = {
.version = "$Revision: 1.1 $",
.module = THIS_MODULE,
.driver = {
.name = "sculld",
},
};
调用register_ldd_driver 后:
$ tree /sys/bus/ldd/drivers
/sys/bus/ldd/drivers
`-- sculld
|-- sculld0 -> ../../../../devices/ldd0/sculld0
|-- sculld1 -> ../../../../devices/ldd0/sculld1
|-- sculld2 -> ../../../../devices/ldd0/sculld2
|-- sculld3 -> ../../../../devices/ldd0/sculld3
`-- version
Classes
*/sys/class, 功能分类. 除了特殊的/sys/block. dirver是不用做专门的支持的,高层代码自动处理.
*如果driver 创建class, 则自己完全拥有他, 不用考虑其他模块的引用问题, 并且user interface很友好.
simple calss 接口 (提供设备号给user space)
struct class_simple *class_simple_create(struct module *owner, char *name); //用IS_ERR检查返回值
void class_simple_destroy(struct class_simple *cs);
struct class_device *class_simple_device_add(struct class_simple *cs, dev_t devnum,
struct device *device, //如果不为NULL, 则符号连接到 /sys/device
const char *fmt, ...);
int class_simple_set_hotplug(struct class_simple *cs, int
(*hotplug)(struct class_device *dev, char **envp, int num_envp, char
*buffer, int buffer_size));
void class_simple_device_remove(dev_t dev);
The Full Class Interface
struct class {
char *name; //唯一的名称,/sys/class
struct class_attribute *class_attrs; //class属性
struct class_device_attribute *class_dev_attrs; //calss 对应设备的属性
int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
void (*release)(struct class_device *dev); //设备删除时调用
void (*class_release)(struct class *class); //calss 本身删除时被调用
/* Some fields omitted */
};
int class_register(struct class *cls);
void class_unregister(struct class *cls);
struct class_attribute {
struct attribute attr;
ssize_t (*show)(struct class *cls, char *buf);
ssize_t (*store)(struct class *cls, const char *buf, size_t count);
};
CLASS_ATTR(name, mode, show, store);
int class_create_file(struct class *cls, const struct class_attribute *attr);
void class_remove_file(struct class *cls, const struct class_attribute *attr);
Class devices
struct class_device {
struct kobject kobj;
struct class *class;
struct device *dev; //符号连接到 /sys/device, 如果非空
void *class_data;
char class_id[BUS_ID_SIZE];
};
int class_device_register(struct class_device *cd);
void class_device_unregister(struct class_device *cd);
int class_device_rename(struct class_device *cd, char *new_name);
struct class_device_attribute {
struct attribute attr;
ssize_t (*show)(struct class_device *cls, char *buf);
ssize_t (*store)(struct class_device *cls, const char *buf,
size_t count);
};
CLASS_DEVICE_ATTR(name, mode, show, store);
int class_device_create_file(struct class_device *cls, const struct class_device_attribute *attr);
void class_device_remove_file(struct class_device *cls, const struct class_device_attribute *attr);
Class interfaces
在class中有变化的时候, 得到一个机会处理设备.
struct class_interface {
struct class *class;
int (*add) (struct class_device *cd); //一般是给设备添加更多的属性
void (*remove) (struct class_device *cd);
};
int class_interface_register(struct class_interface *intf);
void class_interface_unregister(struct class_interface *intf);
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/79526/showart_2121099.html |
|