Merge master.kernel.org:/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-drm-fsl-dcu.git] / drivers / base / core.c
index 9ea12d9b48a68056edc13a690b1af7561aac37c4..b78fc1e68264872a196c6a7b1e454b080a9831bd 100644 (file)
@@ -120,6 +120,8 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
 
        if (ktype == &ktype_device) {
                struct device *dev = to_dev(kobj);
+               if (dev->uevent_suppress)
+                       return 0;
                if (dev->bus)
                        return 1;
                if (dev->class)
@@ -157,6 +159,11 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
                               "MINOR=%u", MINOR(dev->devt));
        }
 
+       if (dev->type && dev->type->name)
+               add_uevent_var(envp, num_envp, &i,
+                              buffer, buffer_size, &length,
+                              "DEVTYPE=%s", dev->type->name);
+
        if (dev->driver)
                add_uevent_var(envp, num_envp, &i,
                               buffer, buffer_size, &length,
@@ -239,71 +246,157 @@ static struct kset_uevent_ops device_uevent_ops = {
        .uevent =       dev_uevent,
 };
 
+static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       struct kobject *top_kobj;
+       struct kset *kset;
+       char *envp[32];
+       char *data = NULL;
+       char *pos;
+       int i;
+       size_t count = 0;
+       int retval;
+
+       /* search the kset, the device belongs to */
+       top_kobj = &dev->kobj;
+       if (!top_kobj->kset && top_kobj->parent) {
+               do {
+                       top_kobj = top_kobj->parent;
+               } while (!top_kobj->kset && top_kobj->parent);
+       }
+       if (!top_kobj->kset)
+               goto out;
+       kset = top_kobj->kset;
+       if (!kset->uevent_ops || !kset->uevent_ops->uevent)
+               goto out;
+
+       /* respect filter */
+       if (kset->uevent_ops && kset->uevent_ops->filter)
+               if (!kset->uevent_ops->filter(kset, &dev->kobj))
+                       goto out;
+
+       data = (char *)get_zeroed_page(GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       /* let the kset specific function add its keys */
+       pos = data;
+       retval = kset->uevent_ops->uevent(kset, &dev->kobj,
+                                         envp, ARRAY_SIZE(envp),
+                                         pos, PAGE_SIZE);
+       if (retval)
+               goto out;
+
+       /* copy keys to file */
+       for (i = 0; envp[i]; i++) {
+               pos = &buf[count];
+               count += sprintf(pos, "%s\n", envp[i]);
+       }
+out:
+       free_page((unsigned long)data);
+       return count;
+}
+
 static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
+       if (memcmp(buf, "add", 3) != 0)
+               dev_err(dev, "uevent: unsupported action-string; this will "
+                       "be ignored in a future kernel version");
        kobject_uevent(&dev->kobj, KOBJ_ADD);
        return count;
 }
 
