Merge commit 'upstream/master'
[pandora-kernel.git] / drivers / base / core.c
index ee0a51a..068aa1c 100644 (file)
 #include <linux/genhd.h>
 #include <linux/kallsyms.h>
 #include <linux/semaphore.h>
+#include <linux/mutex.h>
 
 #include "base.h"
 #include "power/power.h"
 
 int (*platform_notify)(struct device *dev) = NULL;
 int (*platform_notify_remove)(struct device *dev) = NULL;
+static struct kobject *dev_kobj;
+struct kobject *sysfs_dev_char_kobj;
+struct kobject *sysfs_dev_block_kobj;
 
 #ifdef CONFIG_BLOCK
 static inline int device_is_not_partition(struct device *dev)
@@ -112,12 +116,10 @@ static void device_release(struct kobject *kobj)
                dev->type->release(dev);
        else if (dev->class && dev->class->dev_release)
                dev->class->dev_release(dev);
-       else {
-               printk(KERN_ERR "Device '%s' does not have a release() "
+       else
+               WARN(1, KERN_ERR "Device '%s' does not have a release() "
                        "function, it is broken and must be fixed.\n",
                        dev->bus_id);
-               WARN_ON(1);
-       }
 }
 
 static struct kobj_type device_ktype = {
@@ -548,7 +550,7 @@ static struct kobject *get_device_parent(struct device *dev,
 {
        /* class devices without a parent live in /sys/class/<classname>/ */
        if (dev->class && (!parent || parent->class != dev->class))
-               return &dev->class->subsys.kobj;
+               return &dev->class->p->class_subsys.kobj;
        /* all other devices keep their parent */
        else if (parent)
                return &parent->kobj;
@@ -594,13 +596,13 @@ static struct kobject *get_device_parent(struct device *dev,
                        parent_kobj = &parent->kobj;
 
                /* find our class-directory at the parent and reference it */
-               spin_lock(&dev->class->class_dirs.list_lock);
-               list_for_each_entry(k, &dev->class->class_dirs.list, entry)
+               spin_lock(&dev->class->p->class_dirs.list_lock);
+               list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
                        if (k->parent == parent_kobj) {
                                kobj = kobject_get(k);
                                break;
                        }
-               spin_unlock(&dev->class->class_dirs.list_lock);
+               spin_unlock(&dev->class->p->class_dirs.list_lock);
                if (kobj)
                        return kobj;
 
@@ -608,7 +610,7 @@ static struct kobject *get_device_parent(struct device *dev,
                k = kobject_create();
                if (!k)
                        return NULL;
-               k->kset = &dev->class->class_dirs;
+               k->kset = &dev->class->p->class_dirs;
                retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
                if (retval < 0) {
                        kobject_put(k);
@@ -627,7 +629,7 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
 {
        /* see if we live in a "glue" directory */
        if (!glue_dir || !dev->class ||
-           glue_dir->kset != &dev->class->class_dirs)
+           glue_dir->kset != &dev->class->p->class_dirs)
                return;
 
        kobject_put(glue_dir);
@@ -654,17 +656,18 @@ static int device_add_class_symlinks(struct device *dev)
        if (!dev->class)
                return 0;
 
-       error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj,
+       error = sysfs_create_link(&dev->kobj,
+                                 &dev->class->p->class_subsys.kobj,
                                  "subsystem");
        if (error)
                goto out;
 
 #ifdef CONFIG_SYSFS_DEPRECATED
        /* stacked class devices need a symlink in the class directory */
-       if (dev->kobj.parent != &dev->class->subsys.kobj &&
+       if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
            device_is_not_partition(dev)) {
-               error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
-                                         dev->bus_id);
+               error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
+                                         &dev->kobj, dev->bus_id);
                if (error)
                        goto out_subsys;
        }
@@ -701,13 +704,14 @@ out_device:
        if (dev->parent && device_is_not_partition(dev))
                sysfs_remove_link(&dev->kobj, "device");
 out_busid:
-       if (dev->kobj.parent != &dev->class->subsys.kobj &&
+       if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
            device_is_not_partition(dev))
-               sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+               sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+                                 dev->bus_id);
 #else
        /* link in the class directory pointing to the device */
-       error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
-                                 dev->bus_id);
+       error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
+                                 &dev->kobj, dev->bus_id);
        if (error)
                goto out_subsys;
 
@@ -720,7 +724,7 @@ out_busid:
        return 0;
 
 out_busid:
-       sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+       sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id);
 #endif
 
 out_subsys:
@@ -746,14 +750,15 @@ static void device_remove_class_symlinks(struct device *dev)
                sysfs_remove_link(&dev->kobj, "device");
        }
 
