赞
踩
该系列文章阅读顺序:
class 用于管理同类的设备,常常被我们用来给上层开辟一个属性节点,多用于查看,修改对应的设备信息。
作者: baron
struct class { const char *name; // class名称,用来初始化 subsys_private->susbus.kobj struct module *owner; struct class_attribute *class_attrs; // 默然属性文件指针 // 默认的设备属性文件,当注册设备到该 class 上时,会自动在改设备下创建,即该 class 下的所有设备都会注册这个属性文件。 const struct attribute_group **dev_groups; // 表示class下的设备在 /sys/dev 下的哪个目录,现在有两个目录 char 和 block 默认选择 char。 struct kobject *dev_kobj; // 当class下的设备发生变化时,会调用class的uevent函数 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); int (*shutdown)(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; };
struct subsys_private { struct kset subsys; // 该 class 在sysfs中的目录 struct kset *devices_kset; struct list_head interfaces; struct mutex mutex; struct kset *drivers_kset; struct klist klist_devices; struct klist klist_drivers; struct blocking_notifier_head bus_notifier; unsigned int drivers_autoprobe:1; struct bus_type *bus; struct kset glue_dirs; struct class *class; // 保存上层的class };
int __init classes_init(void)
{
class_kset = kset_create_and_add("class", NULL, NULL); //创建 /sys/class 节点
if (!class_kset)
return -ENOMEM;
return 0;
}
用于在内核中创建一个 class
#define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ }) struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key) { struct class *cls; int retval; //动态创建 class 结构 cls = kzalloc(sizeof(*cls), GFP_KERNEL); if (!cls) { retval = -ENOMEM; goto error; } cls->name = name; //初始化 name cls->owner = owner; //初始化 owner cls->class_release = class_create_release; //初始化 默认release函数 retval = __class_register(cls, key); //注册class if (retval) goto error; return cls; error: kfree(cls); return ERR_PTR(retval); } EXPORT_SYMBOL_GPL(__class_create);
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); //动态创建一个 subsys_private 结构 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); error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name); //初始化 class 目录名 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; //设置子设备的默认类型为char #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为class_kset 即出现在 /sys/class/目录下 (一般是不会设置class的父kobj的,因此默认使用kset作为父kobj) #endif cp->subsys.kobj.ktype = &class_ktype; //初始化class的默认ktype cp->class = cls; cls->p = cp; error = kset_register(&cp->subsys); //注册kset创建对应的class节点,/sys/class/xxx if (error) { kfree(cp); return error; } error = add_class_attrs(class_get(cls)); //增加引用计数,创建默认属性文件 class_put(cls); return error; } EXPORT_SYMBOL_GPL(__class_register);
在 class 下创建对应的设备,返回创建的设备结构。
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; } EXPORT_SYMBOL_GPL(device_create); struct device *device_create_vargs(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, va_list args) { return device_create_groups_vargs(class, parent, devt, drvdata, NULL, fmt, args); } EXPORT_SYMBOL_GPL(device_create_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); if (retval) goto error; retval = device_add(dev); //注册设备 if (retval) goto error; return dev; error: put_device(dev); return ERR_PTR(retval); }
device_add 函数在前面已经有很详细的分析,这里不赘述,不过这里再补充说明一点,device_create 会在 /dev/ 目录下创建设备节点是因为设备有设备号,只要是调用 device_add 注册的设备,同时有设备号就会在 /dev/ 下创建设备节点
属性文件是 class 重点内容,我们多用 class 来给上层提供接口,属性文件创建接口如下。
//快速创建 class_attribute
#define CLASS_ATTR(_name, _mode, _show, _store) \
struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define CLASS_ATTR_RW(_name) \
struct class_attribute class_attr_##_name = __ATTR_RW(_name)
#define CLASS_ATTR_RO(_name) \
struct class_attribute class_attr_##_name = __ATTR_RO(_name)
static inline int __must_check class_create_file(struct class *class, const struct class_attribute *attr)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。