class.c
来自「linux 内核源代码」· C语言 代码 · 共 893 行 · 第 1/2 页
C
893 行
/* * class.c - basic device class management * * Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Open Source Development Labs * Copyright (c) 2003-2004 Greg Kroah-Hartman * Copyright (c) 2003-2004 IBM Corp. * * This file is released under the GPLv2 * */#include <linux/device.h>#include <linux/module.h>#include <linux/init.h>#include <linux/string.h>#include <linux/kdev_t.h>#include <linux/err.h>#include <linux/slab.h>#include "base.h"#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)#define to_class(obj) container_of(obj, struct class, subsys.kobj)static ssize_tclass_attr_show(struct kobject * kobj, struct attribute * attr, char * buf){ struct class_attribute * class_attr = to_class_attr(attr); struct class * dc = to_class(kobj); ssize_t ret = -EIO; if (class_attr->show) ret = class_attr->show(dc, buf); return ret;}static ssize_tclass_attr_store(struct kobject * kobj, struct attribute * attr, const char * buf, size_t count){ struct class_attribute * class_attr = to_class_attr(attr); struct class * dc = to_class(kobj); ssize_t ret = -EIO; if (class_attr->store) ret = class_attr->store(dc, buf, count); return ret;}static void class_release(struct kobject * kobj){ struct class *class = to_class(kobj); pr_debug("class '%s': release.\n", class->name); if (class->class_release) class->class_release(class); else pr_debug("class '%s' does not have a release() function, " "be careful\n", class->name);}static struct sysfs_ops class_sysfs_ops = { .show = class_attr_show, .store = class_attr_store,};static struct kobj_type class_ktype = { .sysfs_ops = &class_sysfs_ops, .release = class_release,};/* Hotplug events for classes go to the class_obj subsys */static decl_subsys(class, &class_ktype, NULL);int class_create_file(struct class * cls, const struct class_attribute * attr){ int error; if (cls) { error = sysfs_create_file(&cls->subsys.kobj, &attr->attr); } else error = -EINVAL; return error;}void class_remove_file(struct class * cls, const struct class_attribute * attr){ if (cls) sysfs_remove_file(&cls->subsys.kobj, &attr->attr);}static struct class *class_get(struct class *cls){ if (cls) return container_of(kset_get(&cls->subsys), struct class, subsys); return NULL;}static void class_put(struct class * cls){ if (cls) kset_put(&cls->subsys);}static int add_class_attrs(struct class * cls){ int i; int error = 0; if (cls->class_attrs) { for (i = 0; attr_name(cls->class_attrs[i]); i++) { error = class_create_file(cls,&cls->class_attrs[i]); if (error) goto Err; } } Done: return error; Err: while (--i >= 0) class_remove_file(cls,&cls->class_attrs[i]); goto Done;}static void remove_class_attrs(struct class * cls){ int i; if (cls->class_attrs) { for (i = 0; attr_name(cls->class_attrs[i]); i++) class_remove_file(cls,&cls->class_attrs[i]); }}int class_register(struct class * cls){ int error; pr_debug("device class '%s': registering\n", cls->name); INIT_LIST_HEAD(&cls->children); INIT_LIST_HEAD(&cls->devices); INIT_LIST_HEAD(&cls->interfaces); kset_init(&cls->class_dirs); init_MUTEX(&cls->sem); error = kobject_set_name(&cls->subsys.kobj, "%s", cls->name); if (error) return error; cls->subsys.kobj.kset = &class_subsys; error = subsystem_register(&cls->subsys); if (!error) { error = add_class_attrs(class_get(cls)); class_put(cls); } return error;}void class_unregister(struct class * cls){ pr_debug("device class '%s': unregistering\n", cls->name); remove_class_attrs(cls); subsystem_unregister(&cls->subsys);}static void class_create_release(struct class *cls){ pr_debug("%s called for %s\n", __FUNCTION__, cls->name); kfree(cls);}static void class_device_create_release(struct class_device *class_dev){ pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id); kfree(class_dev);}/* needed to allow these devices to have parent class devices */static int class_device_create_uevent(struct class_device *class_dev, struct kobj_uevent_env *env){ pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id); return 0;}/** * class_create - create a struct class structure * @owner: pointer to the module that is to "own" this struct class * @name: pointer to a string for the name of this class. * * This is used to create a struct class pointer that can then be used * in calls to class_device_create(). * * Note, the pointer created here is to be destroyed when finished by * making a call to class_destroy(). */struct class *class_create(struct module *owner, const char *name){ struct class *cls; int retval; cls = kzalloc(sizeof(*cls), GFP_KERNEL); if (!cls) { retval = -ENOMEM; goto error; } cls->name = name; cls->owner = owner; cls->class_release = class_create_release; cls->release = class_device_create_release; retval = class_register(cls); if (retval) goto error; return cls;error: kfree(cls); return ERR_PTR(retval);}/** * class_destroy - destroys a struct class structure * @cls: pointer to the struct class that is to be destroyed * * Note, the pointer to be destroyed must have been created with a call * to class_create(). */void class_destroy(struct class *cls){ if ((cls == NULL) || (IS_ERR(cls))) return; class_unregister(cls);}/* Class Device Stuff */int class_device_create_file(struct class_device * class_dev, const struct class_device_attribute * attr){ int error = -EINVAL; if (class_dev) error = sysfs_create_file(&class_dev->kobj, &attr->attr); return error;}void class_device_remove_file(struct class_device * class_dev, const struct class_device_attribute * attr){ if (class_dev) sysfs_remove_file(&class_dev->kobj, &attr->attr);}int class_device_create_bin_file(struct class_device *class_dev, struct bin_attribute *attr){ int error = -EINVAL; if (class_dev) error = sysfs_create_bin_file(&class_dev->kobj, attr); return error;}void class_device_remove_bin_file(struct class_device *class_dev, struct bin_attribute *attr){ if (class_dev) sysfs_remove_bin_file(&class_dev->kobj, attr);}static ssize_tclass_device_attr_show(struct kobject * kobj, struct attribute * attr, char * buf){ struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr); struct class_device * cd = to_class_dev(kobj); ssize_t ret = 0; if (class_dev_attr->show) ret = class_dev_attr->show(cd, buf); return ret;}static ssize_tclass_device_attr_store(struct kobject * kobj, struct attribute * attr, const char * buf, size_t count){ struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr); struct class_device * cd = to_class_dev(kobj); ssize_t ret = 0; if (class_dev_attr->store) ret = class_dev_attr->store(cd, buf, count); return ret;}static struct sysfs_ops class_dev_sysfs_ops = { .show = class_device_attr_show, .store = class_device_attr_store,};static void class_dev_release(struct kobject * kobj){ struct class_device *cd = to_class_dev(kobj); struct class * cls = cd->class; pr_debug("device class '%s': release.\n", cd->class_id); if (cd->release) cd->release(cd); else if (cls->release) cls->release(cd); else { printk(KERN_ERR "Class Device '%s' does not have a release() function, " "it is broken and must be fixed.\n", cd->class_id); WARN_ON(1); }}static struct kobj_type class_device_ktype = { .sysfs_ops = &class_dev_sysfs_ops, .release = class_dev_release,};static int class_uevent_filter(struct kset *kset, struct kobject *kobj){ struct kobj_type *ktype = get_ktype(kobj); if (ktype == &class_device_ktype) { struct class_device *class_dev = to_class_dev(kobj); if (class_dev->class) return 1; } return 0;}static const char *class_uevent_name(struct kset *kset, struct kobject *kobj){ struct class_device *class_dev = to_class_dev(kobj); return class_dev->class->name;}#ifdef CONFIG_SYSFS_DEPRECATEDchar *make_class_name(const char *name, struct kobject *kobj){ char *class_name; int size; size = strlen(name) + strlen(kobject_name(kobj)) + 2; class_name = kmalloc(size, GFP_KERNEL); if (!class_name) return NULL; strcpy(class_name, name); strcat(class_name, ":"); strcat(class_name, kobject_name(kobj)); return class_name;}static int make_deprecated_class_device_links(struct class_device *class_dev){ char *class_name; int error; if (!class_dev->dev) return 0; class_name = make_class_name(class_dev->class->name, &class_dev->kobj); if (class_name) error = sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj, class_name); else error = -ENOMEM; kfree(class_name); return error;}static void remove_deprecated_class_device_links(struct class_device *class_dev){ char *class_name; if (!class_dev->dev) return; class_name = make_class_name(class_dev->class->name, &class_dev->kobj); if (class_name) sysfs_remove_link(&class_dev->dev->kobj, class_name); kfree(class_name);}#elsestatic inline int make_deprecated_class_device_links(struct class_device *cd){ return 0; }static void remove_deprecated_class_device_links(struct class_device *cd){ }#endifstatic int class_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env){ struct class_device *class_dev = to_class_dev(kobj); struct device *dev = class_dev->dev; int retval = 0; pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id); if (MAJOR(class_dev->devt)) { add_uevent_var(env, "MAJOR=%u", MAJOR(class_dev->devt)); add_uevent_var(env, "MINOR=%u", MINOR(class_dev->devt)); } if (dev) { const char *path = kobject_get_path(&dev->kobj, GFP_KERNEL); if (path) { add_uevent_var(env, "PHYSDEVPATH=%s", path); kfree(path); } if (dev->bus) add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name); if (dev->driver) add_uevent_var(env, "PHYSDEVDRIVER=%s", dev->driver->name); } if (class_dev->uevent) { /* have the class device specific function add its stuff */ retval = class_dev->uevent(class_dev, env); if (retval) pr_debug("class_dev->uevent() returned %d\n", retval); } else if (class_dev->class->uevent) { /* have the class specific function add its stuff */ retval = class_dev->class->uevent(class_dev, env); if (retval) pr_debug("class->uevent() returned %d\n", retval); } return retval;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?