-static int device_add_groups(struct device *dev)
+static int device_add_attributes(struct device *dev,
+                                struct device_attribute *attrs)
 {
+       int error = 0;
        int i;
+
+       if (attrs) {
+               for (i = 0; attr_name(attrs[i]); i++) {
+                       error = device_create_file(dev, &attrs[i]);
+                       if (error)
+                               break;
+               }
+               if (error)
+                       while (--i >= 0)
+                               device_remove_file(dev, &attrs[i]);
+       }
+       return error;
+}
+
+static void device_remove_attributes(struct device *dev,
+                                    struct device_attribute *attrs)
+{
+       int i;
+
+       if (attrs)
+               for (i = 0; attr_name(attrs[i]); i++)
+                       device_remove_file(dev, &attrs[i]);
+}
+
+static int device_add_groups(struct device *dev,
+                            struct attribute_group **groups)
+{
        int error = 0;
+       int i;
 
-       if (dev->groups) {
-               for (i = 0; dev->groups[i]; i++) {
-                       error = sysfs_create_group(&dev->kobj, dev->groups[i]);
+       if (groups) {
+               for (i = 0; groups[i]; i++) {
+                       error = sysfs_create_group(&dev->kobj, groups[i]);
                        if (error) {
                                while (--i >= 0)
-                                       sysfs_remove_group(&dev->kobj, dev->groups[i]);
-                               goto out;
+                                       sysfs_remove_group(&dev->kobj, groups[i]);
+                               break;
                        }
                }
        }
-out:
        return error;
 }
 
-static void device_remove_groups(struct device *dev)
+static void device_remove_groups(struct device *dev,
+                                struct attribute_group **groups)
 {
        int i;
-       if (dev->groups) {
-               for (i = 0; dev->groups[i]; i++) {
-                       sysfs_remove_group(&dev->kobj, dev->groups[i]);
-               }
-       }
+
+       if (groups)
+               for (i = 0; groups[i]; i++)
+                       sysfs_remove_group(&dev->kobj, groups[i]);
 }
 
 static int device_add_attrs(struct device *dev)
 {
        struct class *class = dev->class;
        struct device_type *type = dev->type;
-       int error = 0;
-       int i;
+       int error;
 
-       if (class && class->dev_attrs) {
-               for (i = 0; attr_name(class->dev_attrs[i]); i++) {
-                       error = device_create_file(dev, &class->dev_attrs[i]);
-                       if (error)
-                               break;
-               }
+       if (class) {
+               error = device_add_attributes(dev, class->dev_attrs);
                if (error)
-                       while (--i >= 0)
-                               device_remove_file(dev, &class->dev_attrs[i]);
+                       return error;
        }
 
-       if (type && type->attrs) {
-               for (i = 0; attr_name(type->attrs[i]); i++) {
-                       error = device_create_file(dev, &type->attrs[i]);
-                       if (error)
-                               break;
-               }
+       if (type) {
+               error = device_add_groups(dev, type->groups);
                if (error)
-                       while (--i >= 0)
-                               device_remove_file(dev, &type->attrs[i]);
+                       goto err_remove_class_attrs;
        }
 
+       error = device_add_groups(dev, dev->groups);
+       if (error)
+               goto err_remove_type_groups;
+
+       return 0;
+
+ err_remove_type_groups:
+       if (type)
+               device_remove_groups(dev, type->groups);
+ err_remove_class_attrs:
+       if (class)
+               device_remove_attributes(dev, class->dev_attrs);
+
        return error;
 }
 
@@ -311,17 +404,14 @@ static void device_remove_attrs(struct device *dev)
 {
        struct class *class = dev->class;
        struct device_type *type = dev->type;
-       int i;
 
-       if (class && class->dev_attrs) {
-               for (i = 0; attr_name(class->dev_attrs[i]); i++)
-                       device_remove_file(dev, &class->dev_attrs[i]);
-       }
+       device_remove_groups(dev, dev->groups);
 
-       if (type && type->attrs) {
-               for (i = 0; attr_name(type->attrs[i]); i++)
-                       device_remove_file(dev, &type->attrs[i]);
-       }
+       if (type)
+               device_remove_groups(dev, type->groups);
+
+       if (class)
+               device_remove_attributes(dev, class->dev_attrs);
 }
 
 
@@ -395,9 +485,10 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
 EXPORT_SYMBOL_GPL(device_remove_bin_file);
 
 /**
- * device_schedule_callback - helper to schedule a callback for a device
+ * device_schedule_callback_owner - helper to schedule a callback for a device
  * @dev: device.
  * @func: callback function to invoke later.
+ * @owner: module owning the callback routine
  *
  * Attribute methods must not unregister themselves or their parent device
  * (which would amount to the same thing).  Attempts to do so will deadlock,
@@ -408,20 +499,23 @@ EXPORT_SYMBOL_GPL(device_remove_bin_file);
  * argument in the workqueue's process context.  @dev will be pinned until
  * @func returns.
  *
+ * This routine is usually called via the inline device_schedule_callback(),
+ * which automatically sets @owner to THIS_MODULE.
+ *
  * Returns 0 if the request was submitted, -ENOMEM if storage could not
- * be allocated.
+ * be allocated, -ENODEV if a reference to @owner isn't available.
  *
  * NOTE: This routine won't work if CONFIG_SYSFS isn't set!  It uses an
  * underlying sysfs routine (since it is intended for use by attribute
  * methods), and if sysfs isn't available you'll get nothing but -ENOSYS.
  */
-int device_schedule_callback(struct device *dev,
-               void (*func)(struct device *))
+int device_schedule_callback_owner(struct device *dev,
+               void (*func)(struct device *), struct module *owner)
 {
        return sysfs_schedule_callback(&dev->kobj,
-                       (void (*)(void *)) func, dev);
+                       (void (*)(void *)) func, dev, owner);
 }
-EXPORT_SYMBOL_GPL(device_schedule_callback);
+EXPORT_SYMBOL_GPL(device_schedule_callback_owner);
 
 static void klist_children_get(struct klist_node *n)
 {
@@ -471,7 +565,7 @@ static struct kobject * get_device_parent(struct device *dev,
        /* Set the parent to the class, not the parent device */
        /* this keeps sysfs from having a symlink to make old udevs happy */
        if (dev->class)
-               return &dev->class->subsys.kset.kobj;
+               return &dev->class->subsys.kobj;
        else if (parent)
                return &parent->kobj;
 
@@ -483,7 +577,7 @@ static struct kobject *virtual_device_parent(struct device *dev)
        static struct kobject *virtual_dir = NULL;
 
        if (!virtual_dir)
-               virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
+               virtual_dir = kobject_add_dir(&devices_subsys.kobj, "virtual");
 
        return virtual_dir;
 }
@@ -586,10 +680,11 @@ int device_add(struct device *dev)
                                             BUS_NOTIFY_ADD_DEVICE, dev);
 
        dev->uevent_attr.attr.name = "uevent";
-       dev->uevent_attr.attr.mode = S_IWUSR;
+       dev->uevent_attr.attr.mode = S_IRUGO | S_IWUSR;
        if (dev->driver)
                dev->uevent_attr.attr.owner = dev->driver->owner;
        dev->uevent_attr.store = store_uevent;
+       dev->uevent_attr.show = show_uevent;
        error = device_create_file(dev, &dev->uevent_attr);
        if (error)
                goto attrError;
@@ -616,12 +711,12 @@ int device_add(struct device *dev)
        }
 
        if (dev->class) {
-               sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj,
+               sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj,
                                  "subsystem");
                /* If this is not a "fake" compatible device, then create the
                 * symlink from the class to the device. */
-               if (dev->kobj.parent != &dev->class->subsys.kset.kobj)
-                       sysfs_create_link(&dev->class->subsys.kset.kobj,
+               if (dev->kobj.parent != &dev->class->subsys.kobj)
+                       sysfs_create_link(&dev->class->subsys.kobj,
                                          &dev->kobj, dev->bus_id);
                if (parent) {
                        sysfs_create_link(&dev->kobj, &dev->parent->kobj,
@@ -638,16 +733,12 @@ int device_add(struct device *dev)
 
        if ((error = device_add_attrs(dev)))
                goto AttrsError;
-       if ((error = device_add_groups(dev)))
-               goto GroupError;
        if ((error = device_pm_add(dev)))
                goto PMError;
        if ((error = bus_add_device(dev)))
                goto BusError;
-       if (!dev->uevent_suppress)
-               kobject_uevent(&dev->kobj, KOBJ_ADD);
-       if ((error = bus_attach_device(dev)))
-               goto AttachError;
+       kobject_uevent(&dev->kobj, KOBJ_ADD);
+       bus_attach_device(dev);
        if (parent)
                klist_add_tail(&dev->knode_parent, &parent->klist_children);
 
@@ -663,19 +754,15 @@ int device_add(struct device *dev)
                up(&dev->class->sem);
        }
  Done:
-       kfree(class_name);
+       kfree(class_name);
        put_device(dev);
        return error;
- AttachError:
-       bus_remove_device(dev);
  BusError:
        device_pm_remove(dev);
  PMError:
        if (dev->bus)
                blocking_notifier_call_chain(&dev->bus->bus_notifier,
                                             BUS_NOTIFY_DEL_DEVICE, dev);
-       device_remove_groups(dev);
- GroupError:
        device_remove_attrs(dev);
  AttrsError:
        if (dev->devt_attr) {
@@ -687,8 +774,8 @@ int device_add(struct device *dev)
                sysfs_remove_link(&dev->kobj, "subsystem");
                /* If this is not a "fake" compatible device, remove the
                 * symlink from the class to the device. */
-               if (dev->kobj.parent != &dev->class->subsys.kset.kobj)
-                       sysfs_remove_link(&dev->class->subsys.kset.kobj,
+               if (dev->kobj.parent != &dev->class->subsys.kobj)
+                       sysfs_remove_link(&dev->class->subsys.kobj,
                                          dev->bus_id);
                if (parent) {
 #ifdef CONFIG_SYSFS_DEPRECATED
@@ -788,8 +875,8 @@ void device_del(struct device * dev)
                sysfs_remove_link(&dev->kobj, "subsystem");
                /* If this is not a "fake" compatible device, remove the
                 * symlink from the class to the device. */
-               if (dev->kobj.parent != &dev->class->subsys.kset.kobj)
-                       sysfs_remove_link(&dev->class->subsys.kset.kobj,
+               if (dev->kobj.parent != &dev->class->subsys.kobj)
+                       sysfs_remove_link(&dev->class->subsys.kobj,
                                          dev->bus_id);
                if (parent) {
 #ifdef CONFIG_SYSFS_DEPRECATED
@@ -838,7 +925,6 @@ void device_del(struct device * dev)
                }
        }
        device_remove_file(dev, &dev->uevent_attr);
-       device_remove_groups(dev);
        device_remove_attrs(dev);
        bus_remove_device(dev);
 
@@ -1106,9 +1192,9 @@ int device_rename(struct device *dev, char *new_name)
 #endif
 
        if (dev->class) {
-               sysfs_remove_link(&dev->class->subsys.kset.kobj,
+               sysfs_remove_link(&dev->class->subsys.kobj,
                                  old_symlink_name);
-               sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
+               sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
                                  dev->bus_id);
        }
        put_device(dev);