#include <linux/string.h>
#include <linux/kdev_t.h>
#include <linux/notifier.h>
-
+#include <linux/genhd.h>
#include <asm/semaphore.h>
#include "base.h"
if (dev->bus && dev->bus->uevent) {
retval = dev->bus->uevent(dev, env);
if (retval)
- pr_debug ("%s: bus uevent() returned %d\n",
- __FUNCTION__, retval);
+ pr_debug("device: '%s': %s: bus uevent() returned %d\n",
+ dev->bus_id, __FUNCTION__, retval);
}
/* have the class specific function add its stuff */
if (dev->class && dev->class->dev_uevent) {
retval = dev->class->dev_uevent(dev, env);
if (retval)
- pr_debug("%s: class uevent() returned %d\n",
+ pr_debug("device: '%s': %s: class uevent() "
+ "returned %d\n", dev->bus_id,
__FUNCTION__, retval);
}
if (dev->type && dev->type->uevent) {
retval = dev->type->uevent(dev, env);
if (retval)
- pr_debug("%s: dev_type uevent() returned %d\n",
+ pr_debug("device: '%s': %s: dev_type uevent() "
+ "returned %d\n", dev->bus_id,
__FUNCTION__, retval);
}
static struct device_attribute devt_attr =
__ATTR(dev, S_IRUGO, show_dev, NULL);
-/*
- * devices_subsys - structure to be registered with kobject core.
- */
-
-decl_subsys(devices, &device_ktype, &device_uevent_ops);
+/* kset to create /sys/devices/ */
+struct kset *devices_kset;
/**
void device_initialize(struct device *dev)
{
- kobj_set_kset_s(dev, devices_subsys);
- kobject_init(&dev->kobj);
+ dev->kobj.kset = devices_kset;
+ kobject_init(&dev->kobj, &device_ktype);
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
}
#ifdef CONFIG_SYSFS_DEPRECATED
-static struct kobject * get_device_parent(struct device *dev,
- struct device *parent)
+static struct kobject *get_device_parent(struct device *dev,
+ struct device *parent)
{
- /*
- * Set the parent to the class, not the parent device
- * for topmost devices in class hierarchy.
- * This keeps sysfs from having a symlink to make old
- * udevs happy
- */
+ /* class devices without a parent live in /sys/class/<classname>/ */
if (dev->class && (!parent || parent->class != dev->class))
return &dev->class->subsys.kobj;
+ /* all other devices keep their parent */
else if (parent)
return &parent->kobj;
return NULL;
}
+
+static inline void cleanup_device_parent(struct device *dev) {}
#else
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.kobj, "virtual");
+ virtual_dir = kobject_create_and_add("virtual",
+ &devices_kset->kobj);
return virtual_dir;
}
-static struct kobject * get_device_parent(struct device *dev,
- struct device *parent)
+static struct kobject *get_device_parent(struct device *dev,
+ struct device *parent)
{
+ int retval;
+
if (dev->class) {
struct kobject *kobj = NULL;
struct kobject *parent_kobj;
/*
* 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.
+ * Class-devices with a non class-device as parent, live
+ * in a "glue" directory to prevent namespace collisions.
*/
if (parent == NULL)
parent_kobj = virtual_device_parent(dev);
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);
+ k = kobject_create();
+ if (!k)
+ return NULL;
+ k->kset = &dev->class->class_dirs;
+ retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
+ if (retval < 0) {
+ kobject_put(k);
+ return NULL;
+ }
+ /* do not emit an uevent for this simple "glue" directory */
+ return k;
}
if (parent)
return &parent->kobj;
return NULL;
}
+
+static void cleanup_device_parent(struct device *dev)
+{
+ struct kobject *glue_dir = dev->kobj.parent;
+
+ /* see if we live in a "glue" directory */
+ if (!dev->class || glue_dir->kset != &dev->class->class_dirs)
+ return;
+
+ kobject_put(glue_dir);
+}
#endif
static int setup_parent(struct device *dev, struct device *parent)
if (!dev->class)
return 0;
+
error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj,
"subsystem");
if (error)
goto out;
- /*
- * 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.kobj) {
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+ /* stacked class devices need a symlink in the class directory */
+ if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ dev->type != &part_type) {
error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
dev->bus_id);
if (error)
goto out_subsys;
}
- if (dev->parent) {
-#ifdef CONFIG_SYSFS_DEPRECATED
- {
- struct device *parent = dev->parent;
- char *class_name;
-
- /*
- * In old sysfs stacked class devices had 'device'
- * link pointing to real device instead of parent
- */
- while (parent->class && !parent->bus && parent->parent)
- parent = parent->parent;
-
- error = sysfs_create_link(&dev->kobj,
- &parent->kobj,
- "device");
- if (error)
- goto out_busid;
- class_name = make_class_name(dev->class->name,
- &dev->kobj);
- if (class_name)
- error = sysfs_create_link(&dev->parent->kobj,
- &dev->kobj, class_name);
- kfree(class_name);
- if (error)
- goto out_device;
- }
-#else
- error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
+ if (dev->parent && dev->type != &part_type) {
+ struct device *parent = dev->parent;
+ char *class_name;
+
+ /*
+ * stacked class devices have the 'device' link
+ * pointing to the bus device instead of the parent
+ */
+ while (parent->class && !parent->bus && parent->parent)
+ parent = parent->parent;
+
+ error = sysfs_create_link(&dev->kobj,
+ &parent->kobj,
"device");
if (error)
goto out_busid;
-#endif
+
+ class_name = make_class_name(dev->class->name,
+ &dev->kobj);
+ if (class_name)
+ error = sysfs_create_link(&dev->parent->kobj,
+ &dev->kobj, class_name);
+ kfree(class_name);
+ if (error)
+ goto out_device;
}
return 0;
-#ifdef CONFIG_SYSFS_DEPRECATED
out_device:
- if (dev->parent)
+ if (dev->parent && dev->type != &part_type)
sysfs_remove_link(&dev->kobj, "device");
-#endif
out_busid:
- if (dev->kobj.parent != &dev->class->subsys.kobj)
+ if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ dev->type != &part_type)
sysfs_remove_link(&dev->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);
+ if (error)
+ goto out_subsys;
+
+ if (dev->parent && dev->type != &part_type) {
+ error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
+ "device");
+ if (error)
+ goto out_busid;
+ }
+ return 0;
+
+out_busid:
+ sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+#endif
+
out_subsys:
sysfs_remove_link(&dev->kobj, "subsystem");
out:
{
if (!dev->class)
return;
- if (dev->parent) {
+
#ifdef CONFIG_SYSFS_DEPRECATED
+ if (dev->parent && dev->type != &part_type) {
char *class_name;
class_name = make_class_name(dev->class->name, &dev->kobj);
sysfs_remove_link(&dev->parent->kobj, class_name);
kfree(class_name);
}
-#endif
sysfs_remove_link(&dev->kobj, "device");
}
- if (dev->kobj.parent != &dev->class->subsys.kobj)
+
+ if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ dev->type != &part_type)
sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+#else
+ if (dev->parent && dev->type != &part_type)
+ sysfs_remove_link(&dev->kobj, "device");
+
+ sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+#endif
+
sysfs_remove_link(&dev->kobj, "subsystem");
}
{
struct device *parent = NULL;
struct class_interface *class_intf;
- int error = -EINVAL;
+ int error;
+
+ error = pm_sleep_lock();
+ if (error) {
+ dev_warn(dev, "Suspicious %s during suspend\n", __FUNCTION__);
+ dump_stack();
+ return error;
+ }
dev = get_device(dev);
- if (!dev || !strlen(dev->bus_id))
+ if (!dev || !strlen(dev->bus_id)) {
+ error = -EINVAL;
goto Error;
+ }
- pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
+ pr_debug("device: '%s': %s\n", dev->bus_id, __FUNCTION__);
parent = get_device(dev->parent);
error = setup_parent(dev, parent);
goto Error;
/* first, register with generic layer. */
- kobject_set_name(&dev->kobj, "%s", dev->bus_id);
- error = kobject_add(&dev->kobj);
+ error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
if (error)
goto Error;
/* notify clients of device entry (new way) */
if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
error = device_create_file(dev, &uevent_attr);
error = device_add_attrs(dev);
if (error)
goto AttrsError;
- error = device_pm_add(dev);
+ error = dpm_sysfs_add(dev);
if (error)
goto PMError;
+ device_pm_add(dev);
error = bus_add_device(dev);
if (error)
goto BusError;
}
Done:
put_device(dev);
+ pm_sleep_unlock();
return error;
BusError:
device_pm_remove(dev);
+ dpm_sysfs_remove(dev);
PMError:
if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev);
device_remove_attrs(dev);
AttrsError:
SymlinkError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
-
- if (dev->class) {
- 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.kobj)
- sysfs_remove_link(&dev->class->subsys.kobj,
- dev->bus_id);
- if (parent) {
-#ifdef CONFIG_SYSFS_DEPRECATED
- char *class_name = make_class_name(dev->class->name,
- &dev->kobj);
- if (class_name)
- sysfs_remove_link(&dev->parent->kobj,
- class_name);
- kfree(class_name);
-#endif
- sysfs_remove_link(&dev->kobj, "device");
- }
- }
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
*/
void put_device(struct device * dev)
{
+ /* might_sleep(); */
if (dev)
kobject_put(&dev->kobj);
}
struct device * parent = dev->parent;
struct class_interface *class_intf;
+ device_pm_remove(dev);
if (parent)
klist_del(&dev->knode_parent);
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
if (dev->class) {
- 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.kobj)
- sysfs_remove_link(&dev->class->subsys.kobj,
- dev->bus_id);
- if (parent) {
-#ifdef CONFIG_SYSFS_DEPRECATED
- char *class_name = make_class_name(dev->class->name,
- &dev->kobj);
- if (class_name)
- sysfs_remove_link(&dev->parent->kobj,
- class_name);
- kfree(class_name);
-#endif
- sysfs_remove_link(&dev->kobj, "device");
- }
+ device_remove_class_symlinks(dev);
down(&dev->class->sem);
/* notify any interfaces that the device is now gone */
/* 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, &uevent_attr);
device_remove_attrs(dev);
if (platform_notify_remove)
platform_notify_remove(dev);
if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev);
- device_pm_remove(dev);
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
+ cleanup_device_parent(dev);
kobject_del(&dev->kobj);
- if (parent)
- put_device(parent);
+ put_device(parent);
}
/**
*/
void device_unregister(struct device * dev)
{
- pr_debug("DEV: Unregistering device. ID = '%s'\n", dev->bus_id);
+ pr_debug("device: '%s': %s\n", dev->bus_id, __FUNCTION__);
device_del(dev);
put_device(dev);
}
int __init devices_init(void)
{
- return subsystem_register(&devices_subsys);
+ devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
+ if (!devices_kset)
+ return -ENOMEM;
+ return 0;
}
EXPORT_SYMBOL_GPL(device_for_each_child);
static void device_create_release(struct device *dev)
{
- pr_debug("%s called for %s\n", __FUNCTION__, dev->bus_id);
+ pr_debug("device: '%s': %s\n", dev->bus_id, __FUNCTION__);
kfree(dev);
}
EXPORT_SYMBOL_GPL(device_create);
/**
- * device_destroy - removes a device that was created with device_create()
+ * find_device - finds a device that was created with device_create()
* @class: pointer to the struct class that this device was registered with
* @devt: the dev_t of the device that was previously registered
- *
- * This call unregisters and cleans up a device that was created with a
- * call to device_create().
*/
-void device_destroy(struct class *class, dev_t devt)
+static struct device *find_device(struct class *class, dev_t devt)
{
struct device *dev = NULL;
struct device *dev_tmp;
}
}
up(&class->sem);
+ return dev;
+}
+/**
+ * device_destroy - removes a device that was created with device_create()
+ * @class: pointer to the struct class that this device was registered with
+ * @devt: the dev_t of the device that was previously registered
+ *
+ * This call unregisters and cleans up a device that was created with a
+ * call to device_create().
+ */
+void device_destroy(struct class *class, dev_t devt)
+{
+ struct device *dev;
+
+ dev = find_device(class, devt);
if (dev)
device_unregister(dev);
}
EXPORT_SYMBOL_GPL(device_destroy);
+#ifdef CONFIG_PM_SLEEP
+/**
+ * destroy_suspended_device - asks the PM core to remove a suspended device
+ * @class: pointer to the struct class that this device was registered with
+ * @devt: the dev_t of the device that was previously registered
+ *
+ * This call notifies the PM core of the necessity to unregister a suspended
+ * device created with a call to device_create() (devices cannot be
+ * unregistered directly while suspended, since the PM core holds their
+ * semaphores at that time).
+ *
+ * It can only be called within the scope of a system sleep transition. In
+ * practice this means it has to be directly or indirectly invoked either by
+ * a suspend or resume method, or by the PM core (e.g. via
+ * disable_nonboot_cpus() or enable_nonboot_cpus()).
+ */
+void destroy_suspended_device(struct class *class, dev_t devt)
+{
+ struct device *dev;
+
+ dev = find_device(class, devt);
+ if (dev)
+ device_pm_schedule_removal(dev);
+}
+EXPORT_SYMBOL_GPL(destroy_suspended_device);
+#endif /* CONFIG_PM_SLEEP */
+
/**
* device_rename - renames a device
* @dev: the pointer to the struct device to be renamed
if (!dev)
return -EINVAL;
- pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name);
+ pr_debug("device: '%s': %s: renaming to '%s'\n", dev->bus_id,
+ __FUNCTION__, new_name);
#ifdef CONFIG_SYSFS_DEPRECATED
if ((dev->class) && (dev->parent))
sysfs_remove_link(&dev->parent->kobj, old_class_name);
}
}
-#endif
-
+#else
if (dev->class) {
sysfs_remove_link(&dev->class->subsys.kobj, old_device_name);
error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
dev->bus_id);
if (error) {
- /* Uh... how to unravel this if restoring can fail? */
dev_err(dev, "%s: sysfs_create_symlink failed (%d)\n",
__FUNCTION__, error);
}
}
+#endif
+
out:
put_device(dev);
put_device(new_parent);
goto out;
}
- pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id,
- new_parent ? new_parent->bus_id : "<NULL>");
+ pr_debug("device: '%s': %s: moving to '%s'\n", dev->bus_id,
+ __FUNCTION__, new_parent ? new_parent->bus_id : "<NULL>");
error = kobject_move(&dev->kobj, new_parent_kobj);
if (error) {
put_device(new_parent);
put_device(dev);
return error;
}
-
EXPORT_SYMBOL_GPL(device_move);
+
+/**
+ * device_shutdown - call ->shutdown() on each device to shutdown.
+ */
+void device_shutdown(void)
+{
+ struct device * dev, *devn;
+
+ list_for_each_entry_safe_reverse(dev, devn, &devices_kset->list,
+ kobj.entry) {
+ if (dev->bus && dev->bus->shutdown) {
+ dev_dbg(dev, "shutdown\n");
+ dev->bus->shutdown(dev);
+ } else if (dev->driver && dev->driver->shutdown) {
+ dev_dbg(dev, "shutdown\n");
+ dev->driver->shutdown(dev);
+ }
+ }
+}