]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/base/core.c
sysfs: add /sys/dev/{char,block} to lookup sysfs path by major:minor
[linux-2.6-omap-h63xx.git] / drivers / base / core.c
index ee0a51a3a41d876451395b0f2a93b6a06a430394..be9aba4dc2fbf9e2d4e746d1841093bc31aa69b9 100644 (file)
@@ -27,6 +27,9 @@
 
 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)
@@ -775,6 +778,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 +880,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);
@@ -872,6 +927,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,8 +1006,10 @@ 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);
 
@@ -1074,7 +1134,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);
@@ -1447,4 +1525,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);
 }