-       if (dev->kobj.parent != &dev->class->subsys.kobj &&
+       if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
            device_is_not_partition(dev))
-               sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+               sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+                                 dev->bus_id);
 #else
        if (dev->parent && device_is_not_partition(dev))
                sysfs_remove_link(&dev->kobj, "device");
 
-       sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+       sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id);
 #endif
 
        sysfs_remove_link(&dev->kobj, "subsystem");
@@ -775,6 +780,54 @@ int dev_set_name(struct device *dev, const char *fmt, ...)
 }
 EXPORT_SYMBOL_GPL(dev_set_name);
 
+/**
+ * device_to_dev_kobj - select a /sys/dev/ directory for the device
+ * @dev: device
+ *
+ * By default we select char/ for new entries.  Setting class->dev_obj
+ * to NULL prevents an entry from being created.  class->dev_kobj must
+ * be set (or cleared) before any devices are registered to the class
+ * otherwise device_create_sys_dev_entry() and
+ * device_remove_sys_dev_entry() will disagree about the the presence
+ * of the link.
+ */
+static struct kobject *device_to_dev_kobj(struct device *dev)
+{
+       struct kobject *kobj;
+
+       if (dev->class)
+               kobj = dev->class->dev_kobj;
+       else
+               kobj = sysfs_dev_char_kobj;
+
+       return kobj;
+}
+
+static int device_create_sys_dev_entry(struct device *dev)
+{
+       struct kobject *kobj = device_to_dev_kobj(dev);
+       int error = 0;
+       char devt_str[15];
+
+       if (kobj) {
+               format_dev_t(devt_str, dev->devt);
+               error = sysfs_create_link(kobj, &dev->kobj, devt_str);
+       }
+
+       return error;
+}
+
+static void device_remove_sys_dev_entry(struct device *dev)
+{
+       struct kobject *kobj = device_to_dev_kobj(dev);
+       char devt_str[15];
+
+       if (kobj) {
+               format_dev_t(devt_str, dev->devt);
+               sysfs_remove_link(kobj, devt_str);
+       }
+}
+
 /**
  * device_add - add device to device hierarchy.
  * @dev: device.
@@ -829,6 +882,10 @@ int device_add(struct device *dev)
                error = device_create_file(dev, &devt_attr);
                if (error)
                        goto ueventattrError;
+
+               error = device_create_sys_dev_entry(dev);
+               if (error)
+                       goto devtattrError;
        }
 
        error = device_add_class_symlinks(dev);
@@ -849,15 +906,16 @@ int device_add(struct device *dev)
                klist_add_tail(&dev->knode_parent, &parent->klist_children);
 
        if (dev->class) {
-               down(&dev->class->sem);
+               mutex_lock(&dev->class->p->class_mutex);
                /* tie the class to the device */
-               list_add_tail(&dev->node, &dev->class->devices);
+               list_add_tail(&dev->node, &dev->class->p->class_devices);
 
                /* notify any interfaces that the device is here */
-               list_for_each_entry(class_intf, &dev->class->interfaces, node)
+               list_for_each_entry(class_intf,
+                                   &dev->class->p->class_interfaces, node)
                        if (class_intf->add_dev)
                                class_intf->add_dev(dev, class_intf);
-               up(&dev->class->sem);
+               mutex_unlock(&dev->class->p->class_mutex);
        }
  Done:
        put_device(dev);
@@ -872,6 +930,9 @@ int device_add(struct device *dev)
  AttrsError:
        device_remove_class_symlinks(dev);
  SymlinkError:
+       if (MAJOR(dev->devt))
+               device_remove_sys_dev_entry(dev);
+ devtattrError:
        if (MAJOR(dev->devt))
                device_remove_file(dev, &devt_attr);
  ueventattrError:
@@ -948,19 +1009,22 @@ void device_del(struct device *dev)
        device_pm_remove(dev);
        if (parent)
                klist_del(&dev->knode_parent);
-       if (MAJOR(dev->devt))
+       if (MAJOR(dev->devt)) {
+               device_remove_sys_dev_entry(dev);
                device_remove_file(dev, &devt_attr);
+       }
        if (dev->class) {
                device_remove_class_symlinks(dev);
 
-               down(&dev->class->sem);
+               mutex_lock(&dev->class->p->class_mutex);
                /* notify any interfaces that the device is now gone */
-               list_for_each_entry(class_intf, &dev->class->interfaces, node)
+               list_for_each_entry(class_intf,
+                                   &dev->class->p->class_interfaces, node)
                        if (class_intf->remove_dev)
                                class_intf->remove_dev(dev, class_intf);
                /* remove the device from the class list */
                list_del_init(&dev->node);
-               up(&dev->class->sem);
+               mutex_unlock(&dev->class->p->class_mutex);
        }
        device_remove_file(dev, &uevent_attr);
        device_remove_attrs(dev);
