fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / base / class.c
index df7fdab..8bf2ca2 100644 (file)
@@ -10,7 +10,6 @@
  *
  */
 
-#include <linux/config.h>
 #include <linux/device.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -20,6 +19,8 @@
 #include <linux/slab.h>
 #include "base.h"
 
+extern struct subsystem devices_subsys;
+
 #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
 #define to_class(obj) container_of(obj, struct class, subsys.kset.kobj)
 
@@ -91,14 +92,14 @@ void class_remove_file(struct class * cls, const struct class_attribute * attr)
                sysfs_remove_file(&cls->subsys.kset.kobj, &attr->attr);
 }
 
-struct class * class_get(struct class * cls)
+static struct class *class_get(struct class *cls)
 {
        if (cls)
                return container_of(subsys_get(&cls->subsys), struct class, subsys);
        return NULL;
 }
 
-void class_put(struct class * cls)
+static void class_put(struct class * cls)
 {
        if (cls)
                subsys_put(&cls->subsys);
@@ -142,6 +143,7 @@ int class_register(struct class * cls)
        pr_debug("device class '%s': registering\n", cls->name);
 
        INIT_LIST_HEAD(&cls->children);
+       INIT_LIST_HEAD(&cls->devices);
        INIT_LIST_HEAD(&cls->interfaces);
        init_MUTEX(&cls->sem);
        error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
@@ -161,6 +163,8 @@ int class_register(struct class * cls)
 void class_unregister(struct class * cls)
 {
        pr_debug("device class '%s': unregistering\n", cls->name);
+       if (cls->virtual_dir)
+               kobject_unregister(cls->virtual_dir);
        remove_class_attrs(cls);
        subsystem_unregister(&cls->subsys);
 }
@@ -197,7 +201,7 @@ static int class_device_create_uevent(struct class_device *class_dev,
  * 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, char *name)
+struct class *class_create(struct module *owner, const char *name)
 {
        struct class *cls;
        int retval;
@@ -226,7 +230,7 @@ error:
 
 /**
  * class_destroy - destroys a struct class structure
- * @cs: pointer to the struct class that is to be destroyed
+ * @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().
@@ -350,6 +354,92 @@ static const char *class_uevent_name(struct kset *kset, struct kobject *kobj)
        return class_dev->class->name;
 }
 
+#ifdef CONFIG_SYSFS_DEPRECATED
+char *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 ERR_PTR(-ENOMEM);
+
+       strcpy(class_name, name);
+       strcat(class_name, ":");
+       strcat(class_name, kobject_name(kobj));
+       return class_name;
+}
+
+static int deprecated_class_uevent(char **envp, int num_envp, int *cur_index,
+                                  char *buffer, int buffer_size,
+                                  int *cur_len,
+                                  struct class_device *class_dev)
+{
+       struct device *dev = class_dev->dev;
+       char *path;
+
+       if (!dev)
+               return 0;
+
+       /* add device, backing this class device (deprecated) */
+       path = kobject_get_path(&dev->kobj, GFP_KERNEL);
+
+       add_uevent_var(envp, num_envp, cur_index, buffer, buffer_size,
+                      cur_len, "PHYSDEVPATH=%s", path);
+       kfree(path);
+
+       if (dev->bus)
+               add_uevent_var(envp, num_envp, cur_index,
+                              buffer, buffer_size, cur_len,
+                              "PHYSDEVBUS=%s", dev->bus->name);
+
+       if (dev->driver)
+               add_uevent_var(envp, num_envp, cur_index,
+                              buffer, buffer_size, cur_len,
+                              "PHYSDEVDRIVER=%s", dev->driver->name);
+       return 0;
+}
+
+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);
+       error = sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
+                                 class_name);
+       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);
+       sysfs_remove_link(&class_dev->dev->kobj, class_name);
+       kfree(class_name);
+}
+#else
+static inline int deprecated_class_uevent(char **envp, int num_envp,
+                                         int *cur_index, char *buffer,
+                                         int buffer_size, int *cur_len,
+                                         struct class_device *class_dev)
+{ return 0; }
+static inline int make_deprecated_class_device_links(struct class_device *cd)
+{ return 0; }
+static void remove_deprecated_class_device_links(struct class_device *cd)
+{ }
+#endif
+
 static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,
                         int num_envp, char *buffer, int buffer_size)
 {
@@ -360,25 +450,8 @@ static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,
 
        pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
 
-       if (class_dev->dev) {
-               /* add physical device, backing this device  */
-               struct device *dev = class_dev->dev;
-               char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);
-
-               add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
-                              &length, "PHYSDEVPATH=%s", path);
-               kfree(path);
-
-               if (dev->bus)
-                       add_uevent_var(envp, num_envp, &i,
-                                      buffer, buffer_size, &length,
-                                      "PHYSDEVBUS=%s", dev->bus->name);
-
-               if (dev->driver)
-                       add_uevent_var(envp, num_envp, &i,
-                                      buffer, buffer_size, &length,
-                                      "PHYSDEVDRIVER=%s", dev->driver->name);
-       }
+       deprecated_class_uevent(envp, num_envp, &i, buffer, buffer_size,
+                               &length, class_dev);
 
        if (MAJOR(class_dev->devt)) {
                add_uevent_var(envp, num_envp, &i,
@@ -456,6 +529,35 @@ static void class_device_remove_attrs(struct class_device * cd)
        }
 }
 
