diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/Makefile | 4 | ||||
-rw-r--r-- | drivers/base/base.h | 2 | ||||
-rw-r--r-- | drivers/base/bus.c | 299 | ||||
-rw-r--r-- | drivers/base/class.c | 194 | ||||
-rw-r--r-- | drivers/base/class_simple.c | 199 | ||||
-rw-r--r-- | drivers/base/core.c | 62 | ||||
-rw-r--r-- | drivers/base/dd.c | 248 | ||||
-rw-r--r-- | drivers/base/dmapool.c | 2 | ||||
-rw-r--r-- | drivers/base/driver.c | 39 | ||||
-rw-r--r-- | drivers/base/node.c | 22 | ||||
-rw-r--r-- | drivers/base/power/resume.c | 8 | ||||
-rw-r--r-- | drivers/base/power/suspend.c | 16 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 4 | ||||
-rw-r--r-- | drivers/base/sys.c | 4 |
14 files changed, 587 insertions, 516 deletions
diff --git a/drivers/base/Makefile b/drivers/base/Makefile index a47928a2e57..66d9c4643fc 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -1,7 +1,7 @@ # Makefile for the Linux device tree -obj-y := core.o sys.o bus.o \ - driver.o class.o class_simple.o platform.o \ +obj-y := core.o sys.o bus.o dd.o \ + driver.o class.o platform.o \ cpu.o firmware.o init.o map.o dmapool.o \ attribute_container.o transport_class.o obj-y += power/ diff --git a/drivers/base/base.h b/drivers/base/base.h index 8d1e8bd4863..645f6269292 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -4,6 +4,8 @@ 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 void driver_detach(struct device_driver * drv); + static inline struct class_device *to_class_dev(struct kobject *obj) { return container_of(obj, struct class_device, kobj); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 3cb04bb04c2..43722af90bd 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -17,9 +17,6 @@ #include "base.h" #include "power/power.h" -#define to_dev(node) container_of(node, struct device, bus_list) -#define to_drv(node) container_of(node, struct device_driver, kobj.entry) - #define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr) #define to_bus(obj) container_of(obj, struct bus_type, subsys.kset.kobj) @@ -36,7 +33,7 @@ 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); - ssize_t ret = 0; + ssize_t ret = -EIO; if (drv_attr->show) ret = drv_attr->show(drv, buf); @@ -49,7 +46,7 @@ drv_attr_store(struct kobject * kobj, struct attribute * attr, { struct driver_attribute * drv_attr = to_drv_attr(attr); struct device_driver * drv = to_driver(kobj); - ssize_t ret = 0; + ssize_t ret = -EIO; if (drv_attr->store) ret = drv_attr->store(drv, buf, count); @@ -135,50 +132,11 @@ static struct kobj_type ktype_bus = { decl_subsys(bus, &ktype_bus, NULL); -static int __bus_for_each_dev(struct bus_type *bus, struct device *start, - void *data, int (*fn)(struct device *, void *)) -{ - struct list_head *head; - struct device *dev; - int error = 0; - - if (!(bus = get_bus(bus))) - return -EINVAL; - - head = &bus->devices.list; - dev = list_prepare_entry(start, head, bus_list); - list_for_each_entry_continue(dev, head, bus_list) { - get_device(dev); - error = fn(dev, data); - put_device(dev); - if (error) - break; - } - put_bus(bus); - return error; -} -static int __bus_for_each_drv(struct bus_type *bus, struct device_driver *start, - void * data, int (*fn)(struct device_driver *, void *)) +static struct device * next_device(struct klist_iter * i) { - struct list_head *head; - struct device_driver *drv; - int error = 0; - - if (!(bus = get_bus(bus))) - return -EINVAL; - - head = &bus->drivers.list; - drv = list_prepare_entry(start, head, kobj.entry); - list_for_each_entry_continue(drv, head, kobj.entry) { - get_driver(drv); - error = fn(drv, data); - put_driver(drv); - if (error) - break; - } - put_bus(bus); - return error; + struct klist_node * n = klist_next(i); + return n ? container_of(n, struct device, knode_bus) : NULL; } /** @@ -204,12 +162,27 @@ static int __bus_for_each_drv(struct bus_type *bus, struct device_driver *start, int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, int (*fn)(struct device *, void *)) { - int ret; + struct klist_iter i; + struct device * dev; + int error = 0; - down_read(&bus->subsys.rwsem); - ret = __bus_for_each_dev(bus, start, data, fn); - up_read(&bus->subsys.rwsem); - return ret; + if (!bus) + return -EINVAL; + + klist_iter_init_node(&bus->klist_devices, &i, + (start ? &start->knode_bus : NULL)); + while ((dev = next_device(&i)) && !error) + error = fn(dev, data); + klist_iter_exit(&i); + return error; +} + + + +static struct device_driver * next_driver(struct klist_iter * i) +{ + struct klist_node * n = klist_next(i); + return n ? container_of(n, struct device_driver, knode_bus) : NULL; } /** @@ -235,179 +208,19 @@ int bus_for_each_dev(struct bus_type * bus, struct device * start, int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, void * data, int (*fn)(struct device_driver *, void *)) { - int ret; - - down_read(&bus->subsys.rwsem); - ret = __bus_for_each_drv(bus, start, data, fn); - up_read(&bus->subsys.rwsem); - return ret; -} - -/** - * 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. - * - * 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.) - */ - -void device_bind_driver(struct device * dev) -{ - pr_debug("bound device '%s' to driver '%s'\n", - dev->bus_id, dev->driver->name); - list_add_tail(&dev->driver_list, &dev->driver->devices); - sysfs_create_link(&dev->driver->kobj, &dev->kobj, - kobject_name(&dev->kobj)); - sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver"); -} - - -/** - * driver_probe_device - attempt to bind device & driver. - * @drv: driver. - * @dev: device. - * - * First, we call the bus's match function, if one present, which - * should compare the device IDs the driver supports with the - * device IDs of the device. Note we don't do this ourselves - * because we don't know the format of the ID structures, nor what - * is to be considered a match and what is not. - * - * If we find a match, we call @drv->probe(@dev) if it exists, and - * call device_bind_driver() above. - */ -int driver_probe_device(struct device_driver * drv, struct device * dev) -{ - if (drv->bus->match && !drv->bus->match(dev, drv)) - return -ENODEV; - - dev->driver = drv; - if (drv->probe) { - int error = drv->probe(dev); - if (error) { - dev->driver = NULL; - return error; - } - } - - device_bind_driver(dev); - return 0; -} - - -/** - * 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. - */ -int device_attach(struct device * dev) -{ - struct bus_type * bus = dev->bus; - struct list_head * entry; - int error; - - if (dev->driver) { - device_bind_driver(dev); - return 1; - } - - if (bus->match) { - list_for_each(entry, &bus->drivers.list) { - struct device_driver * drv = to_drv(entry); - error = driver_probe_device(drv, dev); - if (!error) - /* success, driver matched */ - return 1; - if (error != -ENODEV && error != -ENXIO) - /* driver matched but the probe failed */ - printk(KERN_WARNING - "%s: probe of %s failed with error %d\n", - drv->name, dev->bus_id, error); - } - } - - return 0; -} - - -/** - * 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. - * - * Note that we ignore the -ENODEV error from driver_probe_device(), - * since it's perfectly valid for a driver not to bind to any devices. - */ -void driver_attach(struct device_driver * drv) -{ - struct bus_type * bus = drv->bus; - struct list_head * entry; - int error; - - if (!bus->match) - return; - - list_for_each(entry, &bus->devices.list) { - struct device * dev = container_of(entry, struct device, bus_list); - if (!dev->driver) { - error = driver_probe_device(drv, dev); - if (error && (error != -ENODEV)) - /* driver matched but the probe failed */ - printk(KERN_WARNING - "%s: probe of %s failed with error %d\n", - drv->name, dev->bus_id, error); - } - } -} - - -/** - * device_release_driver - manually detach device from driver. - * @dev: device. - * - * Manually detach device from driver. - * Note that this is called without incrementing the bus - * reference count nor taking the bus's rwsem. Be sure that - * those are accounted for before calling this function. - */ - -void device_release_driver(struct device * dev) -{ - struct device_driver * drv = dev->driver; - if (drv) { - sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); - sysfs_remove_link(&dev->kobj, "driver"); - list_del_init(&dev->driver_list); - if (drv->remove) - drv->remove(dev); - dev->driver = NULL; - } -} - + struct klist_iter i; + struct device_driver * drv; + int error = 0; -/** - * driver_detach - detach driver from all devices it controls. - * @drv: driver. - */ + if (!bus) + return -EINVAL; -static void driver_detach(struct device_driver * drv) -{ - while (!list_empty(&drv->devices)) { - struct device * dev = container_of(drv->devices.next, struct device, driver_list); - device_release_driver(dev); - } + klist_iter_init_node(&bus->klist_drivers, &i, + start ? &start->knode_bus : NULL); + while ((drv = next_driver(&i)) && !error) + error = fn(drv, data); + klist_iter_exit(&i); + return error; } static int device_add_attrs(struct bus_type * bus, struct device * dev) @@ -456,14 +269,15 @@ int bus_add_device(struct device * dev) int error = 0; if (bus) { - down_write(&dev->bus->subsys.rwsem); pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id); - list_add_tail(&dev->bus_list, &dev->bus->devices.list); - device_attach(dev); - up_write(&dev->bus->subsys.rwsem); - device_add_attrs(bus, dev); - sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); - sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus"); + error = device_attach(dev); + klist_add_tail(&bus->klist_devices, &dev->knode_bus); + if (error >= 0) + error = device_add_attrs(bus, dev); + if (!error) { + sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); + sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus"); + } } return error; } @@ -483,11 +297,9 @@ void bus_remove_device(struct device * dev) sysfs_remove_link(&dev->kobj, "bus"); sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id); device_remove_attrs(dev->bus, dev); - down_write(&dev->bus->subsys.rwsem); + klist_remove(&dev->knode_bus); pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id); device_release_driver(dev); - list_del_init(&dev->bus_list); - up_write(&dev->bus->subsys.rwsem); put_bus(dev->bus); } } @@ -547,9 +359,8 @@ int bus_add_driver(struct device_driver * drv) return error; } - down_write(&bus->subsys.rwsem); driver_attach(drv); - up_write(&bus->subsys.rwsem); + klist_add_tail(&bus->klist_drivers, &drv->knode_bus); module_add_driver(drv->owner, drv); driver_add_attrs(bus, drv); @@ -571,10 +382,9 @@ void bus_remove_driver(struct device_driver * drv) { if (drv->bus) { driver_remove_attrs(drv->bus, drv); - down_write(&drv->bus->subsys.rwsem); + klist_remove(&drv->knode_bus); pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name); driver_detach(drv); - up_write(&drv->bus->subsys.rwsem); module_remove_driver(drv); kobject_unregister(&drv->kobj); put_bus(drv->bus); @@ -587,7 +397,7 @@ static int bus_rescan_devices_helper(struct device *dev, void *data) { int *count = data; - if (!dev->driver && device_attach(dev)) + if (!dev->driver && (device_attach(dev) > 0)) (*count)++; return 0; @@ -607,9 +417,7 @@ int bus_rescan_devices(struct bus_type * bus) { int count = 0; - down_write(&bus->subsys.rwsem); - __bus_for_each_dev(bus, NULL, &count, bus_rescan_devices_helper); - up_write(&bus->subsys.rwsem); + bus_for_each_dev(bus, NULL, &count, bus_rescan_devices_helper); return count; } @@ -710,6 +518,9 @@ int bus_register(struct bus_type * bus) retval = kset_register(&bus->drivers); if (retval) goto bus_drivers_fail; + + klist_init(&bus->klist_devices); + klist_init(&bus->klist_drivers); bus_add_attrs(bus); pr_debug("bus type '%s' registered\n", bus->name); @@ -749,12 +560,6 @@ int __init buses_init(void) EXPORT_SYMBOL_GPL(bus_for_each_dev); EXPORT_SYMBOL_GPL(bus_for_each_drv); -EXPORT_SYMBOL_GPL(driver_probe_device); -EXPORT_SYMBOL_GPL(device_bind_driver); -EXPORT_SYMBOL_GPL(device_release_driver); -EXPORT_SYMBOL_GPL(device_attach); -EXPORT_SYMBOL_GPL(driver_attach); - EXPORT_SYMBOL_GPL(bus_add_device); EXPORT_SYMBOL_GPL(bus_remove_device); EXPORT_SYMBOL_GPL(bus_register); diff --git a/drivers/base/class.c b/drivers/base/class.c index d2a2f8f2b4e..479c1257088 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/string.h> #include <linux/kdev_t.h> +#include <linux/err.h> #include "base.h" #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) @@ -26,7 +27,7 @@ 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); - ssize_t ret = 0; + ssize_t ret = -EIO; if (class_attr->show) ret = class_attr->show(dc, buf); @@ -39,7 +40,7 @@ class_attr_store(struct kobject * kobj, struct attribute * attr, { struct class_attribute * class_attr = to_class_attr(attr); struct class * dc = to_class(kobj); - ssize_t ret = 0; + ssize_t ret = -EIO; if (class_attr->store) ret = class_attr->store(dc, buf, count); @@ -162,6 +163,69 @@ void class_unregister(struct class * cls) subsystem_unregister(&cls->subsys); } +static void class_create_release(struct class *cls) +{ + kfree(cls); +} + +static void class_device_create_release(struct class_device *class_dev) +{ + kfree(class_dev); +} + +/** + * class_create - create a struct class structure + * @owner: pointer to the module that is to "own" this struct class + * @name: pointer to a string for the name of this class. + * + * This is used to create a struct class pointer that can then be used + * in calls to class_device_create(). + * + * Note, the pointer created here is to be destroyed when finished by + * making a call to class_destroy(). + */ +struct class *class_create(struct module *owner, char *name) +{ + struct class *cls; + int retval; + + cls = kmalloc(sizeof(struct class), GFP_KERNEL); + if (!cls) { + retval = -ENOMEM; + goto error; + } + memset(cls, 0x00, sizeof(struct class)); + + cls->name = name; + cls->owner = owner; + cls->class_release = class_create_release; + cls->release = class_device_create_release; + + retval = class_register(cls); + if (retval) + goto error; + + return cls; + +error: + kfree(cls); + return ERR_PTR(retval); +} + +/** + * class_destroy - destroys a struct class structure + * @cs: pointer to the struct class that is to be destroyed + * + * Note, the pointer to be destroyed must have been created with a call + * to class_create(). + */ +void class_destroy(struct class *cls) +{ + if ((cls == NULL) || (IS_ERR(cls))) + return; + + class_unregister(cls); +} /* Class Device Stuff */ @@ -262,7 +326,7 @@ static int class_hotplug_filter(struct kset *kset, struct kobject *kobj) return 0; } -static char *class_hotplug_name(struct kset *kset, struct kobject *kobj) +static const char *class_hotplug_name(struct kset *kset, struct kobject *kobj) { struct class_device *class_dev = to_class_dev(kobj); @@ -375,7 +439,6 @@ static ssize_t show_dev(struct class_device *class_dev, char *buf) { return print_dev_t(buf, class_dev->devt); } -static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL); void class_device_initialize(struct class_device *class_dev) { @@ -412,7 +475,31 @@ int class_device_add(struct class_device *class_dev) if ((error = kobject_add(&class_dev->kobj))) goto register_done; - /* now take care of our own registration */ + /* add the needed attributes to this device */ + if (MAJOR(class_dev->devt)) { + struct class_device_attribute *attr; + attr = kmalloc(sizeof(*attr), GFP_KERNEL); + if (!attr) { + error = -ENOMEM; + kobject_del(&class_dev->kobj); + goto register_done; + } + memset(attr, sizeof(*attr), 0x00); + attr->attr.name = "dev"; + attr->attr.mode = S_IRUGO; + attr->attr.owner = parent->owner; + attr->show = show_dev; + attr->store = NULL; + class_device_create_file(class_dev, attr); + class_dev->devt_attr = attr; + } + + class_device_add_attrs(class_dev); + if (class_dev->dev) + sysfs_create_link(&class_dev->kobj, + &class_dev->dev->kobj, "device"); + + /* notify any interfaces this device is now here */ if (parent) { down(&parent->sem); list_add_tail(&class_dev->node, &parent->children); @@ -421,16 +508,8 @@ int class_device_add(struct class_device *class_dev) class_intf->add(class_dev); up(&parent->sem); } - - if (MAJOR(class_dev->devt)) - class_device_create_file(class_dev, &class_device_attr_dev); - - class_device_add_attrs(class_dev); - if (class_dev->dev) - sysfs_create_link(&class_dev->kobj, - &class_dev->dev->kobj, "device"); - kobject_hotplug(&class_dev->kobj, KOBJ_ADD); + register_done: if (error && parent) class_put(parent); @@ -444,6 +523,58 @@ int class_device_register(struct class_device *class_dev) return class_device_add(class_dev); } +/** + * class_device_create - creates a class device and registers it with sysfs + * @cs: pointer to the struct class that this device should be registered to. + * @dev: the dev_t for the char device to be added. + * @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 + * class_device will be created in sysfs, registered to the specified + * class. A "dev" file will be created, showing the dev_t for the + * device. The pointer to the struct class_device will be returned from + * the call. Any further sysfs files that might be required can be + * created using this pointer. + * + * Note: the struct class passed to this function must have previously + * been created with a call to class_create(). + */ +struct class_device *class_device_create(struct class *cls, dev_t devt, + struct device *device, char *fmt, ...) +{ + va_list args; + struct class_device *class_dev = NULL; + int retval = -ENODEV; + + if (cls == NULL || IS_ERR(cls)) + goto error; + + class_dev = kmalloc(sizeof(struct class_device), GFP_KERNEL); + if (!class_dev) { + retval = -ENOMEM; + goto error; + } + memset(class_dev, 0x00, sizeof(struct class_device)); + + class_dev->devt = devt; + class_dev->dev = device; + class_dev->class = cls; + + va_start(args, fmt); + vsnprintf(class_dev->class_id, BUS_ID_SIZE, fmt, args); + va_end(args); + retval = class_device_register(class_dev); + if (retval) + goto error; + + return class_dev; + +error: + kfree(class_dev); + return ERR_PTR(retval); +} + void class_device_del(struct class_device *class_dev) { struct class * parent = class_dev->class; @@ -460,6 +591,11 @@ void class_device_del(struct class_device *class_dev) if (class_dev->dev) sysfs_remove_link(&class_dev->kobj, "device"); + if (class_dev->devt_attr) { + class_device_remove_file(class_dev, class_dev->devt_attr); + kfree(class_dev->devt_attr); + class_dev->devt_attr = NULL; + } class_device_remove_attrs(class_dev); kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE); @@ -477,6 +613,32 @@ void class_device_unregister(struct class_device *class_dev) class_device_put(class_dev); } +/** + * class_device_destroy - removes a class device that was created with class_device_create() + * @cls: the pointer to the struct class that this device was registered * with. + * @dev: the dev_t of the device that was previously registered. + * + * This call unregisters and cleans up a class device that was created with a + * call to class_device_create() + */ +void class_device_destroy(struct class *cls, dev_t devt) +{ + struct class_device *class_dev = NULL; + struct class_device *class_dev_tmp; + + down(&cls->sem); + list_for_each_entry(class_dev_tmp, &cls->children, node) { + if (class_dev_tmp->devt == devt) { + class_dev = class_dev_tmp; + break; + } + } + up(&cls->sem); + + if (class_dev) + class_device_unregister(class_dev); +} + int class_device_rename(struct class_device *class_dev, char *new_name) { int error = 0; @@ -576,6 +738,8 @@ EXPORT_SYMBOL_GPL(class_register); EXPORT_SYMBOL_GPL(class_unregister); EXPORT_SYMBOL_GPL(class_get); EXPORT_SYMBOL_GPL(class_put); +EXPORT_SYMBOL_GPL(class_create); +EXPORT_SYMBOL_GPL(class_destroy); EXPORT_SYMBOL_GPL(class_device_register); EXPORT_SYMBOL_GPL(class_device_unregister); @@ -584,6 +748,8 @@ EXPORT_SYMBOL_GPL(class_device_add); EXPORT_SYMBOL_GPL(class_device_del); EXPORT_SYMBOL_GPL(class_device_get); EXPORT_SYMBOL_GPL(class_device_put); +EXPORT_SYMBOL_GPL(class_device_create); +EXPORT_SYMBOL_GPL(class_device_destroy); EXPORT_SYMBOL_GPL(class_device_create_file); EXPORT_SYMBOL_GPL(class_device_remove_file); EXPORT_SYMBOL_GPL(class_device_create_bin_file); diff --git a/drivers/base/class_simple.c b/drivers/base/class_simple.c deleted file mode 100644 index 27699eb20a3..00000000000 --- a/drivers/base/class_simple.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * class_simple.c - a "simple" interface for classes for simple char devices. - * - * Copyright (c) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> - * Copyright (c) 2003-2004 IBM Corp. - * - * This file is released under the GPLv2 - * - */ - -#include <linux/config.h> -#include <linux/device.h> -#include <linux/err.h> - -struct class_simple { - struct class class; -}; -#define to_class_simple(d) container_of(d, struct class_simple, class) - -struct simple_dev { - struct list_head node; - struct class_device class_dev; -}; -#define to_simple_dev(d) container_of(d, struct simple_dev, class_dev) - -static LIST_HEAD(simple_dev_list); -static DEFINE_SPINLOCK(simple_dev_list_lock); - -static void release_simple_dev(struct class_device *class_dev) -{ - struct simple_dev *s_dev = to_simple_dev(class_dev); - kfree(s_dev); -} - -static void class_simple_release(struct class *class) -{ - struct class_simple *cs = to_class_simple(class); - kfree(cs); -} - -/** - * class_simple_create - create a struct class_simple structure - * @owner: pointer to the module that is to "own" this struct class_simple - * @name: pointer to a string for the name of this class. - * - * This is used to create a struct class_simple pointer that can then be used - * in calls to class_simple_device_add(). This is used when you do not wish to - * create a full blown class support for a type of char devices. - * - * Note, the pointer created here is to be destroyed when finished by making a - * call to class_simple_destroy(). - */ -struct class_simple *class_simple_create(struct module *owner, char *name) -{ - struct class_simple *cs; - int retval; - - cs = kmalloc(sizeof(*cs), GFP_KERNEL); - if (!cs) { - retval = -ENOMEM; - goto error; - } - memset(cs, 0x00, sizeof(*cs)); - - cs->class.name = name; - cs->class.class_release = class_simple_release; - cs->class.release = release_simple_dev; - - retval = class_register(&cs->class); - if (retval) - goto error; - - return cs; - -error: - kfree(cs); - return ERR_PTR(retval); -} -EXPORT_SYMBOL(class_simple_create); - -/** - * class_simple_destroy - destroys a struct class_simple structure - * @cs: pointer to the struct class_simple that is to be destroyed - * - * Note, the pointer to be destroyed must have been created with a call to - * class_simple_create(). - */ -void class_simple_destroy(struct class_simple *cs) -{ - if ((cs == NULL) || (IS_ERR(cs))) - return; - - class_unregister(&cs->class); -} -EXPORT_SYMBOL(class_simple_destroy); - -/** - * class_simple_device_add - adds a class device to sysfs for a character driver - * @cs: pointer to the struct class_simple that this device should be registered to. - * @dev: the dev_t for the device to be added. - * @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 simple char device classes that do not - * implement their own class device registration. A struct class_device will - * be created in sysfs, registered to the specified class. A "dev" file will - * be created, showing the dev_t for the device. The pointer to the struct - * class_device will be returned from the call. Any further sysfs files that - * might be required can be created using this pointer. - * Note: the struct class_simple passed to this function must have previously been - * created with a call to class_simple_create(). - */ -struct class_device *class_simple_device_add(struct class_simple *cs, dev_t dev, struct device *device, const char *fmt, ...) -{ - va_list args; - struct simple_dev *s_dev = NULL; - int retval; - - if ((cs == NULL) || (IS_ERR(cs))) { - retval = -ENODEV; - goto error; - } - - s_dev = kmalloc(sizeof(*s_dev), GFP_KERNEL); - if (!s_dev) { - retval = -ENOMEM; - goto error; - } - memset(s_dev, 0x00, sizeof(*s_dev)); - - s_dev->class_dev.devt = dev; - s_dev->class_dev.dev = device; - s_dev->class_dev.class = &cs->class; - - va_start(args, fmt); - vsnprintf(s_dev->class_dev.class_id, BUS_ID_SIZE, fmt, args); - va_end(args); - retval = class_device_register(&s_dev->class_dev); - if (retval) - goto error; - - spin_lock(&simple_dev_list_lock); - list_add(&s_dev->node, &simple_dev_list); - spin_unlock(&simple_dev_list_lock); - - return &s_dev->class_dev; - -error: - kfree(s_dev); - return ERR_PTR(retval); -} -EXPORT_SYMBOL(class_simple_device_add); - -/** - * class_simple_set_hotplug - set the hotplug callback in the embedded struct class - * @cs: pointer to the struct class_simple to hold the pointer - * @hotplug: function pointer to the hotplug function - * - * Implement and set a hotplug function to add environment variables specific to this - * class on the hotplug event. - */ -int class_simple_set_hotplug(struct class_simple *cs, - int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size)) -{ - if ((cs == NULL) || (IS_ERR(cs))) - return -ENODEV; - cs->class.hotplug = hotplug; - return 0; -} -EXPORT_SYMBOL(class_simple_set_hotplug); - -/** - * class_simple_device_remove - removes a class device that was created with class_simple_device_add() - * @dev: the dev_t of the device that was previously registered. - * - * This call unregisters and cleans up a class device that was created with a - * call to class_device_simple_add() - */ -void class_simple_device_remove(dev_t dev) -{ - struct simple_dev *s_dev = NULL; - int found = 0; - - spin_lock(&simple_dev_list_lock); - list_for_each_entry(s_dev, &simple_dev_list, node) { - if (s_dev->class_dev.devt == dev) { - found = 1; - break; - } - } - if (found) { - list_del(&s_dev->node); - spin_unlock(&simple_dev_list_lock); - class_device_unregister(&s_dev->class_dev); - } else { - spin_unlock(&simple_dev_list_lock); - } -} -EXPORT_SYMBOL(class_simple_device_remove); diff --git a/drivers/base/core.c b/drivers/base/core.c index fbc223486f8..86d79755fbf 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -36,10 +36,10 @@ 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); - ssize_t ret = 0; + ssize_t ret = -EIO; if (dev_attr->show) - ret = dev_attr->show(dev, buf); + ret = dev_attr->show(dev, dev_attr, buf); return ret; } @@ -49,10 +49,10 @@ dev_attr_store(struct kobject * kobj, struct attribute * attr, { struct device_attribute * dev_attr = to_dev_attr(attr); struct device * dev = to_dev(kobj); - ssize_t ret = 0; + ssize_t ret = -EIO; if (dev_attr->store) - ret = dev_attr->store(dev, buf, count); + ret = dev_attr->store(dev, dev_attr, buf, count); return ret; } @@ -102,7 +102,7 @@ static int dev_hotplug_filter(struct kset *kset, struct kobject *kobj) return 0; } -static char *dev_hotplug_name(struct kset *kset, struct kobject *kobj) +static const char *dev_hotplug_name(struct kset *kset, struct kobject *kobj) { struct device *dev = to_dev(kobj); @@ -207,11 +207,9 @@ void device_initialize(struct device *dev) { kobj_set_kset_s(dev, devices_subsys); kobject_init(&dev->kobj); - INIT_LIST_HEAD(&dev->node); - INIT_LIST_HEAD(&dev->children); - INIT_LIST_HEAD(&dev->driver_list); - INIT_LIST_HEAD(&dev->bus_list); + klist_init(&dev->klist_children); INIT_LIST_HEAD(&dev->dma_pools); + init_MUTEX(&dev->sem); } /** @@ -250,10 +248,8 @@ int device_add(struct device *dev) goto PMError; if ((error = bus_add_device(dev))) goto BusError; - down_write(&devices_subsys.rwsem); if (parent) - list_add_tail(&dev->node, &parent->children); - up_write(&devices_subsys.rwsem); + klist_add_tail(&parent->klist_children, &dev->knode_parent); /* notify platform of device entry */ if (platform_notify) @@ -336,10 +332,8 @@ void device_del(struct device * dev) { struct device * parent = dev->parent; - down_write(&devices_subsys.rwsem); if (parent) - list_del_init(&dev->node); - up_write(&devices_subsys.rwsem); + klist_remove(&dev->knode_parent); /* Notify the platform of the removal, in case they * need to do anything... @@ -373,6 +367,12 @@ void device_unregister(struct device * dev) } +static struct device * next_device(struct klist_iter * 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. * @dev: parent struct device. @@ -385,39 +385,20 @@ void device_unregister(struct device * dev) * 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 * dev, void * data, +int device_for_each_child(struct device * parent, void * data, int (*fn)(struct device *, void *)) { + struct klist_iter i; struct device * child; int error = 0; - down_read(&devices_subsys.rwsem); - list_for_each_entry(child, &dev->children, node) { - if((error = fn(child, data))) - break; - } - up_read(&devices_subsys.rwsem); + klist_iter_init(&parent->klist_children, &i); + while ((child = next_device(&i)) && !error) + error = fn(child, data); + klist_iter_exit(&i); return error; } -/** - * device_find - locate device on a bus by name. - * @name: name of the device. - * @bus: bus to scan for the device. - * - * Call kset_find_obj() to iterate over list of devices on - * a bus to find device by name. Return device if found. - * - * Note that kset_find_obj increments device's reference count. - */ -struct device *device_find(const char *name, struct bus_type *bus) -{ - struct kobject *k = kset_find_obj(&bus->devices, name); - if (k) - return to_dev(k); - return NULL; -} - int __init devices_init(void) { return subsystem_register(&devices_subsys); @@ -433,7 +414,6 @@ EXPORT_SYMBOL_GPL(device_del); EXPORT_SYMBOL_GPL(device_unregister); EXPORT_SYMBOL_GPL(get_device); EXPORT_SYMBOL_GPL(put_device); -EXPORT_SYMBOL_GPL(device_find); EXPORT_SYMBOL_GPL(device_create_file); EXPORT_SYMBOL_GPL(device_remove_file); diff --git a/drivers/base/dd.c b/drivers/base/dd.c new file mode 100644 index 00000000000..6db3a789c54 --- /dev/null +++ b/drivers/base/dd.c @@ -0,0 +1,248 @@ +/* + * 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. + * + * 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 + * + * This file is released under the GPLv2 + */ + +#include <linux/device.h> +#include <linux/module.h> + +#include "base.h" +#include "power/power.h" + +#define to_drv(node) container_of(node, struct device_driver, kobj.entry) + + +/** + * 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. + * + * 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. + */ +void device_bind_driver(struct device * dev) +{ + pr_debug("bound device '%s' to driver '%s'\n", + dev->bus_id, dev->driver->name); + klist_add_tail(&dev->driver->klist_devices, &dev->knode_driver); + sysfs_create_link(&dev->driver->kobj, &dev->kobj, + kobject_name(&dev->kobj)); + sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver"); +} + +/** + * driver_probe_device - attempt to bind device & driver. + * @drv: driver. + * @dev: device. + * + * First, we call the bus's match function, if one present, which + * should compare the device IDs the driver supports with the + * device IDs of the device. Note we don't do this ourselves + * because we don't know the format of the ID structures, nor what + * is to be considered a match and what is not. + * + * + * This function returns 1 if a match is found, an error if one + * occurs (that is not -ENODEV or -ENXIO), and 0 otherwise. + * + * This function must be called with @dev->sem held. + */ +static int driver_probe_device(struct device_driver * drv, struct device * dev) +{ + int ret = 0; + + 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); + dev->driver = drv; + if (drv->probe) { + ret = drv->probe(dev); + if (ret) { + dev->driver = NULL; + goto ProbeFailed; + } + } + device_bind_driver(dev); + ret = 1; + pr_debug("%s: Bound Device %s to Driver %s\n", + drv->bus->name, dev->bus_id, drv->name); + goto Done; + + ProbeFailed: + if (ret == -ENODEV || ret == -ENXIO) { + /* Driver matched, but didn't support device + * or device not found. + * Not an error; keep going. + */ + ret = 0; + } else { + /* driver matched but the probe failed */ + printk(KERN_WARNING + "%s: probe of %s failed with error %d\n", + drv->name, dev->bus_id, ret); + } + Done: + return ret; +} + +static int __device_attach(struct device_driver * drv, void * data) +{ + struct device * dev = data; + return driver_probe_device(drv, dev); +} + +/** + * 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. + * + * Returns 1 if the device was bound to a driver; + * 0 if no matching device was found; error code otherwise. + */ +int device_attach(struct device * dev) +{ + int ret = 0; + + down(&dev->sem); + if (dev->driver) { + device_bind_driver(dev); + ret = 1; + } else + ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + up(&dev->sem); + return ret; +} + +static int __driver_attach(struct device * dev, void * data) +{ + struct device_driver * drv = data; + + /* + * Lock device and try to bind to it. We drop the error + * here and always return 0, because we need to keep trying + * to bind to devices and some drivers will return an error + * simply if it didn't support the device. + * + * driver_probe_device() will spit a warning if there + * is an error. + */ + + down(&dev->sem); + if (!dev->driver) + driver_probe_device(drv, dev); + up(&dev->sem); + + + return 0; +} + +/** + * 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. + */ +void driver_attach(struct device_driver * drv) +{ + bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); +} + +/** + * device_release_driver - manually detach device from driver. + * @dev: device. + * + * Manually detach device from driver. + * + * __device_release_driver() must be called with @dev->sem held. + */ + +static void __device_release_driver(struct device * dev) +{ + struct device_driver * drv; + + drv = dev->driver; + if (drv) { + get_driver(drv); + sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + sysfs_remove_link(&dev->kobj, "driver"); + klist_remove(&dev->knode_driver); + + if (drv->remove) + drv->remove(dev); + dev->driver = NULL; + put_driver(drv); + } +} + +void device_release_driver(struct device * dev) +{ + /* + * If anyone calls device_release_driver() recursively from + * within their ->remove callback for the same device, they + * will deadlock right here. + */ + down(&dev->sem); + __device_release_driver(dev); + up(&dev->sem); +} + + +/** + * driver_detach - detach driver from all devices it controls. + * @drv: driver. + */ +void driver_detach(struct device_driver * drv) +{ + struct device * dev; + + for (;;) { + spin_lock_irq(&drv->klist_devices.k_lock); + if (list_empty(&drv->klist_devices.k_list)) { + spin_unlock_irq(&drv->klist_devices.k_lock); + break; + } + dev = list_entry(drv->klist_devices.k_list.prev, + struct device, knode_driver.n_node); + get_device(dev); + spin_unlock_irq(&drv->klist_devices.k_lock); + + down(&dev->sem); + if (dev->driver == drv) + __device_release_driver(dev); + up(&dev->sem); + 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 f48833df61a..c4aebf2f522 100644 --- a/drivers/base/dmapool.c +++ b/drivers/base/dmapool.c @@ -41,7 +41,7 @@ struct dma_page { /* cacheable header for 'allocation' bytes */ static DECLARE_MUTEX (pools_lock); static ssize_t -show_pools (struct device *dev, char *buf) +show_pools (struct device *dev, struct device_attribute *attr, char *buf) { unsigned temp; unsigned size; diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 3b269f7e521..1b645886e9e 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -18,6 +18,43 @@ #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) +{ + 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. + * @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. + */ + +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; + int error = 0; + + if (!drv) + return -EINVAL; + + klist_iter_init_node(&drv->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_create_file - create sysfs file for driver. * @drv: driver. @@ -85,7 +122,7 @@ void put_driver(struct device_driver * drv) */ int driver_register(struct device_driver * drv) { - INIT_LIST_HEAD(&drv->devices); + klist_init(&drv->klist_devices); init_completion(&drv->unloaded); return bus_add_driver(drv); } diff --git a/drivers/base/node.c b/drivers/base/node.c index 583d57ec49a..904b27caf69 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -87,7 +87,7 @@ static ssize_t node_read_numastat(struct sys_device * dev, char * buf) for (i = 0; i < MAX_NR_ZONES; i++) { struct zone *z = &pg->node_zones[i]; for (cpu = 0; cpu < NR_CPUS; cpu++) { - struct per_cpu_pageset *ps = &z->pageset[cpu]; + struct per_cpu_pageset *ps = zone_pcp(z,cpu); numa_hit += ps->numa_hit; numa_miss += ps->numa_miss; numa_foreign += ps->numa_foreign; @@ -136,7 +136,7 @@ static SYSDEV_ATTR(distance, S_IRUGO, node_read_distance, NULL); * * Initialize and register the node device. */ -int __init register_node(struct node *node, int num, struct node *parent) +int register_node(struct node *node, int num, struct node *parent) { int error; @@ -153,8 +153,24 @@ int __init register_node(struct node *node, int num, struct node *parent) return error; } +/** + * unregister_node - unregister a node device + * @node: node going away + * + * Unregisters a node device @node. All the devices on the node must be + * unregistered before calling this function. + */ +void unregister_node(struct node *node) +{ + sysdev_remove_file(&node->sysdev, &attr_cpumap); + sysdev_remove_file(&node->sysdev, &attr_meminfo); + sysdev_remove_file(&node->sysdev, &attr_numastat); + sysdev_remove_file(&node->sysdev, &attr_distance); + + sysdev_unregister(&node->sysdev); +} -int __init register_node_type(void) +static int __init register_node_type(void) { return sysdev_class_register(&node_class); } diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 26468971ef5..bdd96b03b88 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -22,6 +22,9 @@ extern int sysdev_resume(void); int resume_device(struct device * dev) { + int error = 0; + + down(&dev->sem); if (dev->power.pm_parent && dev->power.pm_parent->power.power_state) { dev_err(dev, "PM: resume from %d, parent %s still %d\n", @@ -31,9 +34,10 @@ int resume_device(struct device * dev) } if (dev->bus && dev->bus->resume) { dev_dbg(dev,"resuming\n"); - return dev->bus->resume(dev); + error = dev->bus->resume(dev); } - return 0; + up(&dev->sem); + return error; } diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 0ec44ef840b..2ccee3763ac 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -39,6 +39,7 @@ int suspend_device(struct device * dev, pm_message_t state) { int error = 0; + down(&dev->sem); if (dev->power.power_state) { dev_dbg(dev, "PM: suspend %d-->%d\n", dev->power.power_state, state); @@ -58,7 +59,7 @@ int suspend_device(struct device * dev, pm_message_t state) dev_dbg(dev, "suspending\n"); error = dev->bus->suspend(dev, state); } - + up(&dev->sem); return error; } @@ -113,8 +114,19 @@ int device_suspend(pm_message_t state) put_device(dev); } up(&dpm_list_sem); - if (error) + if (error) { + /* we failed... before resuming, bring back devices from + * dpm_off_irq list back to main dpm_off list, we do want + * to call resume() on them, in case they partially suspended + * despite returning -EAGAIN + */ + while (!list_empty(&dpm_off_irq)) { + struct list_head * entry = dpm_off_irq.next; + list_del(entry); + list_add(entry, &dpm_off); + } dpm_resume(); + } up(&dpm_sem); return error; } diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 6ac96349a8e..f82b3df9545 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -24,12 +24,12 @@ * low-power state. */ -static ssize_t state_show(struct device * dev, char * buf) +static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf) { return sprintf(buf, "%u\n", dev->power.power_state); } -static ssize_t state_store(struct device * dev, const char * buf, size_t n) +static ssize_t state_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t n) { u32 state; char * rest; diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 9102e3756f9..f37a13de804 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -37,7 +37,7 @@ sysdev_show(struct kobject * kobj, struct attribute * attr, char * buffer) if (sysdev_attr->show) return sysdev_attr->show(sysdev, buffer); - return 0; + return -EIO; } @@ -50,7 +50,7 @@ sysdev_store(struct kobject * kobj, struct attribute * attr, if (sysdev_attr->store) return sysdev_attr->store(sysdev, buffer, count); - return 0; + return -EIO; } static struct sysfs_ops sysfs_ops = { |