diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/Makefile | 3 | ||||
-rw-r--r-- | drivers/base/attribute_container.c | 23 | ||||
-rw-r--r-- | drivers/base/base.h | 64 | ||||
-rw-r--r-- | drivers/base/bus.c | 540 | ||||
-rw-r--r-- | drivers/base/class.c | 319 | ||||
-rw-r--r-- | drivers/base/core.c | 606 | ||||
-rw-r--r-- | drivers/base/cpu.c | 2 | ||||
-rw-r--r-- | drivers/base/dd.c | 164 | ||||
-rw-r--r-- | drivers/base/dmapool.c | 1 | ||||
-rw-r--r-- | drivers/base/driver.c | 216 | ||||
-rw-r--r-- | drivers/base/firmware.c | 26 | ||||
-rw-r--r-- | drivers/base/hypervisor.c | 12 | ||||
-rw-r--r-- | drivers/base/init.c | 10 | ||||
-rw-r--r-- | drivers/base/memory.c | 2 | ||||
-rw-r--r-- | drivers/base/module.c | 94 | ||||
-rw-r--r-- | drivers/base/node.c | 2 | ||||
-rw-r--r-- | drivers/base/platform.c | 239 | ||||
-rw-r--r-- | drivers/base/power/Makefile | 14 | ||||
-rw-r--r-- | drivers/base/power/main.c | 510 | ||||
-rw-r--r-- | drivers/base/power/power.h | 47 | ||||
-rw-r--r-- | drivers/base/power/shutdown.c | 48 | ||||
-rw-r--r-- | drivers/base/sys.c | 49 |
22 files changed, 1765 insertions, 1226 deletions
diff --git a/drivers/base/Makefile b/drivers/base/Makefile index b39ea3f59c9..63e09c015ca 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -11,6 +11,9 @@ obj-$(CONFIG_FW_LOADER) += firmware_class.o obj-$(CONFIG_NUMA) += node.o obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o obj-$(CONFIG_SMP) += topology.o +ifeq ($(CONFIG_SYSFS),y) +obj-$(CONFIG_MODULES) += module.o +endif obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o ifeq ($(CONFIG_DEBUG_DRIVER),y) diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index 7370d7cf598..3b43e8a9f87 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -61,7 +61,7 @@ attribute_container_classdev_to_container(struct class_device *classdev) } EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container); -static struct list_head attribute_container_list; +static LIST_HEAD(attribute_container_list); static DEFINE_MUTEX(attribute_container_mutex); @@ -320,9 +320,14 @@ attribute_container_add_attrs(struct class_device *classdev) struct class_device_attribute **attrs = cont->attrs; int i, error; - if (!attrs) + BUG_ON(attrs && cont->grp); + + if (!attrs && !cont->grp) return 0; + if (cont->grp) + return sysfs_create_group(&classdev->kobj, cont->grp); + for (i = 0; attrs[i]; i++) { error = class_device_create_file(classdev, attrs[i]); if (error) @@ -378,9 +383,14 @@ attribute_container_remove_attrs(struct class_device *classdev) struct class_device_attribute **attrs = cont->attrs; int i; - if (!attrs) + if (!attrs && !cont->grp) return; + if (cont->grp) { + sysfs_remove_group(&classdev->kobj, cont->grp); + return ; + } + for (i = 0; attrs[i]; i++) class_device_remove_file(classdev, attrs[i]); } @@ -429,10 +439,3 @@ attribute_container_find_class_device(struct attribute_container *cont, return cdev; } EXPORT_SYMBOL_GPL(attribute_container_find_class_device); - -int __init -attribute_container_init(void) -{ - INIT_LIST_HEAD(&attribute_container_list); - return 0; -} diff --git a/drivers/base/base.h b/drivers/base/base.h index 10b2fb6c9ce..c0444146c09 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -1,6 +1,42 @@ -/* initialisation functions */ +/** + * struct bus_type_private - structure to hold the private to the driver core portions of the bus_type structure. + * + * @subsys - the struct kset that defines this bus. This is the main kobject + * @drivers_kset - the list of drivers associated with this bus + * @devices_kset - the list of devices associated with this bus + * @klist_devices - the klist to iterate over the @devices_kset + * @klist_drivers - the klist to iterate over the @drivers_kset + * @bus_notifier - the bus notifier list for anything that cares about things + * on this bus. + * @bus - pointer back to the struct bus_type that this structure is associated + * with. + * + * This structure is the one that is the actual kobject allowing struct + * bus_type to be statically allocated safely. Nothing outside of the driver + * core should ever touch these fields. + */ +struct bus_type_private { + struct kset subsys; + struct kset *drivers_kset; + struct kset *devices_kset; + struct klist klist_devices; + struct klist klist_drivers; + struct blocking_notifier_head bus_notifier; + unsigned int drivers_autoprobe:1; + struct bus_type *bus; +}; + +struct driver_private { + struct kobject kobj; + struct klist klist_devices; + struct klist_node knode_bus; + struct module_kobject *mkobj; + struct device_driver *driver; +}; +#define to_driver(obj) container_of(obj, struct driver_private, kobj) +/* initialisation functions */ extern int devices_init(void); extern int buses_init(void); extern int classes_init(void); @@ -13,17 +49,16 @@ static inline int hypervisor_init(void) { return 0; } extern int platform_bus_init(void); extern int system_bus_init(void); extern int cpu_dev_init(void); -extern int attribute_container_init(void); -extern int bus_add_device(struct device * dev); -extern void bus_attach_device(struct device * dev); -extern void bus_remove_device(struct device * dev); +extern int bus_add_device(struct device *dev); +extern void bus_attach_device(struct device *dev); +extern void bus_remove_device(struct device *dev); -extern int bus_add_driver(struct device_driver *); -extern void bus_remove_driver(struct device_driver *); +extern int bus_add_driver(struct device_driver *drv); +extern void bus_remove_driver(struct device_driver *drv); -extern void driver_detach(struct device_driver * drv); -extern int driver_probe_device(struct device_driver *, struct device *); +extern void driver_detach(struct device_driver *drv); +extern int driver_probe_device(struct device_driver *drv, struct device *dev); extern void sysdev_shutdown(void); extern int sysdev_suspend(pm_message_t state); @@ -44,4 +79,13 @@ extern char *make_class_name(const char *name, struct kobject *kobj); extern int devres_release_all(struct device *dev); -extern struct kset devices_subsys; +extern struct kset *devices_kset; + +#if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS) +extern void module_add_driver(struct module *mod, struct device_driver *drv); +extern void module_remove_driver(struct device_driver *drv); +#else +static inline void module_add_driver(struct module *mod, + struct device_driver *drv) { } +static inline void module_remove_driver(struct device_driver *drv) { } +#endif diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 9a19b071c57..055989e9479 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -3,6 +3,8 @@ * * Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Open Source Development Labs + * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de> + * Copyright (c) 2007 Novell Inc. * * This file is released under the GPLv2 * @@ -17,14 +19,13 @@ #include "power/power.h" #define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr) -#define to_bus(obj) container_of(obj, struct bus_type, subsys.kobj) +#define to_bus(obj) container_of(obj, struct bus_type_private, subsys.kobj) /* * sysfs bindings for drivers */ #define to_drv_attr(_attr) container_of(_attr, struct driver_attribute, attr) -#define to_driver(obj) container_of(obj, struct device_driver, kobj) static int __must_check bus_rescan_devices_helper(struct device *dev, @@ -32,37 +33,40 @@ static int __must_check bus_rescan_devices_helper(struct device *dev, static struct bus_type *bus_get(struct bus_type *bus) { - return bus ? container_of(kset_get(&bus->subsys), - struct bus_type, subsys) : NULL; + if (bus) { + kset_get(&bus->p->subsys); + return bus; + } + return NULL; } static void bus_put(struct bus_type *bus) { - kset_put(&bus->subsys); + if (bus) + kset_put(&bus->p->subsys); } -static ssize_t -drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) +static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) { - struct driver_attribute * drv_attr = to_drv_attr(attr); - struct device_driver * drv = to_driver(kobj); + struct driver_attribute *drv_attr = to_drv_attr(attr); + struct driver_private *drv_priv = to_driver(kobj); ssize_t ret = -EIO; if (drv_attr->show) - ret = drv_attr->show(drv, buf); + ret = drv_attr->show(drv_priv->driver, buf); return ret; } -static ssize_t -drv_attr_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count) +static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) { - struct driver_attribute * drv_attr = to_drv_attr(attr); - struct device_driver * drv = to_driver(kobj); + struct driver_attribute *drv_attr = to_drv_attr(attr); + struct driver_private *drv_priv = to_driver(kobj); ssize_t ret = -EIO; if (drv_attr->store) - ret = drv_attr->store(drv, buf, count); + ret = drv_attr->store(drv_priv->driver, buf, count); return ret; } @@ -71,22 +75,12 @@ static struct sysfs_ops driver_sysfs_ops = { .store = drv_attr_store, }; - -static void driver_release(struct kobject * kobj) +static void driver_release(struct kobject *kobj) { - /* - * Yes this is an empty release function, it is this way because struct - * device is always a static object, not a dynamic one. Yes, this is - * not nice and bad, but remember, drivers are code, reference counted - * by the module count, not a device, which is really data. And yes, - * in the future I do want to have all drivers be created dynamically, - * and am working toward that goal, but it will take a bit longer... - * - * But do not let this example give _anyone_ the idea that they can - * create a release function without any code in it at all, to do that - * is almost always wrong. If you have any questions about this, - * please send an email to <greg@kroah.com> - */ + struct driver_private *drv_priv = to_driver(kobj); + + pr_debug("driver: '%s': %s\n", kobject_name(kobj), __FUNCTION__); + kfree(drv_priv); } static struct kobj_type driver_ktype = { @@ -94,34 +88,30 @@ static struct kobj_type driver_ktype = { .release = driver_release, }; - /* * sysfs bindings for buses */ - - -static ssize_t -bus_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) +static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) { - struct bus_attribute * bus_attr = to_bus_attr(attr); - struct bus_type * bus = to_bus(kobj); + struct bus_attribute *bus_attr = to_bus_attr(attr); + struct bus_type_private *bus_priv = to_bus(kobj); ssize_t ret = 0; if (bus_attr->show) - ret = bus_attr->show(bus, buf); + ret = bus_attr->show(bus_priv->bus, buf); return ret; } -static ssize_t -bus_attr_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count) +static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) { - struct bus_attribute * bus_attr = to_bus_attr(attr); - struct bus_type * bus = to_bus(kobj); + struct bus_attribute *bus_attr = to_bus_attr(attr); + struct bus_type_private *bus_priv = to_bus(kobj); ssize_t ret = 0; if (bus_attr->store) - ret = bus_attr->store(bus, buf, count); + ret = bus_attr->store(bus_priv->bus, buf, count); return ret; } @@ -130,24 +120,26 @@ static struct sysfs_ops bus_sysfs_ops = { .store = bus_attr_store, }; -int bus_create_file(struct bus_type * bus, struct bus_attribute * attr) +int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) { int error; if (bus_get(bus)) { - error = sysfs_create_file(&bus->subsys.kobj, &attr->attr); + error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr); bus_put(bus); } else error = -EINVAL; return error; } +EXPORT_SYMBOL_GPL(bus_create_file); -void bus_remove_file(struct bus_type * bus, struct bus_attribute * attr) +void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr) { if (bus_get(bus)) { - sysfs_remove_file(&bus->subsys.kobj, &attr->attr); + sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr); bus_put(bus); } } +EXPORT_SYMBOL_GPL(bus_remove_file); static struct kobj_type bus_ktype = { .sysfs_ops = &bus_sysfs_ops, @@ -166,20 +158,11 @@ static struct kset_uevent_ops bus_uevent_ops = { .filter = bus_uevent_filter, }; -static decl_subsys(bus, &bus_ktype, &bus_uevent_ops); +static struct kset *bus_kset; #ifdef CONFIG_HOTPLUG /* Manually detach a device from its associated driver. */ -static int driver_helper(struct device *dev, void *data) -{ - const char *name = data; - - if (strcmp(name, dev->bus_id) == 0) - return 1; - return 0; -} - static ssize_t driver_unbind(struct device_driver *drv, const char *buf, size_t count) { @@ -187,7 +170,7 @@ static ssize_t driver_unbind(struct device_driver *drv, struct device *dev; int err = -ENODEV; - dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); + dev = bus_find_device_by_name(bus, NULL, buf); if (dev && dev->driver == drv) { if (dev->parent) /* Needed for USB */ down(&dev->parent->sem); @@ -214,7 +197,7 @@ static ssize_t driver_bind(struct device_driver *drv, struct device *dev; int err = -ENODEV; - dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); + dev = bus_find_device_by_name(bus, NULL, buf); if (dev && dev->driver == NULL) { if (dev->parent) /* Needed for USB */ down(&dev->parent->sem); @@ -224,10 +207,13 @@ static ssize_t driver_bind(struct device_driver *drv, if (dev->parent) up(&dev->parent->sem); - if (err > 0) /* success */ + if (err > 0) { + /* success */ err = count; - else if (err == 0) /* driver didn't accept device */ + } else if (err == 0) { + /* driver didn't accept device */ err = -ENODEV; + } } put_device(dev); bus_put(bus); @@ -237,16 +223,16 @@ static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind); static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf) { - return sprintf(buf, "%d\n", bus->drivers_autoprobe); + return sprintf(buf, "%d\n", bus->p->drivers_autoprobe); } static ssize_t store_drivers_autoprobe(struct bus_type *bus, const char *buf, size_t count) { if (buf[0] == '0') - bus->drivers_autoprobe = 0; + bus->p->drivers_autoprobe = 0; else - bus->drivers_autoprobe = 1; + bus->p->drivers_autoprobe = 1; return count; } @@ -255,7 +241,7 @@ static ssize_t store_drivers_probe(struct bus_type *bus, { struct device *dev; - dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); + dev = bus_find_device_by_name(bus, NULL, buf); if (!dev) return -ENODEV; if (bus_rescan_devices_helper(dev, NULL) != 0) @@ -264,49 +250,49 @@ static ssize_t store_drivers_probe(struct bus_type *bus, } #endif -static struct device * next_device(struct klist_iter * i) +static struct device *next_device(struct klist_iter *i) { - struct klist_node * n = klist_next(i); + struct klist_node *n = klist_next(i); return n ? container_of(n, struct device, knode_bus) : NULL; } /** - * bus_for_each_dev - device iterator. - * @bus: bus type. - * @start: device to start iterating from. - * @data: data for the callback. - * @fn: function to be called for each device. + * bus_for_each_dev - device iterator. + * @bus: bus type. + * @start: device to start iterating from. + * @data: data for the callback. + * @fn: function to be called for each device. * - * Iterate over @bus's list of devices, and call @fn for each, - * passing it @data. If @start is not NULL, we use that device to - * begin iterating from. + * Iterate over @bus's list of devices, and call @fn for each, + * passing it @data. If @start is not NULL, we use that device to + * begin iterating from. * - * We check the return of @fn each time. If it returns anything - * other than 0, we break out and return that value. + * We check the return of @fn each time. If it returns anything + * other than 0, we break out and return that value. * - * NOTE: The device that returns a non-zero value is not retained - * in any way, nor is its refcount incremented. If the caller needs - * to retain this data, it should do, and increment the reference - * count in the supplied callback. + * NOTE: The device that returns a non-zero value is not retained + * in any way, nor is its refcount incremented. If the caller needs + * to retain this data, it should do, and increment the reference + * count in the supplied callback. */ - -int bus_for_each_dev(struct bus_type * bus, struct device * start, - void * data, int (*fn)(struct device *, void *)) +int bus_for_each_dev(struct bus_type *bus, struct device *start, + void *data, int (*fn)(struct device *, void *)) { struct klist_iter i; - struct device * dev; + struct device *dev; int error = 0; if (!bus) return -EINVAL; - klist_iter_init_node(&bus->klist_devices, &i, + klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->knode_bus : NULL)); while ((dev = next_device(&i)) && !error) error = fn(dev, data); klist_iter_exit(&i); return error; } +EXPORT_SYMBOL_GPL(bus_for_each_dev); /** * bus_find_device - device iterator for locating a particular device. @@ -323,9 +309,9 @@ int bus_for_each_dev(struct bus_type * bus, struct device * start, * if it does. If the callback returns non-zero, this function will * return to the caller and not iterate over any more devices. */ -struct device * bus_find_device(struct bus_type *bus, - struct device *start, void *data, - int (*match)(struct device *, void *)) +struct device *bus_find_device(struct bus_type *bus, + struct device *start, void *data, + int (*match)(struct device *dev, void *data)) { struct klist_iter i; struct device *dev; @@ -333,7 +319,7 @@ struct device * bus_find_device(struct bus_type *bus, if (!bus) return NULL; - klist_iter_init_node(&bus->klist_devices, &i, + klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->knode_bus : NULL)); while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) @@ -341,51 +327,83 @@ struct device * bus_find_device(struct bus_type *bus, klist_iter_exit(&i); return dev; } +EXPORT_SYMBOL_GPL(bus_find_device); + +static int match_name(struct device *dev, void *data) +{ + const char *name = data; + if (strcmp(name, dev->bus_id) == 0) + return 1; + return 0; +} -static struct device_driver * next_driver(struct klist_iter * i) +/** + * bus_find_device_by_name - device iterator for locating a particular device of a specific name + * @bus: bus type + * @start: Device to begin with + * @name: name of the device to match + * + * This is similar to the bus_find_device() function above, but it handles + * searching by a name automatically, no need to write another strcmp matching + * function. + */ +struct device *bus_find_device_by_name(struct bus_type *bus, + struct device *start, const char *name) { - struct klist_node * n = klist_next(i); - return n ? container_of(n, struct device_driver, knode_bus) : NULL; + return bus_find_device(bus, start, (void *)name, match_name); +} +EXPORT_SYMBOL_GPL(bus_find_device_by_name); + +static struct device_driver *next_driver(struct klist_iter *i) +{ + struct klist_node *n = klist_next(i); + struct driver_private *drv_priv; + + if (n) { + drv_priv = container_of(n, struct driver_private, knode_bus); + return drv_priv->driver; + } + return NULL; } /** - * bus_for_each_drv - driver iterator - * @bus: bus we're dealing with. - * @start: driver to start iterating on. - * @data: data to pass to the callback. - * @fn: function to call for each driver. + * bus_for_each_drv - driver iterator + * @bus: bus we're dealing with. + * @start: driver to start iterating on. + * @data: data to pass to the callback. + * @fn: function to call for each driver. * - * This is nearly identical to the device iterator above. - * We iterate over each driver that belongs to @bus, and call - * @fn for each. If @fn returns anything but 0, we break out - * and return it. If @start is not NULL, we use it as the head - * of the list. + * This is nearly identical to the device iterator above. + * We iterate over each driver that belongs to @bus, and call + * @fn for each. If @fn returns anything but 0, we break out + * and return it. If @start is not NULL, we use it as the head + * of the list. * - * NOTE: we don't return the driver that returns a non-zero - * value, nor do we leave the reference count incremented for that - * driver. If the caller needs to know that info, it must set it - * in the callback. It must also be sure to increment the refcount - * so it doesn't disappear before returning to the caller. + * NOTE: we don't return the driver that returns a non-zero + * value, nor do we leave the reference count incremented for that + * driver. If the caller needs to know that info, it must set it + * in the callback. It must also be sure to increment the refcount + * so it doesn't disappear before returning to the caller. */ - -int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, - void * data, int (*fn)(struct device_driver *, void *)) +int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, + void *data, int (*fn)(struct device_driver *, void *)) { struct klist_iter i; - struct device_driver * drv; + struct device_driver *drv; int error = 0; if (!bus) return -EINVAL; - klist_iter_init_node(&bus->klist_drivers, &i, - start ? &start->knode_bus : NULL); + klist_iter_init_node(&bus->p->klist_drivers, &i, + start ? &start->p->knode_bus : NULL); while ((drv = next_driver(&i)) && !error) error = fn(drv, data); klist_iter_exit(&i); return error; } +EXPORT_SYMBOL_GPL(bus_for_each_drv); static int device_add_attrs(struct bus_type *bus, struct device *dev) { @@ -396,7 +414,7 @@ static int device_add_attrs(struct bus_type *bus, struct device *dev) return 0; for (i = 0; attr_name(bus->dev_attrs[i]); i++) { - error = device_create_file(dev,&bus->dev_attrs[i]); + error = device_create_file(dev, &bus->dev_attrs[i]); if (error) { while (--i >= 0) device_remove_file(dev, &bus->dev_attrs[i]); @@ -406,13 +424,13 @@ static int device_add_attrs(struct bus_type *bus, struct device *dev) return error; } -static void device_remove_attrs(struct bus_type * bus, struct device * dev) +static void device_remove_attrs(struct bus_type *bus, struct device *dev) { int i; if (bus->dev_attrs) { for (i = 0; attr_name(bus->dev_attrs[i]); i++) - device_remove_file(dev,&bus->dev_attrs[i]); + device_remove_file(dev, &bus->dev_attrs[i]); } } @@ -420,7 +438,7 @@ static void device_remove_attrs(struct bus_type * bus, struct device * dev) static int make_deprecated_bus_links(struct device *dev) { return sysfs_create_link(&dev->kobj, - &dev->bus->subsys.kobj, "bus"); + &dev->bus->p->subsys.kobj, "bus"); } static void remove_deprecated_bus_links(struct device *dev) @@ -433,28 +451,28 @@ static inline void remove_deprecated_bus_links(struct device *dev) { } #endif /** - * bus_add_device - add device to bus - * @dev: device being added + * bus_add_device - add device to bus + * @dev: device being added * - * - Add the device to its bus's list of devices. - * - Create link to device's bus. + * - Add the device to its bus's list of devices. + * - Create link to device's bus. */ -int bus_add_device(struct device * dev) +int bus_add_device(struct device *dev) { - struct bus_type * bus = bus_get(dev->bus); + struct bus_type *bus = bus_get(dev->bus); int error = 0; if (bus) { - pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id); + pr_debug("bus: '%s': add device %s\n", bus->name, dev->bus_id); error = device_add_attrs(bus, dev); if (error) goto out_put; - error = sysfs_create_link(&bus->devices.kobj, + error = sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev->bus_id); if (error) goto out_id; error = sysfs_create_link(&dev->kobj, - &dev->bus->subsys.kobj, "subsystem"); + &dev->bus->p->subsys.kobj, "subsystem"); if (error) goto out_subsys; error = make_deprecated_bus_links(dev); @@ -466,7 +484,7 @@ int bus_add_device(struct device * dev) out_deprecated: sysfs_remove_link(&dev->kobj, "subsystem"); out_subsys: - sysfs_remove_link(&bus->devices.kobj, dev->bus_id); + sysfs_remove_link(&bus->p->devices_kset->kobj, dev->bus_id); out_id: device_remove_attrs(bus, dev); out_put: @@ -475,56 +493,58 @@ out_put: } /** - * bus_attach_device - add device to bus - * @dev: device tried to attach to a driver + * bus_attach_device - add device to bus + * @dev: device tried to attach to a driver * - * - Add device to bus's list of devices. - * - Try to attach to driver. + * - Add device to bus's list of devices. + * - Try to attach to driver. */ -void bus_attach_device(struct device * dev) +void bus_attach_device(struct device *dev) { struct bus_type *bus = dev->bus; int ret = 0; if (bus) { dev->is_registered = 1; - if (bus->drivers_autoprobe) + if (bus->p->drivers_autoprobe) ret = device_attach(dev); WARN_ON(ret < 0); if (ret >= 0) - klist_add_tail(&dev->knode_bus, &bus->klist_devices); + klist_add_tail(&dev->knode_bus, &bus->p->klist_devices); else dev->is_registered = 0; } } /** - * bus_remove_device - remove device from bus - * @dev: device to be removed + * bus_remove_device - remove device from bus + * @dev: device to be removed * - * - Remove symlink from bus's directory. - * - Delete device from bus's list. - * - Detach from its driver. - * - Drop reference taken in bus_add_device(). + * - Remove symlink from bus's directory. + * - Delete device from bus's list. + * - Detach from its driver. + * - Drop reference taken in bus_add_device(). */ -void bus_remove_device(struct device * dev) +void bus_remove_device(struct device *dev) { if (dev->bus) { sysfs_remove_link(&dev->kobj, "subsystem"); remove_deprecated_bus_links(dev); - sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id); + sysfs_remove_link(&dev->bus->p->devices_kset->kobj, + dev->bus_id); device_remove_attrs(dev->bus, dev); if (dev->is_registered) { dev->is_registered = 0; klist_del(&dev->knode_bus); } - pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id); + pr_debug("bus: '%s': remove device %s\n", + dev->bus->name, dev->bus_id); device_release_driver(dev); bus_put(dev->bus); } } -static int driver_add_attrs(struct bus_type * bus, struct device_driver * drv) +static int driver_add_attrs(struct bus_type *bus, struct device_driver *drv) { int error = 0; int i; @@ -533,19 +553,19 @@ static int driver_add_attrs(struct bus_type * bus, struct device_driver * drv) for (i = 0; attr_name(bus->drv_attrs[i]); i++) { error = driver_create_file(drv, &bus->drv_attrs[i]); if (error) - goto Err; + goto err; } } - Done: +done: return error; - Err: +err: while (--i >= 0) driver_remove_file(drv, &bus->drv_attrs[i]); - goto Done; + goto done; } - -static void driver_remove_attrs(struct bus_type * bus, struct device_driver * drv) +static void driver_remove_attrs(struct bus_type *bus, + struct device_driver *drv) { int i; @@ -616,39 +636,46 @@ static ssize_t driver_uevent_store(struct device_driver *drv, enum kobject_action action; if (kobject_action_type(buf, count, &action) == 0) - kobject_uevent(&drv->kobj, action); + kobject_uevent(&drv->p->kobj, action); return count; } static DRIVER_ATTR(uevent, S_IWUSR, NULL, driver_uevent_store); /** - * bus_add_driver - Add a driver to the bus. - * @drv: driver. - * + * bus_add_driver - Add a driver to the bus. + * @drv: driver. */ int bus_add_driver(struct device_driver *drv) { - struct bus_type * bus = bus_get(drv->bus); + struct bus_type *bus; + struct driver_private *priv; int error = 0; + bus = bus_get(drv->bus); if (!bus) return -EINVAL; - pr_debug("bus %s: add driver %s\n", bus->name, drv->name); - error = kobject_set_name(&drv->kobj, "%s", drv->name); - if (error) - goto out_put_bus; - drv->kobj.kset = &bus->drivers; - error = kobject_register(&drv->kobj); + pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + klist_init(&priv->klist_devices, NULL, NULL); + priv->driver = drv; + drv->p = priv; + priv->kobj.kset = bus->p->drivers_kset; + error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, + "%s", drv->name); if (error) goto out_put_bus; - if (drv->bus->drivers_autoprobe) { + if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; } - klist_add_tail(&drv->knode_bus, &bus->klist_drivers); + klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); module_add_driver(drv->owner, drv); error = driver_create_file(drv, &driver_attr_uevent); @@ -669,24 +696,24 @@ int bus_add_driver(struct device_driver *drv) __FUNCTION__, drv->name); } + kobject_uevent(&priv->kobj, KOBJ_ADD); return error; out_unregister: - kobject_unregister(&drv->kobj); + kobject_put(&priv->kobj); out_put_bus: bus_put(bus); return error; } /** - * bus_remove_driver - delete driver from bus's knowledge. - * @drv: driver. + * bus_remove_driver - delete driver from bus's knowledge. + * @drv: driver. * - * Detach the driver from the devices it controls, and remove - * it from its bus's list of drivers. Finally, we drop the reference - * to the bus we took in bus_add_driver(). + * Detach the driver from the devices it controls, and remove + * it from its bus's list of drivers. Finally, we drop the reference + * to the bus we took in bus_add_driver(). */ - -void bus_remove_driver(struct device_driver * drv) +void bus_remove_driver(struct device_driver *drv) { if (!drv->bus) return; @@ -694,18 +721,17 @@ void bus_remove_driver(struct device_driver * drv) remove_bind_files(drv); driver_remove_attrs(drv->bus, drv); driver_remove_file(drv, &driver_attr_uevent); - klist_remove(&drv->knode_bus); - pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name); + klist_remove(&drv->p->knode_bus); + pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name); driver_detach(drv); module_remove_driver(drv); - kobject_unregister(&drv->kobj); + kobject_put(&drv->p->kobj); bus_put(drv->bus); } - /* Helper for bus_rescan_devices's iter */ static int __must_check bus_rescan_devices_helper(struct device *dev, - void *data) + void *data) { int ret = 0; @@ -727,10 +753,11 @@ static int __must_check bus_rescan_devices_helper(struct device *dev, * attached and rescan it against existing drivers to see if it matches * any by calling device_attach() for the unbound devices. */ -int bus_rescan_devices(struct bus_type * bus) +int bus_rescan_devices(struct bus_type *bus) { return bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper); } +EXPORT_SYMBOL_GPL(bus_rescan_devices); /** * device_reprobe - remove driver for a device and probe for a new driver @@ -755,55 +782,55 @@ int device_reprobe(struct device *dev) EXPORT_SYMBOL_GPL(device_reprobe); /** - * find_bus - locate bus by name. - * @name: name of bus. + * find_bus - locate bus by name. + * @name: name of bus. * - * Call kset_find_obj() to iterate over list of buses to - * find a bus by name. Return bus if found. + * Call kset_find_obj() to iterate over list of buses to + * find a bus by name. Return bus if found. * - * Note that kset_find_obj increments bus' reference count. + * Note that kset_find_obj increments bus' reference count. */ #if 0 -struct bus_type * find_bus(char * name) +struct bus_type *find_bus(char *name) { - struct kobject * k = kset_find_obj(&bus_subsys.kset, name); + struct kobject *k = kset_find_obj(bus_kset, name); return k ? to_bus(k) : NULL; } #endif /* 0 */ /** - * bus_add_attrs - Add default attributes for this bus. - * @bus: Bus that has just been registered. + * bus_add_attrs - Add default attributes for this bus. + * @bus: Bus that has just been registered. */ -static int bus_add_attrs(struct bus_type * bus) +static int bus_add_attrs(struct bus_type *bus) { int error = 0; int i; if (bus->bus_attrs) { for (i = 0; attr_name(bus->bus_attrs[i]); i++) { - error = bus_create_file(bus,&bus->bus_attrs[i]); + error = bus_create_file(bus, &bus->bus_attrs[i]); if (error) - goto Err; + goto err; } } - Done: +done: return error; - Err: +err: while (--i >= 0) - bus_remove_file(bus,&bus->bus_attrs[i]); - goto Done; + bus_remove_file(bus, &bus->bus_attrs[i]); + goto done; } -static void bus_remove_attrs(struct bus_type * bus) +static void bus_remove_attrs(struct bus_type *bus) { int i; if (bus->bus_attrs) { for (i = 0; attr_name(bus->bus_attrs[i]); i++) - bus_remove_file(bus,&bus->bus_attrs[i]); + bus_remove_file(bus, &bus->bus_attrs[i]); } } @@ -827,32 +854,42 @@ static ssize_t bus_uevent_store(struct bus_type *bus, enum kobject_action action; if (kobject_action_type(buf, count, &action) == 0) - kobject_uevent(&bus->subsys.kobj, action); + kobject_uevent(&bus->p->subsys.kobj, action); return count; } static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store); /** - * bus_register - register a bus with the system. - * @bus: bus. + * bus_register - register a bus with the system. + * @bus: bus. * - * Once we have that, we registered the bus with the kobject - * infrastructure, then register the children subsystems it has: - * the devices and drivers that belong to the bus. + * Once we have that, we registered the bus with the kobject + * infrastructure, then register the children subsystems it has: + * the devices and drivers that belong to the bus. */ -int bus_register(struct bus_type * bus) +int bus_register(struct bus_type *bus) { int retval; + struct bus_type_private *priv; + + priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); + if (!priv) + return -ENOMEM; - BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier); + priv->bus = bus; + bus->p = priv; - retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name); + BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); + + retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); if (retval) goto out; - bus->subsys.kobj.kset = &bus_subsys; + priv->subsys.kobj.kset = bus_kset; + priv->subsys.kobj.ktype = &bus_ktype; + priv->drivers_autoprobe = 1; - retval = subsystem_register(&bus->subsys); + retval = kset_register(&priv->subsys); if (retval) goto out; @@ -860,23 +897,23 @@ int bus_register(struct bus_type * bus) if (retval) goto bus_uevent_fail; - kobject_set_name(&bus->devices.kobj, "devices"); - bus->devices.kobj.parent = &bus->subsys.kobj; - retval = kset_register(&bus->devices); - if (retval) + priv->devices_kset = kset_create_and_add("devices", NULL, + &priv->subsys.kobj); + if (!priv->devices_kset) { + retval = -ENOMEM; goto bus_devices_fail; + } - kobject_set_name(&bus->drivers.kobj, "drivers"); - bus->drivers.kobj.parent = &bus->subsys.kobj; - bus->drivers.ktype = &driver_ktype; - retval = kset_register(&bus->drivers); - if (retval) + priv->drivers_kset = kset_create_and_add("drivers", NULL, + &priv->subsys.kobj); + if (!priv->drivers_kset) { + retval = -ENOMEM; goto bus_drivers_fail; + } - klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); - klist_init(&bus->klist_drivers, NULL, NULL); + klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); + klist_init(&priv->klist_drivers, NULL, NULL); - bus->drivers_autoprobe = 1; retval = add_probe_files(bus); if (retval) goto bus_probe_files_fail; @@ -885,66 +922,73 @@ int bus_register(struct bus_type * bus) if (retval) goto bus_attrs_fail; - pr_debug("bus type '%s' registered\n", bus->name); + pr_debug("bus: '%s': registered\n", bus->name); return 0; bus_attrs_fail: remove_probe_files(bus); bus_probe_files_fail: - kset_unregister(&bus->drivers); + kset_unregister(bus->p->drivers_kset); bus_drivers_fail: - kset_unregister(&bus->devices); + kset_unregister(bus->p->devices_kset); bus_devices_fail: bus_remove_file(bus, &bus_attr_uevent); bus_uevent_fail: - subsystem_unregister(&bus->subsys); + kset_unregister(&bus->p->subsys); + kfree(bus->p); out: return retval; } +EXPORT_SYMBOL_GPL(bus_register); /** - * bus_unregister - remove a bus from the system - * @bus: bus. + * bus_unregister - remove a bus from the system + * @bus: bus. * - * Unregister the child subsystems and the bus itself. - * Finally, we call bus_put() to release the refcount + * Unregister the child subsystems and the bus itself. + * Finally, we call bus_put() to release the refcount */ -void bus_unregister(struct bus_type * bus) +void bus_unregister(struct bus_type *bus) { - pr_debug("bus %s: unregistering\n", bus->name); + pr_debug("bus: '%s': unregistering\n", bus->name); bus_remove_attrs(bus); remove_probe_files(bus); - kset_unregister(&bus->drivers); - kset_unregister(&bus->devices); + kset_unregister(bus->p->drivers_kset); + kset_unregister(bus->p->devices_kset); bus_remove_file(bus, &bus_attr_uevent); - subsystem_unregister(&bus->subsys); + kset_unregister(&bus->p->subsys); + kfree(bus->p); } +EXPORT_SYMBOL_GPL(bus_unregister); int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb) { - return blocking_notifier_chain_register(&bus->bus_notifier, nb); + return blocking_notifier_chain_register(&bus->p->bus_notifier, nb); } EXPORT_SYMBOL_GPL(bus_register_notifier); int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb) { - return blocking_notifier_chain_unregister(&bus->bus_notifier, nb); + return blocking_notifier_chain_unregister(&bus->p->bus_notifier, nb); } EXPORT_SYMBOL_GPL(bus_unregister_notifier); -int __init buses_init(void) +struct kset *bus_get_kset(struct bus_type *bus) { - return subsystem_register(&bus_subsys); + return &bus->p->subsys; } +EXPORT_SYMBOL_GPL(bus_get_kset); +struct klist *bus_get_device_klist(struct bus_type *bus) +{ + return &bus->p->klist_devices; +} +EXPORT_SYMBOL_GPL(bus_get_device_klist); -EXPORT_SYMBOL_GPL(bus_for_each_dev); -EXPORT_SYMBOL_GPL(bus_find_device); -EXPORT_SYMBOL_GPL(bus_for_each_drv); - -EXPORT_SYMBOL_GPL(bus_register); -EXPORT_SYMBOL_GPL(bus_unregister); -EXPORT_SYMBOL_GPL(bus_rescan_devices); - -EXPORT_SYMBOL_GPL(bus_create_file); -EXPORT_SYMBOL_GPL(bus_remove_file); +int __init buses_init(void) +{ + bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); + if (!bus_kset) + return -ENOMEM; + return 0; +} diff --git a/drivers/base/class.c b/drivers/base/class.c index a863bb091e1..9d915376c31 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -17,16 +17,17 @@ #include <linux/kdev_t.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/genhd.h> #include "base.h" #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) #define to_class(obj) container_of(obj, struct class, subsys.kobj) -static ssize_t -class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) +static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) { - struct class_attribute * class_attr = to_class_attr(attr); - struct class * dc = to_class(kobj); + struct class_attribute *class_attr = to_class_attr(attr); + struct class *dc = to_class(kobj); ssize_t ret = -EIO; if (class_attr->show) @@ -34,12 +35,11 @@ class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) return ret; } -static ssize_t -class_attr_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count) +static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) { - struct class_attribute * class_attr = to_class_attr(attr); - struct class * dc = to_class(kobj); + struct class_attribute *class_attr = to_class_attr(attr); + struct class *dc = to_class(kobj); ssize_t ret = -EIO; if (class_attr->store) @@ -47,7 +47,7 @@ class_attr_store(struct kobject * kobj, struct attribute * attr, return ret; } -static void class_release(struct kobject * kobj) +static void class_release(struct kobject *kobj) { struct class *class = to_class(kobj); @@ -71,20 +71,20 @@ static struct kobj_type class_ktype = { }; /* Hotplug events for classes go to the class_obj subsys */ -static decl_subsys(class, &class_ktype, NULL); +static struct kset *class_kset; -int class_create_file(struct class * cls, const struct class_attribute * attr) +int class_create_file(struct class *cls, const struct class_attribute *attr) { int error; - if (cls) { + if (cls) error = sysfs_create_file(&cls->subsys.kobj, &attr->attr); - } else + else error = -EINVAL; return error; } -void class_remove_file(struct class * cls, const struct class_attribute * attr) +void class_remove_file(struct class *cls, const struct class_attribute *attr) { if (cls) sysfs_remove_file(&cls->subsys.kobj, &attr->attr); @@ -93,48 +93,48 @@ void class_remove_file(struct class * cls, const struct class_attribute * attr) static struct class *class_get(struct class *cls) { if (cls) - return container_of(kset_get(&cls->subsys), struct class, subsys); + return container_of(kset_get(&cls->subsys), + struct class, subsys); return NULL; } -static void class_put(struct class * cls) +static void class_put(struct class *cls) { if (cls) kset_put(&cls->subsys); } - -static int add_class_attrs(struct class * cls) +static int add_class_attrs(struct class *cls) { int i; int error = 0; if (cls->class_attrs) { for (i = 0; attr_name(cls->class_attrs[i]); i++) { - error = class_create_file(cls,&cls->class_attrs[i]); + error = class_create_file(cls, &cls->class_attrs[i]); if (error) - goto Err; + goto error; } } - Done: +done: return error; - Err: +error: while (--i >= 0) - class_remove_file(cls,&cls->class_attrs[i]); - goto Done; + class_remove_file(cls, &cls->class_attrs[i]); + goto done; } -static void remove_class_attrs(struct class * cls) +static void remove_class_attrs(struct class *cls) { int i; if (cls->class_attrs) { for (i = 0; attr_name(cls->class_attrs[i]); i++) - class_remove_file(cls,&cls->class_attrs[i]); + class_remove_file(cls, &cls->class_attrs[i]); } } -int class_register(struct class * cls) +int class_register(struct class *cls) { int error; @@ -149,9 +149,16 @@ int class_register(struct class * cls) if (error) return error; - cls->subsys.kobj.kset = &class_subsys; +#if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK) + /* let the block class directory show up in the root of sysfs */ + if (cls != &block_class) + cls->subsys.kobj.kset = class_kset; +#else + cls->subsys.kobj.kset = class_kset; +#endif + cls->subsys.kobj.ktype = &class_ktype; - error = subsystem_register(&cls->subsys); + error = kset_register(&cls->subsys); if (!error) { error = add_class_attrs(class_get(cls)); class_put(cls); @@ -159,11 +166,11 @@ int class_register(struct class * cls) return error; } -void class_unregister(struct class * cls) +void class_unregister(struct class *cls) { pr_debug("device class '%s': unregistering\n", cls->name); remove_class_attrs(cls); - subsystem_unregister(&cls->subsys); + kset_unregister(&cls->subsys); } static void class_create_release(struct class *cls) @@ -241,8 +248,8 @@ void class_destroy(struct class *cls) /* Class Device Stuff */ -int class_device_create_file(struct class_device * class_dev, - const struct class_device_attribute * attr) +int class_device_create_file(struct class_device *class_dev, + const struct class_device_attribute *attr) { int error = -EINVAL; if (class_dev) @@ -250,8 +257,8 @@ int class_device_create_file(struct class_device * class_dev, return error; } -void class_device_remove_file(struct class_device * class_dev, - const struct class_device_attribute * attr) +void class_device_remove_file(struct class_device *class_dev, + const struct class_device_attribute *attr) { if (class_dev) sysfs_remove_file(&class_dev->kobj, &attr->attr); @@ -273,12 +280,11 @@ void class_device_remove_bin_file(struct class_device *class_dev, sysfs_remove_bin_file(&class_dev->kobj, attr); } -static ssize_t -class_device_attr_show(struct kobject * kobj, struct attribute * attr, - char * buf) +static ssize_t class_device_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) { - struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr); - struct class_device * cd = to_class_dev(kobj); + struct class_device_attribute *class_dev_attr = to_class_dev_attr(attr); + struct class_device *cd = to_class_dev(kobj); ssize_t ret = 0; if (class_dev_attr->show) @@ -286,12 +292,12 @@ class_device_attr_show(struct kobject * kobj, struct attribute * attr, return ret; } -static ssize_t -class_device_attr_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count) +static ssize_t class_device_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t count) { - struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr); - struct class_device * cd = to_class_dev(kobj); + struct class_device_attribute *class_dev_attr = to_class_dev_attr(attr); + struct class_device *cd = to_class_dev(kobj); ssize_t ret = 0; if (class_dev_attr->store) @@ -304,10 +310,10 @@ static struct sysfs_ops class_dev_sysfs_ops = { .store = class_device_attr_store, }; -static void class_dev_release(struct kobject * kobj) +static void class_dev_release(struct kobject *kobj) { struct class_device *cd = to_class_dev(kobj); - struct class * cls = cd->class; + struct class *cls = cd->class; pr_debug("device class '%s': release.\n", cd->class_id); @@ -316,8 +322,8 @@ static void class_dev_release(struct kobject * kobj) else if (cls->release) cls->release(cd); else { - printk(KERN_ERR "Class Device '%s' does not have a release() function, " - "it is broken and must be fixed.\n", + printk(KERN_ERR "Class Device '%s' does not have a release() " + "function, it is broken and must be fixed.\n", cd->class_id); WARN_ON(1); } @@ -428,7 +434,8 @@ static int class_uevent(struct kset *kset, struct kobject *kobj, add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name); if (dev->driver) - add_uevent_var(env, "PHYSDEVDRIVER=%s", dev->driver->name); + add_uevent_var(env, "PHYSDEVDRIVER=%s", + dev->driver->name); } if (class_dev->uevent) { @@ -452,43 +459,49 @@ static struct kset_uevent_ops class_uevent_ops = { .uevent = class_uevent, }; -static decl_subsys(class_obj, &class_device_ktype, &class_uevent_ops); - +/* + * DO NOT copy how this is created, kset_create_and_add() should be + * called, but this is a hold-over from the old-way and will be deleted + * entirely soon. + */ +static struct kset class_obj_subsys = { + .uevent_ops = &class_uevent_ops, +}; -static int class_device_add_attrs(struct class_device * cd) +static int class_device_add_attrs(struct class_device *cd) { int i; int error = 0; - struct class * cls = cd->class; + struct class *cls = cd->class; if (cls->class_dev_attrs) { for (i = 0; attr_name(cls->class_dev_attrs[i]); i++) { error = class_device_create_file(cd, - &cls->class_dev_attrs[i]); + &cls->class_dev_attrs[i]); if (error) - goto Err; + goto err; } } - Done: +done: return error; - Err: +err: while (--i >= 0) - class_device_remove_file(cd,&cls->class_dev_attrs[i]); - goto Done; + class_device_remove_file(cd, &cls->class_dev_attrs[i]); + goto done; } -static void class_device_remove_attrs(struct class_device * cd) +static void class_device_remove_attrs(struct class_device *cd) { int i; - struct class * cls = cd->class; + struct class *cls = cd->class; if (cls->class_dev_attrs) { for (i = 0; attr_name(cls->class_dev_attrs[i]); i++) - class_device_remove_file(cd,&cls->class_dev_attrs[i]); + class_device_remove_file(cd, &cls->class_dev_attrs[i]); } } -static int class_device_add_groups(struct class_device * cd) +static int class_device_add_groups(struct class_device *cd) { int i; int error = 0; @@ -498,7 +511,8 @@ static int class_device_add_groups(struct class_device * cd) error = sysfs_create_group(&cd->kobj, cd->groups[i]); if (error) { while (--i >= 0) - sysfs_remove_group(&cd->kobj, cd->groups[i]); + sysfs_remove_group(&cd->kobj, + cd->groups[i]); goto out; } } @@ -507,14 +521,12 @@ out: return error; } -static void class_device_remove_groups(struct class_device * cd) +static void class_device_remove_groups(struct class_device *cd) { int i; - if (cd->groups) { - for (i = 0; cd->groups[i]; i++) { + if (cd->groups) + for (i = 0; cd->groups[i]; i++) sysfs_remove_group(&cd->kobj, cd->groups[i]); - } - } } static ssize_t show_dev(struct class_device *class_dev, char *buf) @@ -537,8 +549,8 @@ static struct class_device_attribute class_uevent_attr = void class_device_initialize(struct class_device *class_dev) { - kobj_set_kset_s(class_dev, class_obj_subsys); - kobject_init(&class_dev->kobj); + class_dev->kobj.kset = &class_obj_subsys; + kobject_init(&class_dev->kobj, &class_device_ktype); INIT_LIST_HEAD(&class_dev->node); } @@ -566,16 +578,13 @@ int class_device_add(struct class_device *class_dev) class_dev->class_id); /* first, register with generic layer. */ - error = kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id); - if (error) - goto out2; - if (parent_class_dev) class_dev->kobj.parent = &parent_class_dev->kobj; else class_dev->kobj.parent = &parent_class->subsys.kobj; - error = kobject_add(&class_dev->kobj); + error = kobject_add(&class_dev->kobj, class_dev->kobj.parent, + "%s", class_dev->class_id); if (error) goto out2; @@ -642,7 +651,7 @@ int class_device_add(struct class_device *class_dev) out3: kobject_del(&class_dev->kobj); out2: - if(parent_class_dev) + if (parent_class_dev) class_device_put(parent_class_dev); class_put(parent_class); out1: @@ -659,9 +668,11 @@ int class_device_register(struct class_device *class_dev) /** * class_device_create - creates a class device and registers it with sysfs * @cls: pointer to the struct class that this device should be registered to. - * @parent: pointer to the parent struct class_device of this new device, if any. + * @parent: pointer to the parent struct class_device of this new device, if + * any. * @devt: the dev_t for the char device to be added. - * @device: a pointer to a struct device that is assiociated with this class device. + * @device: a pointer to a struct device that is assiociated with this class + * device. * @fmt: string for the class device's name * * This function can be used by char device classes. A struct @@ -785,7 +796,7 @@ void class_device_destroy(struct class *cls, dev_t devt) class_device_unregister(class_dev); } -struct class_device * class_device_get(struct class_device *class_dev) +struct class_device *class_device_get(struct class_device *class_dev) { if (class_dev) return to_class_dev(kobject_get(&class_dev->kobj)); @@ -798,6 +809,139 @@ void class_device_put(struct class_device *class_dev) kobject_put(&class_dev->kobj); } +/** + * class_for_each_device - device iterator + * @class: the class we're iterating + * @data: data for the callback + * @fn: function to be called for each device + * + * Iterate over @class's list of devices, and call @fn for each, + * passing it @data. + * + * We check the return of @fn each time. If it returns anything + * other than 0, we break out and return that value. + * + * Note, we hold class->sem in this function, so it can not be + * re-acquired in @fn, otherwise it will self-deadlocking. For + * example, calls to add or remove class members would be verboten. + */ +int class_for_each_device(struct class *class, void *data, + int (*fn)(struct device *, void *)) +{ + struct device *dev; + int error = 0; + + if (!class) + return -EINVAL; + down(&class->sem); + list_for_each_entry(dev, &class->devices, node) { + dev = get_device(dev); + if (dev) { + error = fn(dev, data); + put_device(dev); + } else + error = -ENODEV; + if (error) + break; + } + up(&class->sem); + + return error; +} +EXPORT_SYMBOL_GPL(class_for_each_device); + +/** + * class_find_device - device iterator for locating a particular device + * @class: the class we're iterating + * @data: data for the match function + * @match: function to check device + * + * This is similar to the class_for_each_dev() function above, but it + * returns a reference to a device that is 'found' for later use, as + * determined by the @match callback. + * + * The callback should return 0 if the device doesn't match and non-zero + * if it does. If the callback returns non-zero, this function will + * return to the caller and not iterate over any more devices. + * + * Note, you will need to drop the reference with put_device() after use. + * + * We hold class->sem in this function, so it can not be + * re-acquired in @match, otherwise it will self-deadlocking. For + * example, calls to add or remove class members would be verboten. + */ +struct device *class_find_device(struct class *class, void *data, + int (*match)(struct device *, void *)) +{ + struct device *dev; + int found = 0; + + if (!class) + return NULL; + + down(&class->sem); + list_for_each_entry(dev, &class->devices, node) { + dev = get_device(dev); + if (dev) { + if (match(dev, data)) { + found = 1; + break; + } else + put_device(dev); + } else + break; + } + up(&class->sem); + + return found ? dev : NULL; +} +EXPORT_SYMBOL_GPL(class_find_device); + +/** + * class_find_child - device iterator for locating a particular class_device + * @class: the class we're iterating + * @data: data for the match function + * @match: function to check class_device + * + * This function returns a reference to a class_device that is 'found' for + * later use, as determined by the @match callback. + * + * The callback should return 0 if the class_device doesn't match and non-zero + * if it does. If the callback returns non-zero, this function will + * return to the caller and not iterate over any more class_devices. + * + * Note, you will need to drop the reference with class_device_put() after use. + * + * We hold class->sem in this function, so it can not be + * re-acquired in @match, otherwise it will self-deadlocking. For + * example, calls to add or remove class members would be verboten. + */ +struct class_device *class_find_child(struct class *class, void *data, + int (*match)(struct class_device *, void *)) +{ + struct class_device *dev; + int found = 0; + + if (!class) + return NULL; + + down(&class->sem); + list_for_each_entry(dev, &class->children, node) { + dev = class_device_get(dev); + if (dev) { + if (match(dev, data)) { + found = 1; + break; + } else + class_device_put(dev); + } else + break; + } + up(&class->sem); + + return found ? dev : NULL; +} +EXPORT_SYMBOL_GPL(class_find_child); int class_interface_register(struct class_interface *class_intf) { @@ -829,7 +973,7 @@ int class_interface_register(struct class_interface *class_intf) void class_interface_unregister(struct class_interface *class_intf) { - struct class * parent = class_intf->class; + struct class *parent = class_intf->class; struct class_device *class_dev; struct device *dev; @@ -853,15 +997,14 @@ void class_interface_unregister(struct class_interface *class_intf) int __init classes_init(void) { - int retval; - - retval = subsystem_register(&class_subsys); - if (retval) - return retval; + class_kset = kset_create_and_add("class", NULL, NULL); + if (!class_kset) + return -ENOMEM; /* ick, this is ugly, the things we go through to keep from showing up * in sysfs... */ kset_init(&class_obj_subsys); + kobject_set_name(&class_obj_subsys.kobj, "class_obj"); if (!class_obj_subsys.kobj.parent) class_obj_subsys.kobj.parent = &class_obj_subsys.kobj; return 0; diff --git a/drivers/base/core.c b/drivers/base/core.c index c1343414d28..b1727876182 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -18,18 +18,26 @@ #include <linux/string.h> #include <linux/kdev_t.h> #include <linux/notifier.h> - +#include <linux/genhd.h> #include <asm/semaphore.h> #include "base.h" #include "power/power.h" -int (*platform_notify)(struct device * dev) = NULL; -int (*platform_notify_remove)(struct device * dev) = NULL; +int (*platform_notify)(struct device *dev) = NULL; +int (*platform_notify_remove)(struct device *dev) = NULL; -/* - * sysfs bindings for devices. - */ +#ifdef CONFIG_BLOCK +static inline int device_is_not_partition(struct device *dev) +{ + return !(dev->type == &part_type); +} +#else +static inline int device_is_not_partition(struct device *dev) +{ + return 1; +} +#endif /** * dev_driver_string - Return a device's driver name, if at all possible @@ -51,11 +59,11 @@ EXPORT_SYMBOL(dev_driver_string); #define to_dev(obj) container_of(obj, struct device, kobj) #define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) -static ssize_t -dev_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) +static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) { - struct device_attribute * dev_attr = to_dev_attr(attr); - struct device * dev = to_dev(kobj); + struct device_attribute *dev_attr = to_dev_attr(attr); + struct device *dev = to_dev(kobj); ssize_t ret = -EIO; if (dev_attr->show) @@ -63,12 +71,11 @@ dev_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) return ret; } -static ssize_t -dev_attr_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count) +static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) { - struct device_attribute * dev_attr = to_dev_attr(attr); - struct device * dev = to_dev(kobj); + struct device_attribute *dev_attr = to_dev_attr(attr); + struct device *dev = to_dev(kobj); ssize_t ret = -EIO; if (dev_attr->store) @@ -90,9 +97,9 @@ static struct sysfs_ops dev_sysfs_ops = { * reaches 0. We forward the call to the device's release * method, which should handle actually freeing the structure. */ -static void device_release(struct kobject * kobj) +static void device_release(struct kobject *kobj) { - struct device * dev = to_dev(kobj); + struct device *dev = to_dev(kobj); if (dev->release) dev->release(dev); @@ -101,8 +108,8 @@ static void device_release(struct kobject * kobj) else if (dev->class && dev->class->dev_release) dev->class->dev_release(dev); else { - printk(KERN_ERR "Device '%s' does not have a release() function, " - "it is broken and must be fixed.\n", + printk(KERN_ERR "Device '%s' does not have a release() " + "function, it is broken and must be fixed.\n", dev->bus_id); WARN_ON(1); } @@ -185,7 +192,8 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name); if (dev->driver) - add_uevent_var(env, "PHYSDEVDRIVER=%s", dev->driver->name); + add_uevent_var(env, "PHYSDEVDRIVER=%s", + dev->driver->name); } #endif @@ -193,15 +201,16 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, 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); } @@ -209,7 +218,8 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, 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); } @@ -325,7 +335,8 @@ static int device_add_groups(struct device *dev, error = sysfs_create_group(&dev->kobj, groups[i]); if (error) { while (--i >= 0) - sysfs_remove_group(&dev->kobj, groups[i]); + sysfs_remove_group(&dev->kobj, + groups[i]); break; } } @@ -401,20 +412,15 @@ static ssize_t show_dev(struct device *dev, struct device_attribute *attr, 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; /** - * device_create_file - create sysfs attribute file for device. - * @dev: device. - * @attr: device attribute descriptor. + * device_create_file - create sysfs attribute file for device. + * @dev: device. + * @attr: device attribute descriptor. */ - -int device_create_file(struct device * dev, struct device_attribute * attr) +int device_create_file(struct device *dev, struct device_attribute *attr) { int error = 0; if (get_device(dev)) { @@ -425,12 +431,11 @@ int device_create_file(struct device * dev, struct device_attribute * attr) } /** - * device_remove_file - remove sysfs attribute file. - * @dev: device. - * @attr: device attribute descriptor. + * device_remove_file - remove sysfs attribute file. + * @dev: device. + * @attr: device attribute descriptor. */ - -void device_remove_file(struct device * dev, struct device_attribute * attr) +void device_remove_file(struct device *dev, struct device_attribute *attr) { if (get_device(dev)) { sysfs_remove_file(&dev->kobj, &attr->attr); @@ -511,22 +516,20 @@ static void klist_children_put(struct klist_node *n) put_device(dev); } - /** - * device_initialize - init device structure. - * @dev: device. + * device_initialize - init device structure. + * @dev: device. * - * This prepares the device for use by other layers, - * including adding it to the device hierarchy. - * It is the first half of device_register(), if called by - * that, though it can also be called separately, so one - * may use @dev's fields (e.g. the refcount). + * This prepares the device for use by other layers, + * including adding it to the device hierarchy. + * It is the first half of device_register(), if called by + * that, though it can also be called separately, so one + * may use @dev's fields (e.g. the refcount). */ - 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); @@ -539,36 +542,39 @@ void device_initialize(struct device *dev) } #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) {} +static inline void cleanup_glue_dir(struct device *dev, + struct kobject *glue_dir) {} #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; @@ -576,8 +582,8 @@ static struct kobject * get_device_parent(struct device *dev, /* * 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); @@ -598,25 +604,45 @@ static struct kobject * get_device_parent(struct device *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_glue_dir(struct device *dev, struct kobject *glue_dir) +{ + /* see if we live in a "glue" directory */ + if (!dev->class || glue_dir->kset != &dev->class->class_dirs) + return; + + kobject_put(glue_dir); +} + +static void cleanup_device_parent(struct device *dev) +{ + cleanup_glue_dir(dev, dev->kobj.parent); +} #endif -static int setup_parent(struct device *dev, struct device *parent) +static void setup_parent(struct device *dev, struct device *parent) { struct kobject *kobj; kobj = get_device_parent(dev, parent); - if (IS_ERR(kobj)) - return PTR_ERR(kobj); if (kobj) dev->kobj.parent = kobj; - return 0; } static int device_add_class_symlinks(struct device *dev) @@ -625,65 +651,76 @@ static int device_add_class_symlinks(struct device *dev) 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 && + device_is_not_partition(dev)) { 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 && device_is_not_partition(dev)) { + 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 && device_is_not_partition(dev)) 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 && + device_is_not_partition(dev)) 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 && device_is_not_partition(dev)) { + 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: @@ -694,8 +731,9 @@ static void device_remove_class_symlinks(struct device *dev) { if (!dev->class) return; - if (dev->parent) { + #ifdef CONFIG_SYSFS_DEPRECATED + if (dev->parent && device_is_not_partition(dev)) { char *class_name; class_name = make_class_name(dev->class->name, &dev->kobj); @@ -703,45 +741,59 @@ static void device_remove_class_symlinks(struct device *dev) 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 && + device_is_not_partition(dev)) sysfs_remove_link(&dev->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); +#endif + sysfs_remove_link(&dev->kobj, "subsystem"); } /** - * device_add - add device to device hierarchy. - * @dev: device. + * device_add - add device to device hierarchy. + * @dev: device. * - * This is part 2 of device_register(), though may be called - * separately _iff_ device_initialize() has been called separately. + * This is part 2 of device_register(), though may be called + * separately _iff_ device_initialize() has been called separately. * - * This adds it to the kobject hierarchy via kobject_add(), adds it - * to the global and sibling lists for the device, then - * adds it to the other relevant subsystems of the driver model. + * This adds it to the kobject hierarchy via kobject_add(), adds it + * to the global and sibling lists for the device, then + * adds it to the other relevant subsystems of the driver model. */ int device_add(struct device *dev) { 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); - if (error) - goto Error; + setup_parent(dev, parent); /* 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; @@ -751,7 +803,7 @@ int device_add(struct device *dev) /* 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); @@ -770,9 +822,10 @@ int device_add(struct device *dev) 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; @@ -794,12 +847,14 @@ int device_add(struct device *dev) } 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: @@ -807,124 +862,84 @@ int device_add(struct device *dev) 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: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); Error: + cleanup_device_parent(dev); if (parent) put_device(parent); goto Done; } - /** - * device_register - register a device with the system. - * @dev: pointer to the device structure + * device_register - register a device with the system. + * @dev: pointer to the device structure * - * This happens in two clean steps - initialize the device - * and add it to the system. The two steps can be called - * separately, but this is the easiest and most common. - * I.e. you should only call the two helpers separately if - * have a clearly defined need to use and refcount the device - * before it is added to the hierarchy. + * This happens in two clean steps - initialize the device + * and add it to the system. The two steps can be called + * separately, but this is the easiest and most common. + * I.e. you should only call the two helpers separately if + * have a clearly defined need to use and refcount the device + * before it is added to the hierarchy. */ - int device_register(struct device *dev) { device_initialize(dev); return device_add(dev); } - /** - * get_device - increment reference count for device. - * @dev: device. + * get_device - increment reference count for device. + * @dev: device. * - * This simply forwards the call to kobject_get(), though - * we do take care to provide for the case that we get a NULL - * pointer passed in. + * This simply forwards the call to kobject_get(), though + * we do take care to provide for the case that we get a NULL + * pointer passed in. */ - -struct device * get_device(struct device * dev) +struct device *get_device(struct device *dev) { return dev ? to_dev(kobject_get(&dev->kobj)) : NULL; } - /** - * put_device - decrement reference count. - * @dev: device in question. + * put_device - decrement reference count. + * @dev: device in question. */ -void put_device(struct device * dev) +void put_device(struct device *dev) { + /* might_sleep(); */ if (dev) kobject_put(&dev->kobj); } - /** - * device_del - delete device from system. - * @dev: device. + * device_del - delete device from system. + * @dev: device. * - * This is the first part of the device unregistration - * sequence. This removes the device from the lists we control - * from here, has it removed from the other driver model - * subsystems it was added to in device_add(), and removes it - * from the kobject hierarchy. + * This is the first part of the device unregistration + * sequence. This removes the device from the lists we control + * from here, has it removed from the other driver model + * subsystems it was added to in device_add(), and removes it + * from the kobject hierarchy. * - * NOTE: this should be called manually _iff_ device_add() was - * also called manually. + * NOTE: this should be called manually _iff_ device_add() was + * also called manually. */ - -void device_del(struct device * dev) +void device_del(struct device *dev) { - struct device * parent = dev->parent; + 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 */ @@ -934,31 +949,6 @@ 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, &uevent_attr); device_remove_attrs(dev); @@ -977,57 +967,55 @@ void device_del(struct device * 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); } /** - * device_unregister - unregister device from system. - * @dev: device going away. + * device_unregister - unregister device from system. + * @dev: device going away. * - * We do this in two parts, like we do device_register(). First, - * we remove it from all the subsystems with device_del(), then - * we decrement the reference count via put_device(). If that - * is the final reference count, the device will be cleaned up - * via device_release() above. Otherwise, the structure will - * stick around until the final reference to the device is dropped. + * We do this in two parts, like we do device_register(). First, + * we remove it from all the subsystems with device_del(), then + * we decrement the reference count via put_device(). If that + * is the final reference count, the device will be cleaned up + * via device_release() above. Otherwise, the structure will + * stick around until the final reference to the device is dropped. */ -void device_unregister(struct device * dev) +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); } - -static struct device * next_device(struct klist_iter * i) +static struct device *next_device(struct klist_iter *i) { - struct klist_node * n = klist_next(i); + struct klist_node *n = klist_next(i); return n ? container_of(n, struct device, knode_parent) : NULL; } /** - * device_for_each_child - device child iterator. - * @parent: parent struct device. - * @data: data for the callback. - * @fn: function to be called for each device. + * device_for_each_child - device child iterator. + * @parent: parent struct device. + * @data: data for the callback. + * @fn: function to be called for each device. * - * Iterate over @parent's child devices, and call @fn for each, - * passing it @data. + * Iterate over @parent's child devices, and call @fn for each, + * passing it @data. * - * We check the return of @fn each time. If it returns anything - * other than 0, we break out and return that value. + * We check the return of @fn each time. If it returns anything + * other than 0, we break out and return that value. */ -int device_for_each_child(struct device * parent, void * data, - int (*fn)(struct device *, void *)) +int device_for_each_child(struct device *parent, void *data, + int (*fn)(struct device *dev, void *data)) { struct klist_iter i; - struct device * child; + struct device *child; int error = 0; klist_iter_init(&parent->klist_children, &i); @@ -1052,8 +1040,8 @@ int device_for_each_child(struct device * parent, void * data, * current device can be obtained, this function will return to the caller * and not iterate over any more devices. */ -struct device * device_find_child(struct device *parent, void *data, - int (*match)(struct device *, void *)) +struct device *device_find_child(struct device *parent, void *data, + int (*match)(struct device *dev, void *data)) { struct klist_iter i; struct device *child; @@ -1071,7 +1059,10 @@ struct device * device_find_child(struct device *parent, void *data, 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); @@ -1092,7 +1083,7 @@ EXPORT_SYMBOL_GPL(device_remove_file); 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); } @@ -1154,14 +1145,11 @@ error: 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; @@ -1174,12 +1162,54 @@ void device_destroy(struct class *class, dev_t devt) } } 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 @@ -1196,7 +1226,8 @@ int device_rename(struct device *dev, char *new_name) 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)) @@ -1228,18 +1259,18 @@ int device_rename(struct device *dev, char *new_name) 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); @@ -1277,8 +1308,7 @@ static int device_move_class_links(struct device *dev, class_name); if (error) sysfs_remove_link(&dev->kobj, "device"); - } - else + } else error = 0; out: kfree(class_name); @@ -1309,16 +1339,13 @@ int device_move(struct device *dev, struct device *new_parent) return -EINVAL; new_parent = get_device(new_parent); - new_parent_kobj = get_device_parent (dev, new_parent); - if (IS_ERR(new_parent_kobj)) { - error = PTR_ERR(new_parent_kobj); - put_device(new_parent); - goto out; - } - pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id, - new_parent ? new_parent->bus_id : "<NULL>"); + new_parent_kobj = get_device_parent(dev, new_parent); + + 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) { + cleanup_glue_dir(dev, new_parent_kobj); put_device(new_parent); goto out; } @@ -1341,6 +1368,7 @@ int device_move(struct device *dev, struct device *new_parent) klist_add_tail(&dev->knode_parent, &old_parent->klist_children); } + cleanup_glue_dir(dev, new_parent_kobj); put_device(new_parent); goto out; } @@ -1350,5 +1378,23 @@ out: 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); + } + } +} diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 40545071e3c..c5885f5ce0a 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -14,7 +14,7 @@ #include "base.h" struct sysdev_class cpu_sysdev_class = { - set_kset_name("cpu"), + .name = "cpu", }; EXPORT_SYMBOL(cpu_sysdev_class); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 7ac474db88c..a5cde94bb98 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -1,18 +1,20 @@ /* - * drivers/base/dd.c - The core device/driver interactions. + * drivers/base/dd.c - The core device/driver interactions. * - * This file contains the (sometimes tricky) code that controls the - * interactions between devices and drivers, which primarily includes - * driver binding and unbinding. + * This file contains the (sometimes tricky) code that controls the + * interactions between devices and drivers, which primarily includes + * driver binding and unbinding. * - * All of this code used to exist in drivers/base/bus.c, but was - * relocated to here in the name of compartmentalization (since it wasn't - * strictly code just for the 'struct bus_type'. + * All of this code used to exist in drivers/base/bus.c, but was + * relocated to here in the name of compartmentalization (since it wasn't + * strictly code just for the 'struct bus_type'. * - * Copyright (c) 2002-5 Patrick Mochel - * Copyright (c) 2002-3 Open Source Development Labs + * Copyright (c) 2002-5 Patrick Mochel + * Copyright (c) 2002-3 Open Source Development Labs + * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de> + * Copyright (c) 2007 Novell Inc. * - * This file is released under the GPLv2 + * This file is released under the GPLv2 */ #include <linux/device.h> @@ -23,8 +25,6 @@ #include "base.h" #include "power/power.h" -#define to_drv(node) container_of(node, struct device_driver, kobj.entry) - static void driver_bound(struct device *dev) { @@ -34,27 +34,27 @@ static void driver_bound(struct device *dev) return; } - pr_debug("bound device '%s' to driver '%s'\n", - dev->bus_id, dev->driver->name); + pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->bus_id, + __FUNCTION__, dev->driver->name); if (dev->bus) - blocking_notifier_call_chain(&dev->bus->bus_notifier, + blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_BOUND_DRIVER, dev); - klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); + klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices); } static int driver_sysfs_add(struct device *dev) { int ret; - ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj, + ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj, kobject_name(&dev->kobj)); if (ret == 0) { - ret = sysfs_create_link(&dev->kobj, &dev->driver->kobj, + ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj, "driver"); if (ret) - sysfs_remove_link(&dev->driver->kobj, + sysfs_remove_link(&dev->driver->p->kobj, kobject_name(&dev->kobj)); } return ret; @@ -65,24 +65,24 @@ static void driver_sysfs_remove(struct device *dev) struct device_driver *drv = dev->driver; if (drv) { - sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + sysfs_remove_link(&drv->p->kobj, kobject_name(&dev->kobj)); sysfs_remove_link(&dev->kobj, "driver"); } } /** - * device_bind_driver - bind a driver to one device. - * @dev: device. + * device_bind_driver - bind a driver to one device. + * @dev: device. * - * Allow manual attachment of a driver to a device. - * Caller must have already set @dev->driver. + * Allow manual attachment of a driver to a device. + * Caller must have already set @dev->driver. * - * Note that this does not modify the bus reference count - * nor take the bus's rwsem. Please verify those are accounted - * for before calling this. (It is ok to call with no other effort - * from a driver's probe() method.) + * Note that this does not modify the bus reference count + * nor take the bus's rwsem. Please verify those are accounted + * for before calling this. (It is ok to call with no other effort + * from a driver's probe() method.) * - * This function must be called with @dev->sem held. + * This function must be called with @dev->sem held. */ int device_bind_driver(struct device *dev) { @@ -93,6 +93,7 @@ int device_bind_driver(struct device *dev) driver_bound(dev); return ret; } +EXPORT_SYMBOL_GPL(device_bind_driver); static atomic_t probe_count = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue); @@ -102,8 +103,8 @@ static int really_probe(struct device *dev, struct device_driver *drv) int ret = 0; atomic_inc(&probe_count); - pr_debug("%s: Probing driver %s with device %s\n", - drv->bus->name, drv->name, dev->bus_id); + pr_debug("bus: '%s': %s: probing driver %s with device %s\n", + drv->bus->name, __FUNCTION__, drv->name, dev->bus_id); WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; @@ -125,8 +126,8 @@ static int really_probe(struct device *dev, struct device_driver *drv) driver_bound(dev); ret = 1; - pr_debug("%s: Bound Device %s to Driver %s\n", - drv->bus->name, dev->bus_id, drv->name); + pr_debug("bus: '%s': %s: bound device %s to driver %s\n", + drv->bus->name, __FUNCTION__, dev->bus_id, drv->name); goto done; probe_failed: @@ -183,7 +184,7 @@ int driver_probe_done(void) * This function must be called with @dev->sem held. When called for a * USB interface, @dev->parent->sem must be held as well. */ -int driver_probe_device(struct device_driver * drv, struct device * dev) +int driver_probe_device(struct device_driver *drv, struct device *dev) { int ret = 0; @@ -192,8 +193,8 @@ int driver_probe_device(struct device_driver * drv, struct device * dev) if (drv->bus->match && !drv->bus->match(dev, drv)) goto done; - pr_debug("%s: Matched Device %s with Driver %s\n", - drv->bus->name, dev->bus_id, drv->name); + pr_debug("bus: '%s': %s: matched device %s with driver %s\n", + drv->bus->name, __FUNCTION__, dev->bus_id, drv->name); ret = really_probe(dev, drv); @@ -201,27 +202,27 @@ done: return ret; } -static int __device_attach(struct device_driver * drv, void * data) +static int __device_attach(struct device_driver *drv, void *data) { - struct device * dev = data; + struct device *dev = data; return driver_probe_device(drv, dev); } /** - * device_attach - try to attach device to a driver. - * @dev: device. + * device_attach - try to attach device to a driver. + * @dev: device. * - * Walk the list of drivers that the bus has and call - * driver_probe_device() for each pair. If a compatible - * pair is found, break out and return. + * Walk the list of drivers that the bus has and call + * driver_probe_device() for each pair. If a compatible + * pair is found, break out and return. * - * Returns 1 if the device was bound to a driver; - * 0 if no matching device was found; - * -ENODEV if the device is not registered. + * Returns 1 if the device was bound to a driver; + * 0 if no matching device was found; + * -ENODEV if the device is not registered. * - * When called for a USB interface, @dev->parent->sem must be held. + * When called for a USB interface, @dev->parent->sem must be held. */ -int device_attach(struct device * dev) +int device_attach(struct device *dev) { int ret = 0; @@ -240,10 +241,11 @@ int device_attach(struct device * dev) up(&dev->sem); return ret; } +EXPORT_SYMBOL_GPL(device_attach); -static int __driver_attach(struct device * dev, void * data) +static int __driver_attach(struct device *dev, void *data) { - struct device_driver * drv = data; + struct device_driver *drv = data; /* * Lock device and try to bind to it. We drop the error @@ -268,35 +270,35 @@ static int __driver_attach(struct device * dev, void * data) } /** - * driver_attach - try to bind driver to devices. - * @drv: driver. + * driver_attach - try to bind driver to devices. + * @drv: driver. * - * Walk the list of devices that the bus has on it and try to - * match the driver with each one. If driver_probe_device() - * returns 0 and the @dev->driver is set, we've found a - * compatible pair. + * Walk the list of devices that the bus has on it and try to + * match the driver with each one. If driver_probe_device() + * returns 0 and the @dev->driver is set, we've found a + * compatible pair. */ -int driver_attach(struct device_driver * drv) +int driver_attach(struct device_driver *drv) { return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); } +EXPORT_SYMBOL_GPL(driver_attach); /* - * __device_release_driver() must be called with @dev->sem held. - * When called for a USB interface, @dev->parent->sem must be held as well. + * __device_release_driver() must be called with @dev->sem held. + * When called for a USB interface, @dev->parent->sem must be held as well. */ -static void __device_release_driver(struct device * dev) +static void __device_release_driver(struct device *dev) { - struct device_driver * drv; + struct device_driver *drv; - drv = get_driver(dev->driver); + drv = dev->driver; if (drv) { driver_sysfs_remove(dev); sysfs_remove_link(&dev->kobj, "driver"); - klist_remove(&dev->knode_driver); if (dev->bus) - blocking_notifier_call_chain(&dev->bus->bus_notifier, + blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_UNBIND_DRIVER, dev); @@ -306,18 +308,18 @@ static void __device_release_driver(struct device * dev) drv->remove(dev); devres_release_all(dev); dev->driver = NULL; - put_driver(drv); + klist_remove(&dev->knode_driver); } } /** - * device_release_driver - manually detach device from driver. - * @dev: device. + * device_release_driver - manually detach device from driver. + * @dev: device. * - * Manually detach device from driver. - * When called for a USB interface, @dev->parent->sem must be held. + * Manually detach device from driver. + * When called for a USB interface, @dev->parent->sem must be held. */ -void device_release_driver(struct device * dev) +void device_release_driver(struct device *dev) { /* * If anyone calls device_release_driver() recursively from @@ -328,26 +330,26 @@ void device_release_driver(struct device * dev) __device_release_driver(dev); up(&dev->sem); } - +EXPORT_SYMBOL_GPL(device_release_driver); /** * driver_detach - detach driver from all devices it controls. * @drv: driver. */ -void driver_detach(struct device_driver * drv) +void driver_detach(struct device_driver *drv) { - struct device * dev; + struct device *dev; for (;;) { - spin_lock(&drv->klist_devices.k_lock); - if (list_empty(&drv->klist_devices.k_list)) { - spin_unlock(&drv->klist_devices.k_lock); + spin_lock(&drv->p->klist_devices.k_lock); + if (list_empty(&drv->p->klist_devices.k_list)) { + spin_unlock(&drv->p->klist_devices.k_lock); break; } - dev = list_entry(drv->klist_devices.k_list.prev, + dev = list_entry(drv->p->klist_devices.k_list.prev, struct device, knode_driver.n_node); get_device(dev); - spin_unlock(&drv->klist_devices.k_lock); + spin_unlock(&drv->p->klist_devices.k_lock); if (dev->parent) /* Needed for USB */ down(&dev->parent->sem); @@ -360,9 +362,3 @@ void driver_detach(struct device_driver * drv) put_device(dev); } } - -EXPORT_SYMBOL_GPL(device_bind_driver); -EXPORT_SYMBOL_GPL(device_release_driver); -EXPORT_SYMBOL_GPL(device_attach); -EXPORT_SYMBOL_GPL(driver_attach); - diff --git a/drivers/base/dmapool.c b/drivers/base/dmapool.c index 5beddc322e6..b5034dc72a0 100644 --- a/drivers/base/dmapool.c +++ b/drivers/base/dmapool.c @@ -2,7 +2,6 @@ #include <linux/device.h> #include <linux/mm.h> #include <asm/io.h> /* Needed for i386 to build */ -#include <asm/scatterlist.h> /* Needed for i386 to build */ #include <linux/dma-mapping.h> #include <linux/dmapool.h> #include <linux/slab.h> diff --git a/drivers/base/driver.c b/drivers/base/driver.c index eb11475293e..a35f04121a0 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -3,6 +3,8 @@ * * Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Open Source Development Labs + * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de> + * Copyright (c) 2007 Novell Inc. * * This file is released under the GPLv2 * @@ -15,46 +17,42 @@ #include "base.h" #define to_dev(node) container_of(node, struct device, driver_list) -#define to_drv(obj) container_of(obj, struct device_driver, kobj) -static struct device * next_device(struct klist_iter * i) +static struct device *next_device(struct klist_iter *i) { - struct klist_node * n = klist_next(i); + struct klist_node *n = klist_next(i); return n ? container_of(n, struct device, knode_driver) : NULL; } /** - * driver_for_each_device - Iterator for devices bound to a driver. - * @drv: Driver we're iterating. - * @start: Device to begin with - * @data: Data to pass to the callback. - * @fn: Function to call for each device. + * driver_for_each_device - Iterator for devices bound to a driver. + * @drv: Driver we're iterating. + * @start: Device to begin with + * @data: Data to pass to the callback. + * @fn: Function to call for each device. * - * Iterate over the @drv's list of devices calling @fn for each one. + * Iterate over the @drv's list of devices calling @fn for each one. */ - -int driver_for_each_device(struct device_driver * drv, struct device * start, - void * data, int (*fn)(struct device *, void *)) +int driver_for_each_device(struct device_driver *drv, struct device *start, + void *data, int (*fn)(struct device *, void *)) { struct klist_iter i; - struct device * dev; + struct device *dev; int error = 0; if (!drv) return -EINVAL; - klist_iter_init_node(&drv->klist_devices, &i, + klist_iter_init_node(&drv->p->klist_devices, &i, start ? &start->knode_driver : NULL); while ((dev = next_device(&i)) && !error) error = fn(dev, data); klist_iter_exit(&i); return error; } - EXPORT_SYMBOL_GPL(driver_for_each_device); - /** * driver_find_device - device iterator for locating a particular device. * @drv: The device's driver @@ -70,9 +68,9 @@ EXPORT_SYMBOL_GPL(driver_for_each_device); * if it does. If the callback returns non-zero, this function will * return to the caller and not iterate over any more devices. */ -struct device * driver_find_device(struct device_driver *drv, - struct device * start, void * data, - int (*match)(struct device *, void *)) +struct device *driver_find_device(struct device_driver *drv, + struct device *start, void *data, + int (*match)(struct device *dev, void *data)) { struct klist_iter i; struct device *dev; @@ -80,7 +78,7 @@ struct device * driver_find_device(struct device_driver *drv, if (!drv) return NULL; - klist_iter_init_node(&drv->klist_devices, &i, + klist_iter_init_node(&drv->p->klist_devices, &i, (start ? &start->knode_driver : NULL)); while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) @@ -91,111 +89,179 @@ struct device * driver_find_device(struct device_driver *drv, EXPORT_SYMBOL_GPL(driver_find_device); /** - * driver_create_file - create sysfs file for driver. - * @drv: driver. - * @attr: driver attribute descriptor. + * driver_create_file - create sysfs file for driver. + * @drv: driver. + * @attr: driver attribute descriptor. */ - -int driver_create_file(struct device_driver * drv, struct driver_attribute * attr) +int driver_create_file(struct device_driver *drv, + struct driver_attribute *attr) { int error; if (get_driver(drv)) { - error = sysfs_create_file(&drv->kobj, &attr->attr); + error = sysfs_create_file(&drv->p->kobj, &attr->attr); put_driver(drv); } else error = -EINVAL; return error; } - +EXPORT_SYMBOL_GPL(driver_create_file); /** - * driver_remove_file - remove sysfs file for driver. - * @drv: driver. - * @attr: driver attribute descriptor. + * driver_remove_file - remove sysfs file for driver. + * @drv: driver. + * @attr: driver attribute descriptor. */ - -void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr) +void driver_remove_file(struct device_driver *drv, + struct driver_attribute *attr) { if (get_driver(drv)) { - sysfs_remove_file(&drv->kobj, &attr->attr); + sysfs_remove_file(&drv->p->kobj, &attr->attr); put_driver(drv); } } - +EXPORT_SYMBOL_GPL(driver_remove_file); /** - * get_driver - increment driver reference count. - * @drv: driver. + * driver_add_kobj - add a kobject below the specified driver + * + * You really don't want to do this, this is only here due to one looney + * iseries driver, go poke those developers if you are annoyed about + * this... */ -struct device_driver * get_driver(struct device_driver * drv) +int driver_add_kobj(struct device_driver *drv, struct kobject *kobj, + const char *fmt, ...) { - return drv ? to_drv(kobject_get(&drv->kobj)) : NULL; + va_list args; + char *name; + + va_start(args, fmt); + name = kvasprintf(GFP_KERNEL, fmt, args); + va_end(args); + + if (!name) + return -ENOMEM; + + return kobject_add(kobj, &drv->p->kobj, "%s", name); } +EXPORT_SYMBOL_GPL(driver_add_kobj); + +/** + * get_driver - increment driver reference count. + * @drv: driver. + */ +struct device_driver *get_driver(struct device_driver *drv) +{ + if (drv) { + struct driver_private *priv; + struct kobject *kobj; + kobj = kobject_get(&drv->p->kobj); + priv = to_driver(kobj); + return priv->driver; + } + return NULL; +} +EXPORT_SYMBOL_GPL(get_driver); /** - * put_driver - decrement driver's refcount. - * @drv: driver. + * put_driver - decrement driver's refcount. + * @drv: driver. */ -void put_driver(struct device_driver * drv) +void put_driver(struct device_driver *drv) +{ + kobject_put(&drv->p->kobj); +} +EXPORT_SYMBOL_GPL(put_driver); + +static int driver_add_groups(struct device_driver *drv, + struct attribute_group **groups) { - kobject_put(&drv->kobj); + int error = 0; + int i; + + if (groups) { + for (i = 0; groups[i]; i++) { + error = sysfs_create_group(&drv->p->kobj, groups[i]); + if (error) { + while (--i >= 0) + sysfs_remove_group(&drv->p->kobj, + groups[i]); + break; + } + } + } + return error; +} + +static void driver_remove_groups(struct device_driver *drv, + struct attribute_group **groups) +{ + int i; + + if (groups) + for (i = 0; groups[i]; i++) + sysfs_remove_group(&drv->p->kobj, groups[i]); } /** - * driver_register - register driver with bus - * @drv: driver to register + * driver_register - register driver with bus + * @drv: driver to register * - * We pass off most of the work to the bus_add_driver() call, - * since most of the things we have to do deal with the bus - * structures. + * We pass off most of the work to the bus_add_driver() call, + * since most of the things we have to do deal with the bus + * structures. */ -int driver_register(struct device_driver * drv) +int driver_register(struct device_driver *drv) { + int ret; + if ((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || - (drv->bus->shutdown && drv->shutdown)) { - printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); - } - klist_init(&drv->klist_devices, NULL, NULL); - return bus_add_driver(drv); + (drv->bus->shutdown && drv->shutdown)) + printk(KERN_WARNING "Driver '%s' needs updating - please use " + "bus_type methods\n", drv->name); + ret = bus_add_driver(drv); + if (ret) + return ret; + ret = driver_add_groups(drv, drv->groups); + if (ret) + bus_remove_driver(drv); + return ret; } +EXPORT_SYMBOL_GPL(driver_register); /** - * driver_unregister - remove driver from system. - * @drv: driver. + * driver_unregister - remove driver from system. + * @drv: driver. * - * Again, we pass off most of the work to the bus-level call. + * Again, we pass off most of the work to the bus-level call. */ - -void driver_unregister(struct device_driver * drv) +void driver_unregister(struct device_driver *drv) { + driver_remove_groups(drv, drv->groups); bus_remove_driver(drv); } +EXPORT_SYMBOL_GPL(driver_unregister); /** - * driver_find - locate driver on a bus by its name. - * @name: name of the driver. - * @bus: bus to scan for the driver. + * driver_find - locate driver on a bus by its name. + * @name: name of the driver. + * @bus: bus to scan for the driver. * - * Call kset_find_obj() to iterate over list of drivers on - * a bus to find driver by name. Return driver if found. + * Call kset_find_obj() to iterate over list of drivers on + * a bus to find driver by name. Return driver if found. * - * Note that kset_find_obj increments driver's reference count. + * Note that kset_find_obj increments driver's reference count. */ struct device_driver *driver_find(const char *name, struct bus_type *bus) { - struct kobject *k = kset_find_obj(&bus->drivers, name); - if (k) - return to_drv(k); + struct kobject *k = kset_find_obj(bus->p->drivers_kset, name); + struct driver_private *priv; + + if (k) { + priv = to_driver(k); + return priv->driver; + } return NULL; } - -EXPORT_SYMBOL_GPL(driver_register); -EXPORT_SYMBOL_GPL(driver_unregister); -EXPORT_SYMBOL_GPL(get_driver); -EXPORT_SYMBOL_GPL(put_driver); EXPORT_SYMBOL_GPL(driver_find); - -EXPORT_SYMBOL_GPL(driver_create_file); -EXPORT_SYMBOL_GPL(driver_remove_file); diff --git a/drivers/base/firmware.c b/drivers/base/firmware.c index 90c86293216..11381555680 100644 --- a/drivers/base/firmware.c +++ b/drivers/base/firmware.c @@ -3,11 +3,11 @@ * * Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Open Source Development Labs + * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de> + * Copyright (c) 2007 Novell Inc. * * This file is released under the GPLv2 - * */ - #include <linux/kobject.h> #include <linux/module.h> #include <linux/init.h> @@ -15,23 +15,13 @@ #include "base.h" -static decl_subsys(firmware, NULL, NULL); - -int firmware_register(struct kset *s) -{ - kobj_set_kset_s(s, firmware_subsys); - return subsystem_register(s); -} - -void firmware_unregister(struct kset *s) -{ - subsystem_unregister(s); -} +struct kobject *firmware_kobj; +EXPORT_SYMBOL_GPL(firmware_kobj); int __init firmware_init(void) { - return subsystem_register(&firmware_subsys); + firmware_kobj = kobject_create_and_add("firmware", NULL); + if (!firmware_kobj) + return -ENOMEM; + return 0; } - -EXPORT_SYMBOL_GPL(firmware_register); -EXPORT_SYMBOL_GPL(firmware_unregister); diff --git a/drivers/base/hypervisor.c b/drivers/base/hypervisor.c index 7080b413ddc..6428cba3aad 100644 --- a/drivers/base/hypervisor.c +++ b/drivers/base/hypervisor.c @@ -2,19 +2,23 @@ * hypervisor.c - /sys/hypervisor subsystem. * * Copyright (C) IBM Corp. 2006 + * Copyright (C) 2007 Greg Kroah-Hartman <gregkh@suse.de> + * Copyright (C) 2007 Novell Inc. * * This file is released under the GPLv2 */ #include <linux/kobject.h> #include <linux/device.h> - #include "base.h" -decl_subsys(hypervisor, NULL, NULL); -EXPORT_SYMBOL_GPL(hypervisor_subsys); +struct kobject *hypervisor_kobj; +EXPORT_SYMBOL_GPL(hypervisor_kobj); int __init hypervisor_init(void) { - return subsystem_register(&hypervisor_subsys); + hypervisor_kobj = kobject_create_and_add("hypervisor", NULL); + if (!hypervisor_kobj) + return -ENOMEM; + return 0; } diff --git a/drivers/base/init.c b/drivers/base/init.c index 37138154f9e..7bd9b6a5b01 100644 --- a/drivers/base/init.c +++ b/drivers/base/init.c @@ -1,10 +1,8 @@ /* - * * Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Open Source Development Labs * * This file is released under the GPLv2 - * */ #include <linux/device.h> @@ -14,12 +12,11 @@ #include "base.h" /** - * driver_init - initialize driver model. + * driver_init - initialize driver model. * - * Call the driver model init functions to initialize their - * subsystems. Called early from init/main.c. + * Call the driver model init functions to initialize their + * subsystems. Called early from init/main.c. */ - void __init driver_init(void) { /* These are the core pieces */ @@ -36,5 +33,4 @@ void __init driver_init(void) system_bus_init(); cpu_dev_init(); memory_dev_init(); - attribute_container_init(); } diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 7868707c7ed..7ae413fdd5f 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -26,7 +26,7 @@ #define MEMORY_CLASS_NAME "memory" static struct sysdev_class memory_sysdev_class = { - set_kset_name(MEMORY_CLASS_NAME), + .name = MEMORY_CLASS_NAME, }; static const char *memory_uevent_name(struct kset *kset, struct kobject *kobj) diff --git a/drivers/base/module.c b/drivers/base/module.c new file mode 100644 index 00000000000..103be9cacb0 --- /dev/null +++ b/drivers/base/module.c @@ -0,0 +1,94 @@ +/* + * module.c - module sysfs fun for drivers + * + * This file is released under the GPLv2 + * + */ +#include <linux/device.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/string.h> +#include "base.h" + +static char *make_driver_name(struct device_driver *drv) +{ + char *driver_name; + + driver_name = kmalloc(strlen(drv->name) + strlen(drv->bus->name) + 2, + GFP_KERNEL); + if (!driver_name) + return NULL; + + sprintf(driver_name, "%s:%s", drv->bus->name, drv->name); + return driver_name; +} + +static void module_create_drivers_dir(struct module_kobject *mk) +{ + if (!mk || mk->drivers_dir) + return; + + mk->drivers_dir = kobject_create_and_add("drivers", &mk->kobj); +} + +void module_add_driver(struct module *mod, struct device_driver *drv) +{ + char *driver_name; + int no_warn; + struct module_kobject *mk = NULL; + + if (!drv) + return; + + if (mod) + mk = &mod->mkobj; + else if (drv->mod_name) { + struct kobject *mkobj; + + /* Lookup built-in module entry in /sys/modules */ + mkobj = kset_find_obj(module_kset, drv->mod_name); + if (mkobj) { + mk = container_of(mkobj, struct module_kobject, kobj); + /* remember our module structure */ + drv->p->mkobj = mk; + /* kset_find_obj took a reference */ + kobject_put(mkobj); + } + } + + if (!mk) + return; + + /* Don't check return codes; these calls are idempotent */ + no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module"); + driver_name = make_driver_name(drv); + if (driver_name) { + module_create_drivers_dir(mk); + no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj, + driver_name); + kfree(driver_name); + } +} + +void module_remove_driver(struct device_driver *drv) +{ + struct module_kobject *mk = NULL; + char *driver_name; + + if (!drv) + return; + + sysfs_remove_link(&drv->p->kobj, "module"); + + if (drv->owner) + mk = &drv->owner->mkobj; + else if (drv->p->mkobj) + mk = drv->p->mkobj; + if (mk && mk->drivers_dir) { + driver_name = make_driver_name(drv); + if (driver_name) { + sysfs_remove_link(mk->drivers_dir, driver_name); + kfree(driver_name); + } + } +} diff --git a/drivers/base/node.c b/drivers/base/node.c index 88eeed72b5d..e59861f18ce 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -15,7 +15,7 @@ #include <linux/device.h> static struct sysdev_class node_class = { - set_kset_name("node"), + .name = "node", }; diff --git a/drivers/base/platform.c b/drivers/base/platform.c index fb560924148..efaf282c438 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -20,7 +20,8 @@ #include "base.h" -#define to_platform_driver(drv) (container_of((drv), struct platform_driver, driver)) +#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \ + driver)) struct device platform_bus = { .bus_id = "platform", @@ -28,14 +29,13 @@ struct device platform_bus = { EXPORT_SYMBOL_GPL(platform_bus); /** - * platform_get_resource - get a resource for a device - * @dev: platform device - * @type: resource type - * @num: resource index + * platform_get_resource - get a resource for a device + * @dev: platform device + * @type: resource type + * @num: resource index */ -struct resource * -platform_get_resource(struct platform_device *dev, unsigned int type, - unsigned int num) +struct resource *platform_get_resource(struct platform_device *dev, + unsigned int type, unsigned int num) { int i; @@ -43,8 +43,7 @@ platform_get_resource(struct platform_device *dev, unsigned int type, struct resource *r = &dev->resource[i]; if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| - IORESOURCE_IRQ|IORESOURCE_DMA)) - == type) + IORESOURCE_IRQ|IORESOURCE_DMA)) == type) if (num-- == 0) return r; } @@ -53,9 +52,9 @@ platform_get_resource(struct platform_device *dev, unsigned int type, EXPORT_SYMBOL_GPL(platform_get_resource); /** - * platform_get_irq - get an IRQ for a device - * @dev: platform device - * @num: IRQ number index + * platform_get_irq - get an IRQ for a device + * @dev: platform device + * @num: IRQ number index */ int platform_get_irq(struct platform_device *dev, unsigned int num) { @@ -66,14 +65,13 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) EXPORT_SYMBOL_GPL(platform_get_irq); /** - * platform_get_resource_byname - get a resource for a device by name - * @dev: platform device - * @type: resource type - * @name: resource name + * platform_get_resource_byname - get a resource for a device by name + * @dev: platform device + * @type: resource type + * @name: resource name */ -struct resource * -platform_get_resource_byname(struct platform_device *dev, unsigned int type, - char *name) +struct resource *platform_get_resource_byname(struct platform_device *dev, + unsigned int type, char *name) { int i; @@ -90,22 +88,23 @@ platform_get_resource_byname(struct platform_device *dev, unsigned int type, EXPORT_SYMBOL_GPL(platform_get_resource_byname); /** - * platform_get_irq - get an IRQ for a device - * @dev: platform device - * @name: IRQ name + * platform_get_irq - get an IRQ for a device + * @dev: platform device + * @name: IRQ name */ int platform_get_irq_byname(struct platform_device *dev, char *name) { - struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); + struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ, + name); return r ? r->start : -ENXIO; } EXPORT_SYMBOL_GPL(platform_get_irq_byname); /** - * platform_add_devices - add a numbers of platform devices - * @devs: array of platform devices to add - * @num: number of platform devices in array + * platform_add_devices - add a numbers of platform devices + * @devs: array of platform devices to add + * @num: number of platform devices in array */ int platform_add_devices(struct platform_device **devs, int num) { @@ -130,12 +129,11 @@ struct platform_object { }; /** - * platform_device_put - * @pdev: platform device to free + * platform_device_put + * @pdev: platform device to free * - * Free all memory associated with a platform device. This function - * must _only_ be externally called in error cases. All other usage - * is a bug. + * Free all memory associated with a platform device. This function must + * _only_ be externally called in error cases. All other usage is a bug. */ void platform_device_put(struct platform_device *pdev) { @@ -146,7 +144,8 @@ EXPORT_SYMBOL_GPL(platform_device_put); static void platform_device_release(struct device *dev) { - struct platform_object *pa = container_of(dev, struct platform_object, pdev.dev); + struct platform_object *pa = container_of(dev, struct platform_object, + pdev.dev); kfree(pa->pdev.dev.platform_data); kfree(pa->pdev.resource); @@ -154,12 +153,12 @@ static void platform_device_release(struct device *dev) } /** - * platform_device_alloc - * @name: base name of the device we're adding - * @id: instance id + * platform_device_alloc + * @name: base name of the device we're adding + * @id: instance id * - * Create a platform device object which can have other objects attached - * to it, and which will have attached objects freed when it is released. + * Create a platform device object which can have other objects attached + * to it, and which will have attached objects freed when it is released. */ struct platform_device *platform_device_alloc(const char *name, int id) { @@ -179,16 +178,17 @@ struct platform_device *platform_device_alloc(const char *name, int id) EXPORT_SYMBOL_GPL(platform_device_alloc); /** - * platform_device_add_resources - * @pdev: platform device allocated by platform_device_alloc to add resources to - * @res: set of resources that needs to be allocated for the device - * @num: number of resources + * platform_device_add_resources + * @pdev: platform device allocated by platform_device_alloc to add resources to + * @res: set of resources that needs to be allocated for the device + * @num: number of resources * - * Add a copy of the resources to the platform device. The memory - * associated with the resources will be freed when the platform - * device is released. + * Add a copy of the resources to the platform device. The memory + * associated with the resources will be freed when the platform device is + * released. */ -int platform_device_add_resources(struct platform_device *pdev, struct resource *res, unsigned int num) +int platform_device_add_resources(struct platform_device *pdev, + struct resource *res, unsigned int num) { struct resource *r; @@ -203,16 +203,17 @@ int platform_device_add_resources(struct platform_device *pdev, struct resource EXPORT_SYMBOL_GPL(platform_device_add_resources); /** - * platform_device_add_data - * @pdev: platform device allocated by platform_device_alloc to add resources to - * @data: platform specific data for this platform device - * @size: size of platform specific data + * platform_device_add_data + * @pdev: platform device allocated by platform_device_alloc to add resources to + * @data: platform specific data for this platform device + * @size: size of platform specific data * - * Add a copy of platform specific data to the platform device's platform_data - * pointer. The memory associated with the platform data will be freed - * when the platform device is released. + * Add a copy of platform specific data to the platform device's + * platform_data pointer. The memory associated with the platform data + * will be freed when the platform device is released. */ -int platform_device_add_data(struct platform_device *pdev, const void *data, size_t size) +int platform_device_add_data(struct platform_device *pdev, const void *data, + size_t size) { void *d; @@ -226,11 +227,11 @@ int platform_device_add_data(struct platform_device *pdev, const void *data, siz EXPORT_SYMBOL_GPL(platform_device_add_data); /** - * platform_device_add - add a platform device to device hierarchy - * @pdev: platform device we're adding + * platform_device_add - add a platform device to device hierarchy + * @pdev: platform device we're adding * - * This is part 2 of platform_device_register(), though may be called - * separately _iff_ pdev was allocated by platform_device_alloc(). + * This is part 2 of platform_device_register(), though may be called + * separately _iff_ pdev was allocated by platform_device_alloc(). */ int platform_device_add(struct platform_device *pdev) { @@ -289,13 +290,12 @@ int platform_device_add(struct platform_device *pdev) EXPORT_SYMBOL_GPL(platform_device_add); /** - * platform_device_del - remove a platform-level device - * @pdev: platform device we're removing + * platform_device_del - remove a platform-level device + * @pdev: platform device we're removing * - * Note that this function will also release all memory- and port-based - * resources owned by the device (@dev->resource). This function - * must _only_ be externally called in error cases. All other usage - * is a bug. + * Note that this function will also release all memory- and port-based + * resources owned by the device (@dev->resource). This function must + * _only_ be externally called in error cases. All other usage is a bug. */ void platform_device_del(struct platform_device *pdev) { @@ -314,11 +314,10 @@ void platform_device_del(struct platform_device *pdev) EXPORT_SYMBOL_GPL(platform_device_del); /** - * platform_device_register - add a platform-level device - * @pdev: platform device we're adding - * + * platform_device_register - add a platform-level device + * @pdev: platform device we're adding */ -int platform_device_register(struct platform_device * pdev) +int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); return platform_device_add(pdev); @@ -326,14 +325,14 @@ int platform_device_register(struct platform_device * pdev) EXPORT_SYMBOL_GPL(platform_device_register); /** - * platform_device_unregister - unregister a platform-level device - * @pdev: platform device we're unregistering + * platform_device_unregister - unregister a platform-level device + * @pdev: platform device we're unregistering * - * Unregistration is done in 2 steps. First we release all resources - * and remove it from the subsystem, then we drop reference count by - * calling platform_device_put(). + * Unregistration is done in 2 steps. First we release all resources + * and remove it from the subsystem, then we drop reference count by + * calling platform_device_put(). */ -void platform_device_unregister(struct platform_device * pdev) +void platform_device_unregister(struct platform_device *pdev) { platform_device_del(pdev); platform_device_put(pdev); @@ -341,27 +340,29 @@ void platform_device_unregister(struct platform_device * pdev) EXPORT_SYMBOL_GPL(platform_device_unregister); /** - * platform_device_register_simple - * @name: base name of the device we're adding - * @id: instance id - * @res: set of resources that needs to be allocated for the device - * @num: number of resources + * platform_device_register_simple + * @name: base name of the device we're adding + * @id: instance id + * @res: set of resources that needs to be allocated for the device + * @num: number of resources * - * This function creates a simple platform device that requires minimal - * resource and memory management. Canned release function freeing - * memory allocated for the device allows drivers using such devices - * to be unloaded without waiting for the last reference to the device - * to be dropped. + * This function creates a simple platform device that requires minimal + * resource and memory management. Canned release function freeing memory + * allocated for the device allows drivers using such devices to be + * unloaded without waiting for the last reference to the device to be + * dropped. * - * This interface is primarily intended for use with legacy drivers - * which probe hardware directly. Because such drivers create sysfs - * device nodes themselves, rather than letting system infrastructure - * handle such device enumeration tasks, they don't fully conform to - * the Linux driver model. In particular, when such drivers are built - * as modules, they can't be "hotplugged". + * This interface is primarily intended for use with legacy drivers which + * probe hardware directly. Because such drivers create sysfs device nodes + * themselves, rather than letting system infrastructure handle such device + * enumeration tasks, they don't fully conform to the Linux driver model. + * In particular, when such drivers are built as modules, they can't be + * "hotplugged". */ -struct platform_device *platform_device_register_simple(char *name, int id, - struct resource *res, unsigned int num) +struct platform_device *platform_device_register_simple(const char *name, + int id, + struct resource *res, + unsigned int num) { struct platform_device *pdev; int retval; @@ -436,8 +437,8 @@ static int platform_drv_resume(struct device *_dev) } /** - * platform_driver_register - * @drv: platform driver structure + * platform_driver_register + * @drv: platform driver structure */ int platform_driver_register(struct platform_driver *drv) { @@ -457,8 +458,8 @@ int platform_driver_register(struct platform_driver *drv) EXPORT_SYMBOL_GPL(platform_driver_register); /** - * platform_driver_unregister - * @drv: platform driver structure + * platform_driver_unregister + * @drv: platform driver structure */ void platform_driver_unregister(struct platform_driver *drv) { @@ -497,12 +498,12 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv, * if the probe was successful, and make sure any forced probes of * new devices fail. */ - spin_lock(&platform_bus_type.klist_drivers.k_lock); + spin_lock(&platform_bus_type.p->klist_drivers.k_lock); drv->probe = NULL; - if (code == 0 && list_empty(&drv->driver.klist_devices.k_list)) + if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list)) retval = -ENODEV; drv->driver.probe = platform_drv_probe_fail; - spin_unlock(&platform_bus_type.klist_drivers.k_lock); + spin_unlock(&platform_bus_type.p->klist_drivers.k_lock); if (code != retval) platform_driver_unregister(drv); @@ -516,8 +517,8 @@ EXPORT_SYMBOL_GPL(platform_driver_probe); * (b) sysfs attribute lets new-style coldplug recover from hotplug events * mishandled before system is fully running: "modprobe $(cat modalias)" */ -static ssize_t -modalias_show(struct device *dev, struct device_attribute *a, char *buf) +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, + char *buf) { struct platform_device *pdev = to_platform_device(dev); int len = snprintf(buf, PAGE_SIZE, "platform:%s\n", pdev->name); @@ -538,26 +539,24 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } - /** - * platform_match - bind platform device to platform driver. - * @dev: device. - * @drv: driver. + * platform_match - bind platform device to platform driver. + * @dev: device. + * @drv: driver. * - * Platform device IDs are assumed to be encoded like this: - * "<name><instance>", where <name> is a short description of the - * type of device, like "pci" or "floppy", and <instance> is the - * enumerated instance of the device, like '0' or '42'. - * Driver IDs are simply "<name>". - * So, extract the <name> from the platform_device structure, - * and compare it against the name of the driver. Return whether - * they match or not. + * Platform device IDs are assumed to be encoded like this: + * "<name><instance>", where <name> is a short description of the type of + * device, like "pci" or "floppy", and <instance> is the enumerated + * instance of the device, like '0' or '42'. Driver IDs are simply + * "<name>". So, extract the <name> from the platform_device structure, + * and compare it against the name of the driver. Return whether they match + * or not. */ - -static int platform_match(struct device * dev, struct device_driver * drv) +static int platform_match(struct device *dev, struct device_driver *drv) { - struct platform_device *pdev = container_of(dev, struct platform_device, dev); + struct platform_device *pdev; + pdev = container_of(dev, struct platform_device, dev); return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); } @@ -574,9 +573,10 @@ static int platform_suspend(struct device *dev, pm_message_t mesg) static int platform_suspend_late(struct device *dev, pm_message_t mesg) { struct platform_driver *drv = to_platform_driver(dev->driver); - struct platform_device *pdev = container_of(dev, struct platform_device, dev); + struct platform_device *pdev; int ret = 0; + pdev = container_of(dev, struct platform_device, dev); if (dev->driver && drv->suspend_late) ret = drv->suspend_late(pdev, mesg); @@ -586,16 +586,17 @@ static int platform_suspend_late(struct device *dev, pm_message_t mesg) static int platform_resume_early(struct device *dev) { struct platform_driver *drv = to_platform_driver(dev->driver); - struct platform_device *pdev = container_of(dev, struct platform_device, dev); + struct platform_device *pdev; int ret = 0; + pdev = container_of(dev, struct platform_device, dev); if (dev->driver && drv->resume_early) ret = drv->resume_early(pdev); return ret; } -static int platform_resume(struct device * dev) +static int platform_resume(struct device *dev) { int ret = 0; diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index a803733c839..911208b8925 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,10 +1,6 @@ -obj-y := shutdown.o -obj-$(CONFIG_PM_SLEEP) += main.o sysfs.o -obj-$(CONFIG_PM_TRACE) += trace.o +obj-$(CONFIG_PM) += sysfs.o +obj-$(CONFIG_PM_SLEEP) += main.o +obj-$(CONFIG_PM_TRACE_RTC) += trace.o -ifeq ($(CONFIG_DEBUG_DRIVER),y) -EXTRA_CFLAGS += -DDEBUG -endif -ifeq ($(CONFIG_PM_VERBOSE),y) -EXTRA_CFLAGS += -DDEBUG -endif +ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG +ccflags-$(CONFIG_PM_VERBOSE) += -DDEBUG diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 0ab4ab21f56..200ed5fafd5 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -24,38 +24,85 @@ #include <linux/mutex.h> #include <linux/pm.h> #include <linux/resume-trace.h> +#include <linux/rwsem.h> #include "../base.h" #include "power.h" +/* + * The entries in the dpm_active list are in a depth first order, simply + * because children are guaranteed to be discovered after parents, and + * are inserted at the back of the list on discovery. + * + * All the other lists are kept in the same order, for consistency. + * However the lists aren't always traversed in the same order. + * Semaphores must be acquired from the top (i.e., front) down + * and released in the opposite order. Devices must be suspended + * from the bottom (i.e., end) up and resumed in the opposite order. + * That way no parent will be suspended while it still has an active + * child. + * + * Since device_pm_add() may be called with a device semaphore held, + * we must never try to acquire a device semaphore while holding + * dpm_list_mutex. + */ + LIST_HEAD(dpm_active); +static LIST_HEAD(dpm_locked); static LIST_HEAD(dpm_off); static LIST_HEAD(dpm_off_irq); +static LIST_HEAD(dpm_destroy); -static DEFINE_MUTEX(dpm_mtx); static DEFINE_MUTEX(dpm_list_mtx); -int (*platform_enable_wakeup)(struct device *dev, int is_on); +static DECLARE_RWSEM(pm_sleep_rwsem); +int (*platform_enable_wakeup)(struct device *dev, int is_on); -int device_pm_add(struct device *dev) +/** + * device_pm_add - add a device to the list of active devices + * @dev: Device to be added to the list + */ +void device_pm_add(struct device *dev) { - int error; - pr_debug("PM: Adding info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", kobject_name(&dev->kobj)); mutex_lock(&dpm_list_mtx); list_add_tail(&dev->power.entry, &dpm_active); - error = dpm_sysfs_add(dev); - if (error) - list_del(&dev->power.entry); mutex_unlock(&dpm_list_mtx); - return error; } +/** + * device_pm_remove - remove a device from the list of active devices + * @dev: Device to be removed from the list + * + * This function also removes the device's PM-related sysfs attributes. + */ void device_pm_remove(struct device *dev) { + /* + * If this function is called during a suspend, it will be blocked, + * because we're holding the device's semaphore at that time, which may + * lead to a deadlock. In that case we want to print a warning. + * However, it may also be called by unregister_dropped_devices() with + * the device's semaphore released, in which case the warning should + * not be printed. + */ + if (down_trylock(&dev->sem)) { + if (down_read_trylock(&pm_sleep_rwsem)) { + /* No suspend in progress, wait on dev->sem */ + down(&dev->sem); + up_read(&pm_sleep_rwsem); + } else { + /* Suspend in progress, we may deadlock */ + dev_warn(dev, "Suspicious %s during suspend\n", + __FUNCTION__); + dump_stack(); + /* The user has been warned ... */ + down(&dev->sem); + } + } pr_debug("PM: Removing info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", kobject_name(&dev->kobj)); @@ -63,25 +110,124 @@ void device_pm_remove(struct device *dev) dpm_sysfs_remove(dev); list_del_init(&dev->power.entry); mutex_unlock(&dpm_list_mtx); + up(&dev->sem); +} + +/** + * device_pm_schedule_removal - schedule the removal of a suspended device + * @dev: Device to destroy + * + * Moves the device to the dpm_destroy list for further processing by + * unregister_dropped_devices(). + */ +void device_pm_schedule_removal(struct device *dev) +{ + pr_debug("PM: Preparing for removal: %s:%s\n", + dev->bus ? dev->bus->name : "No Bus", + kobject_name(&dev->kobj)); + mutex_lock(&dpm_list_mtx); + list_move_tail(&dev->power.entry, &dpm_destroy); + mutex_unlock(&dpm_list_mtx); +} + +/** + * pm_sleep_lock - mutual exclusion for registration and suspend + * + * Returns 0 if no suspend is underway and device registration + * may proceed, otherwise -EBUSY. + */ +int pm_sleep_lock(void) +{ + if (down_read_trylock(&pm_sleep_rwsem)) + return 0; + + return -EBUSY; +} + +/** + * pm_sleep_unlock - mutual exclusion for registration and suspend + * + * This routine undoes the effect of device_pm_add_lock + * when a device's registration is complete. + */ +void pm_sleep_unlock(void) +{ + up_read(&pm_sleep_rwsem); } /*------------------------- Resume routines -------------------------*/ /** - * resume_device - Restore state for one device. + * resume_device_early - Power on one device (early resume). * @dev: Device. * + * Must be called with interrupts disabled. */ - -static int resume_device(struct device * dev) +static int resume_device_early(struct device *dev) { int error = 0; TRACE_DEVICE(dev); TRACE_RESUME(0); - down(&dev->sem); + if (dev->bus && dev->bus->resume_early) { + dev_dbg(dev, "EARLY resume\n"); + error = dev->bus->resume_early(dev); + } + + TRACE_RESUME(error); + return error; +} + +/** + * dpm_power_up - Power on all regular (non-sysdev) devices. + * + * Walk the dpm_off_irq list and power each device up. This + * is used for devices that required they be powered down with + * interrupts disabled. As devices are powered on, they are moved + * to the dpm_off list. + * + * Must be called with interrupts disabled and only one CPU running. + */ +static void dpm_power_up(void) +{ + + while (!list_empty(&dpm_off_irq)) { + struct list_head *entry = dpm_off_irq.next; + struct device *dev = to_device(entry); + + list_move_tail(entry, &dpm_off); + resume_device_early(dev); + } +} + +/** + * device_power_up - Turn on all devices that need special attention. + * + * Power on system devices, then devices that required we shut them down + * with interrupts disabled. + * + * Must be called with interrupts disabled. + */ +void device_power_up(void) +{ + sysdev_resume(); + dpm_power_up(); +} +EXPORT_SYMBOL_GPL(device_power_up); + +/** + * resume_device - Restore state for one device. + * @dev: Device. + * + */ +static int resume_device(struct device *dev) +{ + int error = 0; + + TRACE_DEVICE(dev); + TRACE_RESUME(0); if (dev->bus && dev->bus->resume) { dev_dbg(dev,"resuming\n"); @@ -98,126 +244,94 @@ static int resume_device(struct device * dev) error = dev->class->resume(dev); } - up(&dev->sem); - - TRACE_RESUME(error); - return error; -} - - -static int resume_device_early(struct device * dev) -{ - int error = 0; - - TRACE_DEVICE(dev); - TRACE_RESUME(0); - if (dev->bus && dev->bus->resume_early) { - dev_dbg(dev,"EARLY resume\n"); - error = dev->bus->resume_early(dev); - } TRACE_RESUME(error); return error; } -/* - * Resume the devices that have either not gone through - * the late suspend, or that did go through it but also - * went through the early resume +/** + * dpm_resume - Resume every device. + * + * Resume the devices that have either not gone through + * the late suspend, or that did go through it but also + * went through the early resume. + * + * Take devices from the dpm_off_list, resume them, + * and put them on the dpm_locked list. */ static void dpm_resume(void) { mutex_lock(&dpm_list_mtx); while(!list_empty(&dpm_off)) { - struct list_head * entry = dpm_off.next; - struct device * dev = to_device(entry); - - get_device(dev); - list_move_tail(entry, &dpm_active); + struct list_head *entry = dpm_off.next; + struct device *dev = to_device(entry); + list_move_tail(entry, &dpm_locked); mutex_unlock(&dpm_list_mtx); resume_device(dev); mutex_lock(&dpm_list_mtx); - put_device(dev); } mutex_unlock(&dpm_list_mtx); } - /** - * device_resume - Restore state of each device in system. + * unlock_all_devices - Release each device's semaphore * - * Walk the dpm_off list, remove each entry, resume the device, - * then add it to the dpm_active list. + * Go through the dpm_off list. Put each device on the dpm_active + * list and unlock it. */ - -void device_resume(void) +static void unlock_all_devices(void) { - might_sleep(); - mutex_lock(&dpm_mtx); - dpm_resume(); - mutex_unlock(&dpm_mtx); -} - -EXPORT_SYMBOL_GPL(device_resume); + mutex_lock(&dpm_list_mtx); + while (!list_empty(&dpm_locked)) { + struct list_head *entry = dpm_locked.prev; + struct device *dev = to_device(entry); + list_move(entry, &dpm_active); + up(&dev->sem); + } + mutex_unlock(&dpm_list_mtx); +} /** - * dpm_power_up - Power on some devices. - * - * Walk the dpm_off_irq list and power each device up. This - * is used for devices that required they be powered down with - * interrupts disabled. As devices are powered on, they are moved - * to the dpm_active list. + * unregister_dropped_devices - Unregister devices scheduled for removal * - * Interrupts must be disabled when calling this. + * Unregister all devices on the dpm_destroy list. */ - -static void dpm_power_up(void) +static void unregister_dropped_devices(void) { - while(!list_empty(&dpm_off_irq)) { - struct list_head * entry = dpm_off_irq.next; - struct device * dev = to_device(entry); + mutex_lock(&dpm_list_mtx); + while (!list_empty(&dpm_destroy)) { + struct list_head *entry = dpm_destroy.next; + struct device *dev = to_device(entry); - list_move_tail(entry, &dpm_off); - resume_device_early(dev); + up(&dev->sem); + mutex_unlock(&dpm_list_mtx); + /* This also removes the device from the list */ + device_unregister(dev); + mutex_lock(&dpm_list_mtx); } + mutex_unlock(&dpm_list_mtx); } - /** - * device_power_up - Turn on all devices that need special attention. + * device_resume - Restore state of each device in system. * - * Power on system devices then devices that required we shut them down - * with interrupts disabled. - * Called with interrupts disabled. + * Resume all the devices, unlock them all, and allow new + * devices to be registered once again. */ - -void device_power_up(void) +void device_resume(void) { - sysdev_resume(); - dpm_power_up(); + might_sleep(); + dpm_resume(); + unlock_all_devices(); + unregister_dropped_devices(); + up_write(&pm_sleep_rwsem); } - -EXPORT_SYMBOL_GPL(device_power_up); +EXPORT_SYMBOL_GPL(device_resume); /*------------------------- Suspend routines -------------------------*/ -/* - * The entries in the dpm_active list are in a depth first order, simply - * because children are guaranteed to be discovered after parents, and - * are inserted at the back of the list on discovery. - * - * All list on the suspend path are done in reverse order, so we operate - * on the leaves of the device tree (or forests, depending on how you want - * to look at it ;) first. As nodes are removed from the back of the list, - * they are inserted into the front of their destintation lists. - * - * Things are the reverse on the resume path - iterations are done in - * forward order, and nodes are inserted at the back of their destination - * lists. This way, the ancestors will be accessed before their descendents. - */ - static inline char *suspend_verb(u32 event) { switch (event) { @@ -228,7 +342,6 @@ static inline char *suspend_verb(u32 event) } } - static void suspend_device_dbg(struct device *dev, pm_message_t state, char *info) { @@ -238,16 +351,73 @@ suspend_device_dbg(struct device *dev, pm_message_t state, char *info) } /** - * suspend_device - Save state of one device. + * suspend_device_late - Shut down one device (late suspend). * @dev: Device. * @state: Power state device is entering. + * + * This is called with interrupts off and only a single CPU running. + */ +static int suspend_device_late(struct device *dev, pm_message_t state) +{ + int error = 0; + + if (dev->bus && dev->bus->suspend_late) { + suspend_device_dbg(dev, state, "LATE "); + error = dev->bus->suspend_late(dev, state); + suspend_report_result(dev->bus->suspend_late, error); + } + return error; +} + +/** + * device_power_down - Shut down special devices. + * @state: Power state to enter. + * + * Power down devices that require interrupts to be disabled + * and move them from the dpm_off list to the dpm_off_irq list. + * Then power down system devices. + * + * Must be called with interrupts disabled and only one CPU running. */ +int device_power_down(pm_message_t state) +{ + int error = 0; + + while (!list_empty(&dpm_off)) { + struct list_head *entry = dpm_off.prev; + struct device *dev = to_device(entry); + + list_del_init(&dev->power.entry); + error = suspend_device_late(dev, state); + if (error) { + printk(KERN_ERR "Could not power down device %s: " + "error %d\n", + kobject_name(&dev->kobj), error); + if (list_empty(&dev->power.entry)) + list_add(&dev->power.entry, &dpm_off); + break; + } + if (list_empty(&dev->power.entry)) + list_add(&dev->power.entry, &dpm_off_irq); + } + + if (!error) + error = sysdev_suspend(state); + if (error) + dpm_power_up(); + return error; +} +EXPORT_SYMBOL_GPL(device_power_down); -static int suspend_device(struct device * dev, pm_message_t state) +/** + * suspend_device - Save state of one device. + * @dev: Device. + * @state: Power state device is entering. + */ +int suspend_device(struct device *dev, pm_message_t state) { int error = 0; - down(&dev->sem); if (dev->power.power_state.event) { dev_dbg(dev, "PM: suspend %d-->%d\n", dev->power.power_state.event, state.event); @@ -270,123 +440,105 @@ static int suspend_device(struct device * dev, pm_message_t state) error = dev->bus->suspend(dev, state); suspend_report_result(dev->bus->suspend, error); } - up(&dev->sem); - return error; -} - - -/* - * This is called with interrupts off, only a single CPU - * running. We can't acquire a mutex or semaphore (and we don't - * need the protection) - */ -static int suspend_device_late(struct device *dev, pm_message_t state) -{ - int error = 0; - - if (dev->bus && dev->bus->suspend_late) { - suspend_device_dbg(dev, state, "LATE "); - error = dev->bus->suspend_late(dev, state); - suspend_report_result(dev->bus->suspend_late, error); - } return error; } /** - * device_suspend - Save state and stop all devices in system. - * @state: Power state to put each device in. + * dpm_suspend - Suspend every device. + * @state: Power state to put each device in. * - * Walk the dpm_active list, call ->suspend() for each device, and move - * it to the dpm_off list. + * Walk the dpm_locked list. Suspend each device and move it + * to the dpm_off list. * * (For historical reasons, if it returns -EAGAIN, that used to mean * that the device would be called again with interrupts disabled. * These days, we use the "suspend_late()" callback for that, so we * print a warning and consider it an error). - * - * If we get a different error, try and back out. - * - * If we hit a failure with any of the devices, call device_resume() - * above to bring the suspended devices back to life. - * */ - -int device_suspend(pm_message_t state) +static int dpm_suspend(pm_message_t state) { int error = 0; - might_sleep(); - mutex_lock(&dpm_mtx); mutex_lock(&dpm_list_mtx); - while (!list_empty(&dpm_active) && error == 0) { - struct list_head * entry = dpm_active.prev; - struct device * dev = to_device(entry); + while (!list_empty(&dpm_locked)) { + struct list_head *entry = dpm_locked.prev; + struct device *dev = to_device(entry); - get_device(dev); + list_del_init(&dev->power.entry); mutex_unlock(&dpm_list_mtx); - error = suspend_device(dev, state); - - mutex_lock(&dpm_list_mtx); - - /* Check if the device got removed */ - if (!list_empty(&dev->power.entry)) { - /* Move it to the dpm_off list */ - if (!error) - list_move(&dev->power.entry, &dpm_off); - } - if (error) + if (error) { printk(KERN_ERR "Could not suspend device %s: " - "error %d%s\n", - kobject_name(&dev->kobj), error, - error == -EAGAIN ? " (please convert to suspend_late)" : ""); - put_device(dev); + "error %d%s\n", + kobject_name(&dev->kobj), + error, + (error == -EAGAIN ? + " (please convert to suspend_late)" : + "")); + mutex_lock(&dpm_list_mtx); + if (list_empty(&dev->power.entry)) + list_add(&dev->power.entry, &dpm_locked); + mutex_unlock(&dpm_list_mtx); + break; + } + mutex_lock(&dpm_list_mtx); + if (list_empty(&dev->power.entry)) + list_add(&dev->power.entry, &dpm_off); } mutex_unlock(&dpm_list_mtx); - if (error) - dpm_resume(); - mutex_unlock(&dpm_mtx); return error; } -EXPORT_SYMBOL_GPL(device_suspend); - /** - * device_power_down - Shut down special devices. - * @state: Power state to enter. + * lock_all_devices - Acquire every device's semaphore * - * Walk the dpm_off_irq list, calling ->power_down() for each device that - * couldn't power down the device with interrupts enabled. When we're - * done, power down system devices. + * Go through the dpm_active list. Carefully lock each device's + * semaphore and put it in on the dpm_locked list. */ - -int device_power_down(pm_message_t state) +static void lock_all_devices(void) { - int error = 0; - struct device * dev; + mutex_lock(&dpm_list_mtx); + while (!list_empty(&dpm_active)) { + struct list_head *entry = dpm_active.next; + struct device *dev = to_device(entry); - while (!list_empty(&dpm_off)) { - struct list_head * entry = dpm_off.prev; + /* Required locking order is dev->sem first, + * then dpm_list_mutex. Hence this awkward code. + */ + get_device(dev); + mutex_unlock(&dpm_list_mtx); + down(&dev->sem); + mutex_lock(&dpm_list_mtx); - dev = to_device(entry); - error = suspend_device_late(dev, state); - if (error) - goto Error; - list_move(&dev->power.entry, &dpm_off_irq); + if (list_empty(entry)) + up(&dev->sem); /* Device was removed */ + else + list_move_tail(entry, &dpm_locked); + put_device(dev); } + mutex_unlock(&dpm_list_mtx); +} + +/** + * device_suspend - Save state and stop all devices in system. + * + * Prevent new devices from being registered, then lock all devices + * and suspend them. + */ +int device_suspend(pm_message_t state) +{ + int error; - error = sysdev_suspend(state); - Done: + might_sleep(); + down_write(&pm_sleep_rwsem); + lock_all_devices(); + error = dpm_suspend(state); + if (error) + device_resume(); return error; - Error: - printk(KERN_ERR "Could not power down device %s: " - "error %d\n", kobject_name(&dev->kobj), error); - dpm_power_up(); - goto Done; } - -EXPORT_SYMBOL_GPL(device_power_down); +EXPORT_SYMBOL_GPL(device_suspend); void __suspend_report_result(const char *function, void *fn, int ret) { diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 5c4efd493fa..6f0dfca8ebd 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -1,10 +1,3 @@ -/* - * shutdown.c - */ - -extern void device_shutdown(void); - - #ifdef CONFIG_PM_SLEEP /* @@ -13,13 +6,40 @@ extern void device_shutdown(void); extern struct list_head dpm_active; /* The active device list */ -static inline struct device * to_device(struct list_head * entry) +static inline struct device *to_device(struct list_head *entry) { return container_of(entry, struct device, power.entry); } -extern int device_pm_add(struct device *); +extern void device_pm_add(struct device *); extern void device_pm_remove(struct device *); +extern void device_pm_schedule_removal(struct device *); +extern int pm_sleep_lock(void); +extern void pm_sleep_unlock(void); + +#else /* CONFIG_PM_SLEEP */ + + +static inline void device_pm_add(struct device *dev) +{ +} + +static inline void device_pm_remove(struct device *dev) +{ +} + +static inline int pm_sleep_lock(void) +{ + return 0; +} + +static inline void pm_sleep_unlock(void) +{ +} + +#endif + +#ifdef CONFIG_PM /* * sysfs.c @@ -28,16 +48,15 @@ extern void device_pm_remove(struct device *); extern int dpm_sysfs_add(struct device *); extern void dpm_sysfs_remove(struct device *); -#else /* CONFIG_PM_SLEEP */ +#else /* CONFIG_PM */ - -static inline int device_pm_add(struct device * dev) +static inline int dpm_sysfs_add(struct device *dev) { return 0; } -static inline void device_pm_remove(struct device * dev) -{ +static inline void dpm_sysfs_remove(struct device *dev) +{ } #endif diff --git a/drivers/base/power/shutdown.c b/drivers/base/power/shutdown.c deleted file mode 100644 index 56e8eaaac01..00000000000 --- a/drivers/base/power/shutdown.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * shutdown.c - power management functions for the device tree. - * - * Copyright (c) 2002-3 Patrick Mochel - * 2002-3 Open Source Development Lab - * - * This file is released under the GPLv2 - * - */ - -#include <linux/device.h> -#include <asm/semaphore.h> - -#include "../base.h" -#include "power.h" - -#define to_dev(node) container_of(node, struct device, kobj.entry) - - -/** - * We handle system devices differently - we suspend and shut them - * down last and resume them first. That way, we don't do anything stupid like - * shutting down the interrupt controller before any devices.. - * - * Note that there are not different stages for power management calls - - * they only get one called once when interrupts are disabled. - */ - - -/** - * 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_subsys.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); - } - } -} - diff --git a/drivers/base/sys.c b/drivers/base/sys.c index ac7ff6d0c6e..2f79c55acdc 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -25,8 +25,6 @@ #include "base.h" -extern struct kset devices_subsys; - #define to_sysdev(k) container_of(k, struct sys_device, kobj) #define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr) @@ -128,18 +126,17 @@ void sysdev_class_remove_file(struct sysdev_class *c, } EXPORT_SYMBOL_GPL(sysdev_class_remove_file); -/* - * declare system_subsys - */ -static decl_subsys(system, &ktype_sysdev_class, NULL); +static struct kset *system_kset; int sysdev_class_register(struct sysdev_class * cls) { pr_debug("Registering sysdev class '%s'\n", kobject_name(&cls->kset.kobj)); INIT_LIST_HEAD(&cls->drivers); - cls->kset.kobj.parent = &system_subsys.kobj; - cls->kset.kobj.kset = &system_subsys; + cls->kset.kobj.parent = &system_kset->kobj; + cls->kset.kobj.ktype = &ktype_sysdev_class; + cls->kset.kobj.kset = system_kset; + kobject_set_name(&cls->kset.kobj, cls->name); return kset_register(&cls->kset); } @@ -228,20 +225,15 @@ int sysdev_register(struct sys_device * sysdev) if (!cls) return -EINVAL; + pr_debug("Registering sys device '%s'\n", kobject_name(&sysdev->kobj)); + /* Make sure the kset is set */ sysdev->kobj.kset = &cls->kset; - /* But make sure we point to the right type for sysfs translation */ - sysdev->kobj.ktype = &ktype_sysdev; - error = kobject_set_name(&sysdev->kobj, "%s%d", - kobject_name(&cls->kset.kobj), sysdev->id); - if (error) - return error; - - pr_debug("Registering sys device '%s'\n", kobject_name(&sysdev->kobj)); - /* Register the object */ - error = kobject_register(&sysdev->kobj); + error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL, + "%s%d", kobject_name(&cls->kset.kobj), + sysdev->id); if (!error) { struct sysdev_driver * drv; @@ -258,6 +250,7 @@ int sysdev_register(struct sys_device * sysdev) } mutex_unlock(&sysdev_drivers_lock); } + kobject_uevent(&sysdev->kobj, KOBJ_ADD); return error; } @@ -272,7 +265,7 @@ void sysdev_unregister(struct sys_device * sysdev) } mutex_unlock(&sysdev_drivers_lock); - kobject_unregister(&sysdev->kobj); + kobject_put(&sysdev->kobj); } @@ -298,8 +291,7 @@ void sysdev_shutdown(void) pr_debug("Shutting Down System Devices\n"); mutex_lock(&sysdev_drivers_lock); - list_for_each_entry_reverse(cls, &system_subsys.list, - kset.kobj.entry) { + list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) { struct sys_device * sysdev; pr_debug("Shutting down type '%s':\n", @@ -361,9 +353,7 @@ int sysdev_suspend(pm_message_t state) pr_debug("Suspending System Devices\n"); - list_for_each_entry_reverse(cls, &system_subsys.list, - kset.kobj.entry) { - + list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) { pr_debug("Suspending type '%s':\n", kobject_name(&cls->kset.kobj)); @@ -414,8 +404,7 @@ aux_driver: } /* resume other classes */ - list_for_each_entry_continue(cls, &system_subsys.list, - kset.kobj.entry) { + list_for_each_entry_continue(cls, &system_kset->list, kset.kobj.entry) { list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { pr_debug(" %s\n", kobject_name(&err_dev->kobj)); __sysdev_resume(err_dev); @@ -440,7 +429,7 @@ int sysdev_resume(void) pr_debug("Resuming System Devices\n"); - list_for_each_entry(cls, &system_subsys.list, kset.kobj.entry) { + list_for_each_entry(cls, &system_kset->list, kset.kobj.entry) { struct sys_device * sysdev; pr_debug("Resuming type '%s':\n", @@ -458,8 +447,10 @@ int sysdev_resume(void) int __init system_bus_init(void) { - system_subsys.kobj.parent = &devices_subsys.kobj; - return subsystem_register(&system_subsys); + system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj); + if (!system_kset) + return -ENOMEM; + return 0; } EXPORT_SYMBOL_GPL(sysdev_register); |