+static int class_device_add_groups(struct class_device * cd)
+{
+       int i;
+       int error = 0;
+
+       if (cd->groups) {
+               for (i = 0; cd->groups[i]; i++) {
+                       error = sysfs_create_group(&cd->kobj, cd->groups[i]);
+                       if (error) {
+                               while (--i >= 0)
+                                       sysfs_remove_group(&cd->kobj, cd->groups[i]);
+                               goto out;
+                       }
+               }
+       }
+out:
+       return error;
+}
+
+static void class_device_remove_groups(struct class_device * cd)
+{
+       int i;
+       if (cd->groups) {
+               for (i = 0; cd->groups[i]; i++) {
+                       sysfs_remove_group(&cd->kobj, cd->groups[i]);
+               }
+       }
+}
+
 static ssize_t show_dev(struct class_device *class_dev, char *buf)
 {
        return print_dev_t(buf, class_dev->devt);
@@ -475,30 +577,11 @@ void class_device_initialize(struct class_device *class_dev)
        INIT_LIST_HEAD(&class_dev->node);
 }
 
-static char *make_class_name(struct class_device *class_dev)
-{
-       char *name;
-       int size;
-
-       size = strlen(class_dev->class->name) +
-               strlen(kobject_name(&class_dev->kobj)) + 2;
-
-       name = kmalloc(size, GFP_KERNEL);
-       if (!name)
-               return ERR_PTR(-ENOMEM);
-
-       strcpy(name, class_dev->class->name);
-       strcat(name, ":");
-       strcat(name, kobject_name(&class_dev->kobj));
-       return name;
-}
-
 int class_device_add(struct class_device *class_dev)
 {
        struct class *parent_class = NULL;
        struct class_device *parent_class_dev = NULL;
        struct class_interface *class_intf;
-       char *class_name = NULL;
        int error = -EINVAL;
 
        class_dev = class_device_get(class_dev);
@@ -506,18 +589,22 @@ int class_device_add(struct class_device *class_dev)
                return -EINVAL;
 
        if (!strlen(class_dev->class_id))
-               goto register_done;
+               goto out1;
 
        parent_class = class_get(class_dev->class);
        if (!parent_class)
-               goto register_done;
+               goto out1;
+
        parent_class_dev = class_device_get(class_dev->parent);
 
        pr_debug("CLASS: registering class device: ID = '%s'\n",
                 class_dev->class_id);
 
        /* first, register with generic layer. */
-       kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
+       error = kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
+       if (error)
+               goto out2;
+
        if (parent_class_dev)
                class_dev->kobj.parent = &parent_class_dev->kobj;
        else
@@ -525,59 +612,93 @@ int class_device_add(struct class_device *class_dev)
 
        error = kobject_add(&class_dev->kobj);
        if (error)
-               goto register_done;
+               goto out2;
 
        /* add the needed attributes to this device */
+       error = sysfs_create_link(&class_dev->kobj,
+                                 &parent_class->subsys.kset.kobj, "subsystem");
+       if (error)
+               goto out3;
        class_dev->uevent_attr.attr.name = "uevent";
        class_dev->uevent_attr.attr.mode = S_IWUSR;
        class_dev->uevent_attr.attr.owner = parent_class->owner;
        class_dev->uevent_attr.store = store_uevent;
-       class_device_create_file(class_dev, &class_dev->uevent_attr);
+       error = class_device_create_file(class_dev, &class_dev->uevent_attr);
+       if (error)
+               goto out3;
 
        if (MAJOR(class_dev->devt)) {
                struct class_device_attribute *attr;
                attr = kzalloc(sizeof(*attr), GFP_KERNEL);
                if (!attr) {
                        error = -ENOMEM;
-                       kobject_del(&class_dev->kobj);
-                       goto register_done;
+                       goto out4;
                }
                attr->attr.name = "dev";
                attr->attr.mode = S_IRUGO;
                attr->attr.owner = parent_class->owner;
                attr->show = show_dev;
-               class_device_create_file(class_dev, attr);
+               error = class_device_create_file(class_dev, attr);
+               if (error) {
+                       kfree(attr);
+                       goto out4;
+               }
+
                class_dev->devt_attr = attr;
        }
 
-       class_device_add_attrs(class_dev);
+       error = class_device_add_attrs(class_dev);
+       if (error)
+               goto out5;
+
        if (class_dev->dev) {
-               class_name = make_class_name(class_dev);
-               sysfs_create_link(&class_dev->kobj,
-                                 &class_dev->dev->kobj, "device");
-               sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
-                                 class_name);
+               error = sysfs_create_link(&class_dev->kobj,
+                                         &class_dev->dev->kobj, "device");
+               if (error)
+                       goto out6;
        }
 
