]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/base/core.c
driver core: fix namespace issue with devices assigned to classes
[linux-2.6-omap-h63xx.git] / drivers / base / core.c
index d7fcf823a42a71e08af9da4e62d83b0dae5129fd..658eae5dacda194d7d3396b16616e124b6cdedd4 100644 (file)
@@ -477,34 +477,58 @@ static struct kobject * get_device_parent(struct device *dev,
        return NULL;
 }
 #else
-static struct kobject * virtual_device_parent(struct device *dev)
+static struct kobject *virtual_device_parent(struct device *dev)
 {
-       if (!dev->class)
-               return ERR_PTR(-ENODEV);
-
-       if (!dev->class->virtual_dir) {
-               static struct kobject *virtual_dir = NULL;
+       static struct kobject *virtual_dir = NULL;
 
-               if (!virtual_dir)
-                       virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
-               dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
-       }
+       if (!virtual_dir)
+               virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
 
-       return dev->class->virtual_dir;
+       return virtual_dir;
 }
 
 static struct kobject * get_device_parent(struct device *dev,
                                          struct device *parent)
 {
-       /* if this is a class device, and has no parent, create one */
-       if ((dev->class) && (parent == NULL)) {
-               return virtual_device_parent(dev);
-       } else if (parent)
+       if (dev->class) {
+               struct kobject *kobj = NULL;
+               struct kobject *parent_kobj;
+               struct kobject *k;
+
+               /*
+                * If we have no parent, we live in "virtual".
+                * Class-devices with a bus-device as parent, live
+                * in a class-directory to prevent namespace collisions.
+                */
+               if (parent == NULL)
+                       parent_kobj = virtual_device_parent(dev);
+               else if (parent->class)
+                       return &parent->kobj;
+               else
+                       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)
+                       if (k->parent == parent_kobj) {
+                               kobj = kobject_get(k);
+                               break;
+                       }
+               spin_unlock(&dev->class->class_dirs.list_lock);
+               if (kobj)
+                       return kobj;
+
+               /* or create a new class-directory at the parent device */
+               return kobject_kset_add_dir(&dev->class->class_dirs,
+                                           parent_kobj, dev->class->name);
+       }
+
+       if (parent)
                return &parent->kobj;
        return NULL;
 }
-
 #endif
+
 static int setup_parent(struct device *dev, struct device *parent)
 {
        struct kobject *kobj;
@@ -541,7 +565,6 @@ int device_add(struct device *dev)
        pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
 
        parent = get_device(dev->parent);
-
        error = setup_parent(dev, parent);
        if (error)
                goto Error;
@@ -677,15 +700,6 @@ int device_add(struct device *dev)
 #endif
                        sysfs_remove_link(&dev->kobj, "device");
                }
-
-               down(&dev->class->sem);
-               /* notify any interfaces that the device is now gone */
-               list_for_each_entry(class_intf, &dev->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);
        }
  ueventattrError:
        device_remove_file(dev, &dev->uevent_attr);
@@ -796,6 +810,31 @@ void device_del(struct device * dev)
                /* remove the device from the class list */
                list_del_init(&dev->node);
                up(&dev->class->sem);
+
+               /* If we live in a parent class-directory, unreference it */
+               if (dev->kobj.parent->kset == &dev->class->class_dirs) {
+                       struct device *d;
+                       int other = 0;
+
+                       /*
+                        * if we are the last child of our class, delete
+                        * our class-directory at this parent
+                        */
+                       down(&dev->class->sem);
+                       list_for_each_entry(d, &dev->class->devices, node) {
+                               if (d == dev)
+                                       continue;
+                               if (d->kobj.parent == dev->kobj.parent) {
+                                       other = 1;
+                                       break;
+                               }
+                       }
+                       if (!other)
+                               kobject_del(dev->kobj.parent);
+
+                       kobject_put(dev->kobj.parent);
+                       up(&dev->class->sem);
+               }
        }
        device_remove_file(dev, &dev->uevent_attr);
        device_remove_groups(dev);