]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - lib/kobject.c
Driver Core: kill subsys_attribute and default sysfs ops
[linux-2.6-omap-h63xx.git] / lib / kobject.c
index b52e9f4ef3719f58c8217deb5e26bdfd59ebaeb9..c742ac25228a078e34032eb01f88f3d928f0c08e 100644 (file)
@@ -186,8 +186,15 @@ int kobject_add(struct kobject * kobj)
        if (kobj->kset) {
                spin_lock(&kobj->kset->list_lock);
 
-               if (!parent)
+               if (!parent) {
                        parent = kobject_get(&kobj->kset->kobj);
+                       /*
+                        * If the kset is our parent, get a second
+                        * reference, we drop both the kset and the
+                        * parent ref on cleanup
+                        */
+                       kobject_get(parent);
+               }
 
                list_add_tail(&kobj->entry,&kobj->kset->list);
                spin_unlock(&kobj->kset->list_lock);
@@ -232,62 +239,190 @@ int kobject_register(struct kobject * kobj)
        return error;
 }
 
+/**
+ * kobject_set_name_vargs - Set the name of an kobject
+ * @kobj: struct kobject to set the name of
+ * @fmt: format string used to build the name
+ * @vargs: vargs to format the string.
+ */
+static int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
+                                 va_list vargs)
+{
+       va_list aq;
+       char *name;
+
+       va_copy(aq, vargs);
+       name = kvasprintf(GFP_KERNEL, fmt, vargs);
+       va_end(aq);
+
+       if (!name)
+               return -ENOMEM;
+
+       /* Free the old name, if necessary. */
+       kfree(kobj->k_name);
+
+       /* Now, set the new name */
+       kobj->k_name = name;
+
+       return 0;
+}
 
 /**
- *     kobject_set_name - Set the name of an object
- *     @kobj:  object.
- *     @fmt:   format string used to build the name
+ * kobject_set_name - Set the name of a kobject
+ * @kobj: struct kobject to set the name of
+ * @fmt: format string used to build the name
  *
- *     If strlen(name) >= KOBJ_NAME_LEN, then use a dynamically allocated
- *     string that @kobj->k_name points to. Otherwise, use the static 
- *     @kobj->name array.
+ * This sets the name of the kobject.  If you have already added the
+ * kobject to the system, you must call kobject_rename() in order to
+ * change the name of the kobject.
  */