+       error = class_device_add_groups(class_dev);
+       if (error)
+               goto out7;
+
+       error = make_deprecated_class_device_links(class_dev);
+       if (error)
+               goto out8;
+
        kobject_uevent(&class_dev->kobj, KOBJ_ADD);
 
        /* notify any interfaces this device is now here */
-       if (parent_class) {
-               down(&parent_class->sem);
-               list_add_tail(&class_dev->node, &parent_class->children);
-               list_for_each_entry(class_intf, &parent_class->interfaces, node)
-                       if (class_intf->add)
-                               class_intf->add(class_dev, class_intf);
-               up(&parent_class->sem);
+       down(&parent_class->sem);
+       list_add_tail(&class_dev->node, &parent_class->children);
+       list_for_each_entry(class_intf, &parent_class->interfaces, node) {
+               if (class_intf->add)
+                       class_intf->add(class_dev, class_intf);
        }
+       up(&parent_class->sem);
+
+       goto out1;
 
- register_done:
-       if (error) {
-               class_put(parent_class);
+ out8:
+       class_device_remove_groups(class_dev);
+ out7:
+       if (class_dev->dev)
+               sysfs_remove_link(&class_dev->kobj, "device");
+ out6:
+       class_device_remove_attrs(class_dev);
+ out5:
+       if (class_dev->devt_attr)
+               class_device_remove_file(class_dev, class_dev->devt_attr);
+ out4:
+       class_device_remove_file(class_dev, &class_dev->uevent_attr);
+ out3:
+       kobject_del(&class_dev->kobj);
+ out2:
+       if(parent_class_dev)
                class_device_put(parent_class_dev);
-       }
+       class_put(parent_class);
+ out1:
        class_device_put(class_dev);
-       kfree(class_name);
        return error;
 }
 
