Linux 的设备模型引入类,是将其用来作为具有同类型功能设备的一个容器,struct class定义的数据结构是:
struct class {
const char *name; // 类的名称
struct module *owner; // 拥有该类的模块的指针
struct class_attribute *class_attrs; // 类的属性
const struct attribute_group **class_groups;
const struct attribute_group **dev_groups; // 设备的属性
struct kobject *dev_kobj; // 代表当前类中设备的内核对象
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, umode_t *mode);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct kobj_ns_type_operations *ns_type;
const void *(*namespace)(struct device *dev);
const struct dev_pm_ops *pm;
// 类的私有数据区,用于处理类的子系统及其所包含的设备链表
struct subsys_private *p;
};
宏 class_create 用来生成一个类对象,其用途主要是将同类型的设备添加其中。该宏的核心是对函数__class_create 的调用,调用路径如下
#define class_create(owner, name)
__class_create(owner, name, &__key);
__class_register(cls, key);
__class_register函数的实现如下:
int __class_register(struct class *cls, struct lock_class_key *key)
{
struct subsys_private *cp;
int error;
pr_debug("device class '%s': registering\n", cls->name);
// 为__class_create 函数中生成的新的类对象分配私有数据空间
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp)
return -ENOMEM;
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
INIT_LIST_HEAD(&cp->interfaces);
kset_init(&cp->glue_dirs);
__mutex_init(&cp->mutex, "subsys mutex", key);
// 将类对象的 name 成员赋值给代表类的 kobject 对象名称
error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
if (error) {
kfree(cp);
return error;
}
/* set the default /sys/dev directory for devices of this class */
if (!cls->dev_kobj)
cls->dev_kobj = sysfs_dev_char_kobj;
// 为类的 kobj 指定 kset 和 ktype
// class_kset 为系统中所有 class 对象的顶层 kset, 此处将当前 class 对象的 kobj.kset
// 指向 class_kset,意味着通过 class_create 生成的 class,在 sysfs 文件系统中的入口点(目录)
// 将在/sys/class 目录下产生
#if defined(CONFIG_BLOCK)
/* let the block class directory show up in the root of sysfs */
if (!sysfs_deprecated || cls != &block_class)
cp->subsys.kobj.kset = class_kset;
#else
cp->subsys.kobj.kset = class_kset; // kset
#endif
cp->subsys.kobj.ktype = &class_ktype; // ktype
cp->class = cls;
cls->p = cp;
// 调用 kset_register 将之前产生的 class 加入到系统中
// 这样将会在/sys/class 目录下生成一个新的目录
error = kset_register(&cp->subsys);
if (error) {
kfree(cp);
return error;
}
error = class_add_groups(class_get(cls), cls->class_groups);
class_put(cls);
error = add_class_attrs(class_get(cls));
class_put(cls);
return error;
}
在使用device_create创建设备对象时,传入的第一个参数即是class类变量,传入的class和parent变量决定被创建设备在/sys目录下的位置:
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
device_create_vargs
device_create_groups_vargs
static struct device *
device_create_groups_vargs(struct class *class, struct device *parent,
dev_t devt, void *drvdata,
const struct attribute_group **groups,
const char *fmt, va_list args)
{
struct device *dev = NULL;
int retval = -ENODEV;
if (class == NULL || IS_ERR(class))
goto error;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
retval = -ENOMEM;
goto error;
}
device_initialize(dev);
dev->devt = devt;
dev->class = class; // 表示该设备属于哪一类设备
dev->parent = parent;
dev->groups = groups;
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata);
retval = kobject_set_name_vargs(&dev->kobj, fmt, args); // 设置 kobj->name
if (retval)
goto error;
retval = device_add(dev); // 设备注册到系统
if (retval)
goto error;
return dev;
error:
put_device(dev);
return ERR_PTR(retval);
}