-int kobject_set_name(struct kobject * kobj, const char * fmt, ...)
+int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
 {
-       int error = 0;
-       int limit;
-       int need;
        va_list args;
-       char *name;
+       int retval;
 
-       /* find out how big a buffer we need */
-       name = kmalloc(1024, GFP_KERNEL);
-       if (!name) {
-               error = -ENOMEM;
-               goto done;
-       }
        va_start(args, fmt);
-       need = vsnprintf(name, 1024, fmt, args);
+       retval = kobject_set_name_vargs(kobj, fmt, args);
        va_end(args);
-       kfree(name);
 
-       /* Allocate the new space and copy the string in */
-       limit = need + 1;
-       name = kmalloc(limit, GFP_KERNEL);
-       if (!name) {
-               error = -ENOMEM;
-               goto done;
+       return retval;
+}
+EXPORT_SYMBOL(kobject_set_name);
+
+/**
+ * kobject_init_ng - initialize a kobject structure
+ * @kobj: pointer to the kobject to initialize
+ * @ktype: pointer to the ktype for this kobject.
+ *
+ * This function will properly initialize a kobject such that it can then
+ * be passed to the kobject_add() call.
+ *
+ * After this function is called, the kobject MUST be cleaned up by a call
+ * to kobject_put(), not by a call to kfree directly to ensure that all of
+ * the memory is cleaned up properly.
+ */
+void kobject_init_ng(struct kobject *kobj, struct kobj_type *ktype)
+{
+       char *err_str;
+
+       if (!kobj) {
+               err_str = "invalid kobject pointer!";
+               goto error;
        }
+       if (!ktype) {
+               err_str = "must have a ktype to be initialized properly!\n";
+               goto error;
+       }
+       if (atomic_read(&kobj->kref.refcount)) {
+               /* do not error out as sometimes we can recover */
+               printk(KERN_ERR "kobject: reference count is already set, "
+                      "something is seriously wrong.\n");
+               dump_stack();
+       }
+
+       kref_init(&kobj->kref);
+       INIT_LIST_HEAD(&kobj->entry);
+       kobj->ktype = ktype;
+       return;
+
+error:
+       printk(KERN_ERR "kobject: %s\n", err_str);
+       dump_stack();
+}
+EXPORT_SYMBOL(kobject_init_ng);
+
+static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
+                           const char *fmt, va_list vargs)
+{
+       va_list aq;
+       int retval;
+
+       va_copy(aq, vargs);
+       retval = kobject_set_name_vargs(kobj, fmt, aq);
+       va_end(aq);
+       if (retval) {
+               printk(KERN_ERR "kobject: can not set name properly!\n");
+               return retval;
+       }
+       kobj->parent = parent;
+       return kobject_add(kobj);
+}
+
+/**
+ * kobject_add_ng - the main kobject add function
+ * @kobj: the kobject to add
+ * @parent: pointer to the parent of the kobject.
+ * @fmt: format to name the kobject with.
+ *
+ * The kobject name is set and added to the kobject hierarchy in this
+ * function.
+ *
+ * If @parent is set, then the parent of the @kobj will be set to it.
+ * If @parent is NULL, then the parent of the @kobj will be set to the
+ * kobject associted with the kset assigned to this kobject.  If no kset
+ * is assigned to the kobject, then the kobject will be located in the
+ * root of the sysfs tree.
+ *
+ * If this function returns an error, kobject_put() must be called to
+ * properly clean up the memory associated with the object.
+ *
+ * If the function is successful, the only way to properly clean up the
+ * memory is with a call to kobject_del(), in which case, a call to
+ * kobject_put() is not necessary (kobject_del() does the final
+ * kobject_put() to call the release function in the ktype's release
+ * pointer.)
+ *
+ * Under no instance should the kobject that is passed to this function
+ * be directly freed with a call to kfree(), that can leak memory.
+ *
+ * Note, no uevent will be created with this call, the caller should set
+ * up all of the necessary sysfs files for the object and then call
+ * kobject_uevent() with the UEVENT_ADD parameter to ensure that
+ * userspace is properly notified of this kobject's creation.
+ */
+int kobject_add_ng(struct kobject *kobj, struct kobject *parent,
+                  const char *fmt, ...)
+{
+       va_list args;
+       int retval;
+
+       if (!kobj)
+               return -EINVAL;
+
        va_start(args, fmt);
-       need = vsnprintf(name, limit, fmt, args);
+       retval = kobject_add_varg(kobj, parent, fmt, args);
        va_end(args);
 
-       /* something wrong with the string we copied? */
-       if (need >= limit) {
-               kfree(name);
-               error = -EFAULT;
-               goto done;
-       }
+       return retval;
+}
+EXPORT_SYMBOL(kobject_add_ng);
 
-       /* Free the old name, if necessary. */
-       kfree(kobj->k_name);
+/**
+ * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
+ * @kobj: pointer to the kobject to initialize
+ * @ktype: pointer to the ktype for this kobject.
+ * @parent: pointer to the parent of this kobject.
+ * @fmt: the name of the kobject.
+ *
+ * This function combines the call to kobject_init_ng() and
+ * kobject_add_ng().  The same type of error handling after a call to
+ * kobject_add_ng() and kobject lifetime rules are the same here.
+ */
+int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
+                        struct kobject *parent, const char *fmt, ...)
+{
+       va_list args;
+       int retval;
 
-       /* Now, set the new name */
-       kobj->k_name = name;
-done:
-       return error;
+       kobject_init_ng(kobj, ktype);
+
+       va_start(args, fmt);
+       retval = kobject_add_varg(kobj, parent, fmt, args);
+       va_end(args);
+
+       return retval;
 }
-EXPORT_SYMBOL(kobject_set_name);
+EXPORT_SYMBOL_GPL(kobject_init_and_add);
 
 /**
  *     kobject_rename - change the name of an object
@@ -334,8 +469,6 @@ int kobject_rename(struct kobject * kobj, const char *new_name)
        sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
        envp[0] = devpath_string;
        envp[1] = NULL;
-       /* Note : if we want to send the new name alone, not the full path,
-        * we could probably use kobject_name(kobj); */
 
        error = sysfs_rename_dir(kobj, new_name);
 