@@ -589,9 +710,9 @@ int class_device_register(struct class_device *class_dev)
 
 /**
  * class_device_create - creates a class device and registers it with sysfs
- * @cs: pointer to the struct class that this device should be registered to.
+ * @cls: pointer to the struct class that this device should be registered to.
  * @parent: pointer to the parent struct class_device of this new device, if any.
- * @dev: the dev_t for the char device to be added.
+ * @devt: the dev_t for the char device to be added.
  * @device: a pointer to a struct device that is assiociated with this class device.
  * @fmt: string for the class device's name
  *
@@ -612,7 +733,8 @@ int class_device_register(struct class_device *class_dev)
 struct class_device *class_device_create(struct class *cls,
                                         struct class_device *parent,
                                         dev_t devt,
-                                        struct device *device, char *fmt, ...)
+                                        struct device *device,
+                                        const char *fmt, ...)
 {
        va_list args;
        struct class_device *class_dev = NULL;
@@ -653,7 +775,6 @@ void class_device_del(struct class_device *class_dev)
        struct class *parent_class = class_dev->class;
        struct class_device *parent_device = class_dev->parent;
        struct class_interface *class_intf;
-       char *class_name = NULL;
 
        if (parent_class) {
                down(&parent_class->sem);
@@ -665,21 +786,21 @@ void class_device_del(struct class_device *class_dev)
        }
 
        if (class_dev->dev) {
-               class_name = make_class_name(class_dev);
+               remove_deprecated_class_device_links(class_dev);
                sysfs_remove_link(&class_dev->kobj, "device");
-               sysfs_remove_link(&class_dev->dev->kobj, class_name);
        }
+       sysfs_remove_link(&class_dev->kobj, "subsystem");
        class_device_remove_file(class_dev, &class_dev->uevent_attr);
        if (class_dev->devt_attr)
                class_device_remove_file(class_dev, class_dev->devt_attr);
        class_device_remove_attrs(class_dev);
+       class_device_remove_groups(class_dev);
 
        kobject_uevent(&class_dev->kobj, KOBJ_REMOVE);
        kobject_del(&class_dev->kobj);
 
        class_device_put(parent_device);
        class_put(parent_class);
-       kfree(class_name);
 }
 
 void class_device_unregister(struct class_device *class_dev)
@@ -693,7 +814,7 @@ void class_device_unregister(struct class_device *class_dev)
 /**
  * class_device_destroy - removes a class device that was created with class_device_create()
  * @cls: the pointer to the struct class that this device was registered * with.
- * @dev: the dev_t of the device that was previously registered.
+ * @devt: the dev_t of the device that was previously registered.
  *
  * This call unregisters and cleans up a class device that was created with a
  * call to class_device_create()
@@ -728,19 +849,25 @@ int class_device_rename(struct class_device *class_dev, char *new_name)
        pr_debug("CLASS: renaming '%s' to '%s'\n", class_dev->class_id,
                 new_name);
 
+#ifdef CONFIG_SYSFS_DEPRECATED
        if (class_dev->dev)
-               old_class_name = make_class_name(class_dev);
+               old_class_name = make_class_name(class_dev->class->name,
+                                                &class_dev->kobj);
+#endif
 
        strlcpy(class_dev->class_id, new_name, KOBJ_NAME_LEN);
 
        error = kobject_rename(&class_dev->kobj, new_name);
 
+#ifdef CONFIG_SYSFS_DEPRECATED
        if (class_dev->dev) {
-               new_class_name = make_class_name(class_dev);
+               new_class_name = make_class_name(class_dev->class->name,
+                                                &class_dev->kobj);
                sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
                                  new_class_name);
                sysfs_remove_link(&class_dev->dev->kobj, old_class_name);
        }
+#endif
        class_device_put(class_dev);
 
        kfree(old_class_name);
@@ -767,6 +894,7 @@ int class_interface_register(struct class_interface *class_intf)
 {
        struct class *parent;
        struct class_device *class_dev;
+       struct device *dev;
 
        if (!class_intf || !class_intf->class)
                return -ENODEV;
@@ -781,6 +909,10 @@ int class_interface_register(struct class_interface *class_intf)
                list_for_each_entry(class_dev, &parent->children, node)
                        class_intf->add(class_dev, class_intf);
        }
+       if (class_intf->add_dev) {
+               list_for_each_entry(dev, &parent->devices, node)
+                       class_intf->add_dev(dev, class_intf);
+       }
        up(&parent->sem);
 
        return 0;
@@ -790,6 +922,7 @@ void class_interface_unregister(struct class_interface *class_intf)
 {
        struct class * parent = class_intf->class;
        struct class_device *class_dev;
+       struct device *dev;
 
        if (!parent)
                return;
@@ -800,13 +933,15 @@ void class_interface_unregister(struct class_interface *class_intf)
                list_for_each_entry(class_dev, &parent->children, node)
                        class_intf->remove(class_dev, class_intf);
        }
+       if (class_intf->remove_dev) {
+               list_for_each_entry(dev, &parent->devices, node)
+                       class_intf->remove_dev(dev, class_intf);
+       }
        up(&parent->sem);
 
        class_put(parent);
 }
 
-
-
 int __init classes_init(void)
 {
        int retval;
@@ -827,8 +962,6 @@ EXPORT_SYMBOL_GPL(class_create_file);
 EXPORT_SYMBOL_GPL(class_remove_file);
 EXPORT_SYMBOL_GPL(class_register);
 EXPORT_SYMBOL_GPL(class_unregister);
-EXPORT_SYMBOL_GPL(class_get);
-EXPORT_SYMBOL_GPL(class_put);
 EXPORT_SYMBOL_GPL(class_create);
 EXPORT_SYMBOL_GPL(class_destroy);