@@ -1074,7 +1138,25 @@ int __init devices_init(void)
        devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
        if (!devices_kset)
                return -ENOMEM;
+       dev_kobj = kobject_create_and_add("dev", NULL);
+       if (!dev_kobj)
+               goto dev_kobj_err;
+       sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
+       if (!sysfs_dev_block_kobj)
+               goto block_kobj_err;
+       sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
+       if (!sysfs_dev_char_kobj)
+               goto char_kobj_err;
+
        return 0;
+
+ char_kobj_err:
+       kobject_put(sysfs_dev_block_kobj);
+ block_kobj_err:
+       kobject_put(dev_kobj);
+ dev_kobj_err:
+       kset_unregister(devices_kset);
+       return -ENOMEM;
 }
 
 EXPORT_SYMBOL_GPL(device_for_each_child);
@@ -1157,49 +1239,12 @@ error:
 }
 EXPORT_SYMBOL_GPL(device_create_vargs);
 
-/**
- * device_create_drvdata - creates a device and registers it with sysfs
- * @class: pointer to the struct class that this device should be registered to
- * @parent: pointer to the parent struct device of this new device, if any
- * @devt: the dev_t for the char device to be added
- * @drvdata: the data to be added to the device for callbacks
- * @fmt: string for the device's name
- *
- * This function can be used by char device classes.  A struct device
- * will be created in sysfs, registered to the specified class.
- *
- * A "dev" file will be created, showing the dev_t for the device, if
- * the dev_t is not 0,0.
- * If a pointer to a parent struct device is passed in, the newly created
- * struct device will be a child of that device in sysfs.
- * The pointer to the struct device will be returned from the call.
- * Any further sysfs files that might be required can be created using this
- * pointer.
- *
- * Note: the struct class passed to this function must have previously
- * been created with a call to class_create().
- */
-struct device *device_create_drvdata(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_drvdata);
-
 /**
  * device_create - creates a device and registers it with sysfs
  * @class: pointer to the struct class that this device should be registered to
  * @parent: pointer to the parent struct device of this new device, if any
  * @devt: the dev_t for the char device to be added
+ * @drvdata: the data to be added to the device for callbacks
  * @fmt: string for the device's name
  *
  * This function can be used by char device classes.  A struct device
@@ -1217,13 +1262,13 @@ EXPORT_SYMBOL_GPL(device_create_drvdata);
  * been created with a call to class_create().
  */
 struct device *device_create(struct class *class, struct device *parent,
-                            dev_t devt, const char *fmt, ...)
+                            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, NULL, fmt, vargs);
+       dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
        va_end(vargs);
        return dev;
 }
@@ -1248,7 +1293,7 @@ void device_destroy(struct class *class, dev_t devt)
 {
        struct device *dev;
 
-       dev = class_find_device(class, &devt, __match_devt);
+       dev = class_find_device(class, NULL, &devt, __match_devt);
        if (dev) {
                put_device(dev);
                device_unregister(dev);
@@ -1298,8 +1343,9 @@ int device_rename(struct device *dev, char *new_name)
        if (old_class_name) {
                new_class_name = make_class_name(dev->class->name, &dev->kobj);
                if (new_class_name) {
-                       error = sysfs_create_link(&dev->parent->kobj,
-                                                 &dev->kobj, new_class_name);
+                       error = sysfs_create_link_nowarn(&dev->parent->kobj,
+                                                        &dev->kobj,
+                                                        new_class_name);
                        if (error)
                                goto out;
                        sysfs_remove_link(&dev->parent->kobj, old_class_name);
@@ -1307,11 +1353,12 @@ int device_rename(struct device *dev, char *new_name)
        }
 #else
        if (dev->class) {
-               error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
-                                         dev->bus_id);
+               error = sysfs_create_link_nowarn(&dev->class->p->class_subsys.kobj,
+                                                &dev->kobj, dev->bus_id);
                if (error)
                        goto out;
-               sysfs_remove_link(&dev->class->subsys.kobj, old_device_name);
+               sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+                                 old_device_name);
        }
 #endif
 
@@ -1447,4 +1494,7 @@ void device_shutdown(void)
                        dev->driver->shutdown(dev);
                }
        }
+       kobject_put(sysfs_dev_char_kobj);
+       kobject_put(sysfs_dev_block_kobj);
+       kobject_put(dev_kobj);
 }