@@ -445,12 +578,11 @@ struct kobject * kobject_get(struct kobject * kobj)
        return kobj;
 }
 
-/**
- *     kobject_cleanup - free kobject resources. 
- *     @kobj:  object.
+/*
+ * kobject_cleanup - free kobject resources.
+ * @kobj: object to cleanup
  */
-
-void kobject_cleanup(struct kobject * kobj)
+static void kobject_cleanup(struct kobject *kobj)
 {
        struct kobj_type * t = get_ktype(kobj);
        struct kset * s = kobj->kset;
@@ -487,65 +619,72 @@ void kobject_put(struct kobject * kobj)
                kref_put(&kobj->kref, kobject_release);
 }
 
-
-static void dir_release(struct kobject *kobj)
+static void dynamic_kobj_release(struct kobject *kobj)
 {
+       pr_debug("%s: freeing %s\n", __FUNCTION__, kobject_name(kobj));
        kfree(kobj);
 }
 
-static struct kobj_type dir_ktype = {
-       .release        = dir_release,
-       .sysfs_ops      = NULL,
-       .default_attrs  = NULL,
+static struct kobj_type dynamic_kobj_ktype = {
+       .release        = dynamic_kobj_release,
+       .sysfs_ops      = &kobj_sysfs_ops,
 };
 
 /**
- *     kobject_kset_add_dir - add sub directory of object.
- *     @kset:          kset the directory is belongs to.
- *     @parent:        object in which a directory is created.
- *     @name:  directory name.
+ * kobject_create - create a struct kobject dynamically
  *
- *     Add a plain directory object as child of given object.
+ * This function creates a kobject structure dynamically and sets it up
+ * to be a "dynamic" kobject with a default release function set up.
+ *
+ * If the kobject was not able to be created, NULL will be returned.
+ * The kobject structure returned from here must be cleaned up with a
+ * call to kobject_put() and not kfree(), as kobject_init_ng() has
+ * already been called on this structure.
  */
-struct kobject *kobject_kset_add_dir(struct kset *kset,
-                                    struct kobject *parent, const char *name)
+struct kobject *kobject_create(void)
 {
-       struct kobject *k;
-       int ret;
-
-       if (!parent)
-               return NULL;
-
-       k = kzalloc(sizeof(*k), GFP_KERNEL);
-       if (!k)
-               return NULL;
+       struct kobject *kobj;
 
-       k->kset = kset;
-       k->parent = parent;
-       k->ktype = &dir_ktype;
-       kobject_set_name(k, name);
-       ret = kobject_register(k);
-       if (ret < 0) {
-               printk(KERN_WARNING "%s: kobject_register error: %d\n",
-                       __func__, ret);
-               kobject_del(k);
+       kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
+       if (!kobj)
                return NULL;
-       }
 
-       return k;
+       kobject_init_ng(kobj, &dynamic_kobj_ktype);
+       return kobj;
 }
 
 /**
- *     kobject_add_dir - add sub directory of object.
- *     @parent:        object in which a directory is created.
- *     @name:  directory name.
+ * kobject_create_and_add - create a struct kobject dynamically and register it with sysfs
+ *
+ * @name: the name for the kset
+ * @parent: the parent kobject of this kobject, if any.
+ *
+ * This function creates a kset structure dynamically and registers it
+ * with sysfs.  When you are finished with this structure, call
+ * kobject_unregister() and the structure will be dynamically freed when
+ * it is no longer being used.
  *
- *     Add a plain directory object as child of given object.
+ * If the kobject was not able to be created, NULL will be returned.
  */
-struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
+struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
 {
-       return kobject_kset_add_dir(NULL, parent, name);
+       struct kobject *kobj;
+       int retval;
+
+       kobj = kobject_create();
+       if (!kobj)
+               return NULL;
+
+       retval = kobject_add_ng(kobj, parent, "%s", name);
+       if (retval) {
+               printk(KERN_WARNING "%s: kobject_add error: %d\n",
+                      __FUNCTION__, retval);
+               kobject_put(kobj);
+               kobj = NULL;
+       }
+       return kobj;
 }
+EXPORT_SYMBOL_GPL(kobject_create_and_add);
 
 /**
  *     kset_init - initialize a kset for use
@@ -559,6 +698,35 @@ void kset_init(struct kset * k)
        spin_lock_init(&k->list_lock);
 }
 
+/* default kobject attribute operations */
+static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
+                             char *buf)
+{
+       struct kobj_attribute *kattr;
+       ssize_t ret = -EIO;
+
+       kattr = container_of(attr, struct kobj_attribute, attr);
+       if (kattr->show)
+               ret = kattr->show(kobj, kattr, buf);
+       return ret;
+}
+
+static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct kobj_attribute *kattr;
+       ssize_t ret = -EIO;
+
+       kattr = container_of(attr, struct kobj_attribute, attr);
+       if (kattr->store)
+               ret = kattr->store(kobj, kattr, buf, count);
+       return ret;
+}
+
+struct sysfs_ops kobj_sysfs_ops = {
+       .show   = kobj_attr_show,
+       .store  = kobj_attr_store,
+};
 
 /**
  *     kset_add - add a kset object to the hierarchy.
@@ -642,25 +810,89 @@ void subsystem_unregister(struct kset *s)
        kset_unregister(s);
 }
 
+static void kset_release(struct kobject *kobj)
+{
+       struct kset *kset = container_of(kobj, struct kset, kobj);
+       pr_debug("kset %s: now freed\n", kobject_name(kobj));
+       kfree(kset);
+}
+
+static struct kobj_type kset_ktype = {
+       .sysfs_ops      = &kobj_sysfs_ops,
+       .release = kset_release,
+};
+
 /**
- *     subsystem_create_file - export sysfs attribute file.
- *     @s:     subsystem.
- *     @a:     subsystem attribute descriptor.
+ * kset_create - create a struct kset dynamically
+ *
+ * @name: the name for the kset
+ * @uevent_ops: a struct kset_uevent_ops for the kset
+ * @parent_kobj: the parent kobject of this kset, if any.
+ *
+ * This function creates a kset structure dynamically.  This structure can
+ * then be registered with the system and show up in sysfs with a call to
+ * kset_register().  When you are finished with this structure, if
+ * kset_register() has been called, call kset_unregister() and the
+ * structure will be dynamically freed when it is no longer being used.
+ *
+ * If the kset was not able to be created, NULL will be returned.
  */
-
-int subsys_create_file(struct kset *s, struct subsys_attribute *a)
+static struct kset *kset_create(const char *name,
+                               struct kset_uevent_ops *uevent_ops,
+                               struct kobject *parent_kobj)
 {
-       int error = 0;
+       struct kset *kset;
 
-       if (!s || !a)
-               return -EINVAL;
+       kset = kzalloc(sizeof(*kset), GFP_KERNEL);
+       if (!kset)
+               return NULL;
+       kobject_set_name(&kset->kobj, name);
+       kset->uevent_ops = uevent_ops;
+       kset->kobj.parent = parent_kobj;
+
+       /*
+        * The kobject of this kset will have a type of kset_ktype and belong to
+        * no kset itself.  That way we can properly free it when it is
+        * finished being used.
+        */
+       kset->kobj.ktype = &kset_ktype;
+       kset->kobj.kset = NULL;
 
-       if (kset_get(s)) {
-               error = sysfs_create_file(&s->kobj, &a->attr);
-               kset_put(s);
+       return kset;
+}
+
+/**
+ * kset_create_and_add - create a struct kset dynamically and add it to sysfs
+ *
+ * @name: the name for the kset
+ * @uevent_ops: a struct kset_uevent_ops for the kset
+ * @parent_kobj: the parent kobject of this kset, if any.
+ *
+ * This function creates a kset structure dynamically and registers it
+ * with sysfs.  When you are finished with this structure, call
+ * kset_unregister() and the structure will be dynamically freed when it
+ * is no longer being used.
+ *
+ * If the kset was not able to be created, NULL will be returned.
+ */
+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);
+       if (!kset)
+               return NULL;
+       error = kset_register(kset);
+       if (error) {
+               kfree(kset);
+               return NULL;
        }
-       return error;
+       return kset;
 }
+EXPORT_SYMBOL_GPL(kset_create_and_add);
 
 EXPORT_SYMBOL(kobject_init);
 EXPORT_SYMBOL(kobject_register);
@@ -675,4 +907,3 @@ EXPORT_SYMBOL(kset_unregister);
 
 EXPORT_SYMBOL(subsystem_register);
 EXPORT_SYMBOL(subsystem_unregister);
-EXPORT_SYMBOL(subsys_create_file);