summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/base
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/Kconfig40
-rw-r--r--drivers/base/Makefile14
-rw-r--r--drivers/base/attribute_container.c376
-rw-r--r--drivers/base/base.h18
-rw-r--r--drivers/base/bus.c770
-rw-r--r--drivers/base/class.c591
-rw-r--r--drivers/base/class_simple.c199
-rw-r--r--drivers/base/core.c439
-rw-r--r--drivers/base/cpu.c104
-rw-r--r--drivers/base/dmapool.c414
-rw-r--r--drivers/base/driver.c138
-rw-r--r--drivers/base/firmware.c34
-rw-r--r--drivers/base/firmware_class.c583
-rw-r--r--drivers/base/init.c43
-rw-r--r--drivers/base/interface.c51
-rw-r--r--drivers/base/map.c155
-rw-r--r--drivers/base/node.c161
-rw-r--r--drivers/base/platform.c350
-rw-r--r--drivers/base/power/Makefile6
-rw-r--r--drivers/base/power/main.c99
-rw-r--r--drivers/base/power/power.h106
-rw-r--r--drivers/base/power/resume.c112
-rw-r--r--drivers/base/power/runtime.c81
-rw-r--r--drivers/base/power/shutdown.c67
-rw-r--r--drivers/base/power/suspend.c144
-rw-r--r--drivers/base/power/sysfs.c68
-rw-r--r--drivers/base/sys.c397
-rw-r--r--drivers/base/transport_class.c275
28 files changed, 5835 insertions, 0 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
new file mode 100644
index 000000000000..934149c1512b
--- /dev/null
+++ b/drivers/base/Kconfig
@@ -0,0 +1,40 @@
+menu "Generic Driver Options"
+
+config STANDALONE
+ bool "Select only drivers that don't need compile-time external firmware" if EXPERIMENTAL
+ default y
+ help
+ Select this option if you don't have magic firmware for drivers that
+ need it.
+
+ If unsure, say Y.
+
+config PREVENT_FIRMWARE_BUILD
+ bool "Prevent firmware from being built"
+ default y
+ help
+ Say yes to avoid building firmware. Firmware is usually shipped
+ with the driver, and only when updating the firmware a rebuild
+ should be made.
+ If unsure say Y here.
+
+config FW_LOADER
+ tristate "Hotplug firmware loading support"
+ select HOTPLUG
+ ---help---
+ This option is provided for the case where no in-kernel-tree modules
+ require hotplug firmware loading support, but a module built outside
+ the kernel tree does.
+
+config DEBUG_DRIVER
+ bool "Driver Core verbose debug messages"
+ depends on DEBUG_KERNEL
+ help
+ Say Y here if you want the Driver core to produce a bunch of
+ debug messages to the system log. Select this if you are having a
+ problem with the driver core and want to see more of what is
+ going on.
+
+ If you are unsure about this, say N here.
+
+endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
new file mode 100644
index 000000000000..6662b545e0a9
--- /dev/null
+++ b/drivers/base/Makefile
@@ -0,0 +1,14 @@
+# Makefile for the Linux device tree
+
+obj-y := core.o sys.o interface.o bus.o \
+ driver.o class.o class_simple.o platform.o \
+ cpu.o firmware.o init.o map.o dmapool.o \
+ attribute_container.o transport_class.o
+obj-y += power/
+obj-$(CONFIG_FW_LOADER) += firmware_class.o
+obj-$(CONFIG_NUMA) += node.o
+
+ifeq ($(CONFIG_DEBUG_DRIVER),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
new file mode 100644
index 000000000000..ec615d854be9
--- /dev/null
+++ b/drivers/base/attribute_container.c
@@ -0,0 +1,376 @@
+/*
+ * attribute_container.c - implementation of a simple container for classes
+ *
+ * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
+ *
+ * This file is licensed under GPLv2
+ *
+ * The basic idea here is to enable a device to be attached to an
+ * aritrary numer of classes without having to allocate storage for them.
+ * Instead, the contained classes select the devices they need to attach
+ * to via a matching function.
+ */
+
+#include <linux/attribute_container.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+/* This is a private structure used to tie the classdev and the
+ * container .. it should never be visible outside this file */
+struct internal_container {
+ struct list_head node;
+ struct attribute_container *cont;
+ struct class_device classdev;
+};
+
+/**
+ * attribute_container_classdev_to_container - given a classdev, return the container
+ *
+ * @classdev: the class device created by attribute_container_add_device.
+ *
+ * Returns the container associated with this classdev.
+ */
+struct attribute_container *
+attribute_container_classdev_to_container(struct class_device *classdev)
+{
+ struct internal_container *ic =
+ container_of(classdev, struct internal_container, classdev);
+ return ic->cont;
+}
+EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container);
+
+static struct list_head attribute_container_list;
+
+static DECLARE_MUTEX(attribute_container_mutex);
+
+/**
+ * attribute_container_register - register an attribute container
+ *
+ * @cont: The container to register. This must be allocated by the
+ * callee and should also be zeroed by it.
+ */
+int
+attribute_container_register(struct attribute_container *cont)
+{
+ INIT_LIST_HEAD(&cont->node);
+ INIT_LIST_HEAD(&cont->containers);
+
+ down(&attribute_container_mutex);
+ list_add_tail(&cont->node, &attribute_container_list);
+ up(&attribute_container_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(attribute_container_register);
+
+/**
+ * attribute_container_unregister - remove a container registration
+ *
+ * @cont: previously registered container to remove
+ */
+int
+attribute_container_unregister(struct attribute_container *cont)
+{
+ int retval = -EBUSY;
+ down(&attribute_container_mutex);
+ if (!list_empty(&cont->containers))
+ goto out;
+ retval = 0;
+ list_del(&cont->node);
+ out:
+ up(&attribute_container_mutex);
+ return retval;
+
+}
+EXPORT_SYMBOL_GPL(attribute_container_unregister);
+
+/* private function used as class release */
+static void attribute_container_release(struct class_device *classdev)
+{
+ struct internal_container *ic
+ = container_of(classdev, struct internal_container, classdev);
+ struct device *dev = classdev->dev;
+
+ kfree(ic);
+ put_device(dev);
+}
+
+/**
+ * attribute_container_add_device - see if any container is interested in dev
+ *
+ * @dev: device to add attributes to
+ * @fn: function to trigger addition of class device.
+ *
+ * This function allocates storage for the class device(s) to be
+ * attached to dev (one for each matching attribute_container). If no
+ * fn is provided, the code will simply register the class device via
+ * class_device_add. If a function is provided, it is expected to add
+ * the class device at the appropriate time. One of the things that
+ * might be necessary is to allocate and initialise the classdev and
+ * then add it a later time. To do this, call this routine for
+ * allocation and initialisation and then use
+ * attribute_container_device_trigger() to call class_device_add() on
+ * it. Note: after this, the class device contains a reference to dev
+ * which is not relinquished until the release of the classdev.
+ */
+void
+attribute_container_add_device(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *,
+ struct class_device *))
+{
+ struct attribute_container *cont;
+
+ down(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ struct internal_container *ic;
+
+ if (attribute_container_no_classdevs(cont))
+ continue;
+
+ if (!cont->match(cont, dev))
+ continue;
+ ic = kmalloc(sizeof(struct internal_container), GFP_KERNEL);
+ if (!ic) {
+ dev_printk(KERN_ERR, dev, "failed to allocate class container\n");
+ continue;
+ }
+ memset(ic, 0, sizeof(struct internal_container));
+ INIT_LIST_HEAD(&ic->node);
+ ic->cont = cont;
+ class_device_initialize(&ic->classdev);
+ ic->classdev.dev = get_device(dev);
+ ic->classdev.class = cont->class;
+ cont->class->release = attribute_container_release;
+ strcpy(ic->classdev.class_id, dev->bus_id);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else
+ attribute_container_add_class_device(&ic->classdev);
+ list_add_tail(&ic->node, &cont->containers);
+ }
+ up(&attribute_container_mutex);
+}
+
+/**
+ * attribute_container_remove_device - make device eligible for removal.
+ *
+ * @dev: The generic device
+ * @fn: A function to call to remove the device
+ *
+ * This routine triggers device removal. If fn is NULL, then it is
+ * simply done via class_device_unregister (note that if something
+ * still has a reference to the classdev, then the memory occupied
+ * will not be freed until the classdev is released). If you want a
+ * two phase release: remove from visibility and then delete the
+ * device, then you should use this routine with a fn that calls
+ * class_device_del() and then use
+ * attribute_container_device_trigger() to do the final put on the
+ * classdev.
+ */
+void
+attribute_container_remove_device(struct device *dev,
+ void (*fn)(struct attribute_container *,
+ struct device *,
+ struct class_device *))
+{
+ struct attribute_container *cont;
+
+ down(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ struct internal_container *ic, *tmp;
+
+ if (attribute_container_no_classdevs(cont))
+ continue;
+
+ if (!cont->match(cont, dev))
+ continue;
+ list_for_each_entry_safe(ic, tmp, &cont->containers, node) {
+ if (dev != ic->classdev.dev)
+ continue;
+ list_del(&ic->node);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else {
+ attribute_container_remove_attrs(&ic->classdev);
+ class_device_unregister(&ic->classdev);
+ }
+ }
+ }
+ up(&attribute_container_mutex);
+}
+EXPORT_SYMBOL_GPL(attribute_container_remove_device);
+
+/**
+ * attribute_container_device_trigger - execute a trigger for each matching classdev
+ *
+ * @dev: The generic device to run the trigger for
+ * @fn the function to execute for each classdev.
+ *
+ * This funcion is for executing a trigger when you need to know both
+ * the container and the classdev. If you only care about the
+ * container, then use attribute_container_trigger() instead.
+ */
+void
+attribute_container_device_trigger(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *,
+ struct class_device *))
+{
+ struct attribute_container *cont;
+
+ down(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ struct internal_container *ic, *tmp;
+
+ if (!cont->match(cont, dev))
+ continue;
+
+ list_for_each_entry_safe(ic, tmp, &cont->containers, node) {
+ if (dev == ic->classdev.dev)
+ fn(cont, dev, &ic->classdev);
+ }
+ }
+ up(&attribute_container_mutex);
+}
+EXPORT_SYMBOL_GPL(attribute_container_device_trigger);
+
+/**
+ * attribute_container_trigger - trigger a function for each matching container
+ *
+ * @dev: The generic device to activate the trigger for
+ * @fn: the function to trigger
+ *
+ * This routine triggers a function that only needs to know the
+ * matching containers (not the classdev) associated with a device.
+ * It is more lightweight than attribute_container_device_trigger, so
+ * should be used in preference unless the triggering function
+ * actually needs to know the classdev.
+ */
+void
+attribute_container_trigger(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *))
+{
+ struct attribute_container *cont;
+
+ down(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ if (cont->match(cont, dev))
+ fn(cont, dev);
+ }
+ up(&attribute_container_mutex);
+}
+EXPORT_SYMBOL_GPL(attribute_container_trigger);
+
+/**
+ * attribute_container_add_attrs - add attributes
+ *
+ * @classdev: The class device
+ *
+ * This simply creates all the class device sysfs files from the
+ * attributes listed in the container
+ */
+int
+attribute_container_add_attrs(struct class_device *classdev)
+{
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+ struct class_device_attribute **attrs = cont->attrs;
+ int i, error;
+
+ if (!attrs)
+ return 0;
+
+ for (i = 0; attrs[i]; i++) {
+ error = class_device_create_file(classdev, attrs[i]);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(attribute_container_add_attrs);
+
+/**
+ * attribute_container_add_class_device - same function as class_device_add
+ *
+ * @classdev: the class device to add
+ *
+ * This performs essentially the same function as class_device_add except for
+ * attribute containers, namely add the classdev to the system and then
+ * create the attribute files
+ */
+int
+attribute_container_add_class_device(struct class_device *classdev)
+{
+ int error = class_device_add(classdev);
+ if (error)
+ return error;
+ return attribute_container_add_attrs(classdev);
+}
+EXPORT_SYMBOL_GPL(attribute_container_add_class_device);
+
+/**
+ * attribute_container_add_class_device_adapter - simple adapter for triggers
+ *
+ * This function is identical to attribute_container_add_class_device except
+ * that it is designed to be called from the triggers
+ */
+int
+attribute_container_add_class_device_adapter(struct attribute_container *cont,
+ struct device *dev,
+ struct class_device *classdev)
+{
+ return attribute_container_add_class_device(classdev);
+}
+EXPORT_SYMBOL_GPL(attribute_container_add_class_device_adapter);
+
+/**
+ * attribute_container_remove_attrs - remove any attribute files
+ *
+ * @classdev: The class device to remove the files from
+ *
+ */
+void
+attribute_container_remove_attrs(struct class_device *classdev)
+{
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+ struct class_device_attribute **attrs = cont->attrs;
+ int i;
+
+ if (!attrs)
+ return;
+
+ for (i = 0; attrs[i]; i++)
+ class_device_remove_file(classdev, attrs[i]);
+}
+EXPORT_SYMBOL_GPL(attribute_container_remove_attrs);
+
+/**
+ * attribute_container_class_device_del - equivalent of class_device_del
+ *
+ * @classdev: the class device
+ *
+ * This function simply removes all the attribute files and then calls
+ * class_device_del.
+ */
+void
+attribute_container_class_device_del(struct class_device *classdev)
+{
+ attribute_container_remove_attrs(classdev);
+ class_device_del(classdev);
+}
+EXPORT_SYMBOL_GPL(attribute_container_class_device_del);
+
+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
new file mode 100644
index 000000000000..8d1e8bd48632
--- /dev/null
+++ b/drivers/base/base.h
@@ -0,0 +1,18 @@
+extern int bus_add_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 *);
+
+static inline struct class_device *to_class_dev(struct kobject *obj)
+{
+ return container_of(obj, struct class_device, kobj);
+}
+
+static inline
+struct class_device_attribute *to_class_dev_attr(struct attribute *_attr)
+{
+ return container_of(_attr, struct class_device_attribute, attr);
+}
+
+
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
new file mode 100644
index 000000000000..f4fa27315fb4
--- /dev/null
+++ b/drivers/base/bus.c
@@ -0,0 +1,770 @@
+/*
+ * bus.c - bus driver management
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#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)
+
+/*
+ * 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 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);
+ ssize_t ret = 0;
+
+ if (drv_attr->show)
+ ret = drv_attr->show(drv, buf);
+ return ret;
+}
+
+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);
+ ssize_t ret = 0;
+
+ if (drv_attr->store)
+ ret = drv_attr->store(drv, buf, count);
+ return ret;
+}
+
+static struct sysfs_ops driver_sysfs_ops = {
+ .show = drv_attr_show,
+ .store = drv_attr_store,
+};
+
+
+static void driver_release(struct kobject * kobj)
+{
+ struct device_driver * drv = to_driver(kobj);
+ complete(&drv->unloaded);
+}
+
+static struct kobj_type ktype_driver = {
+ .sysfs_ops = &driver_sysfs_ops,
+ .release = driver_release,
+};
+
+
+/*
+ * sysfs bindings for buses
+ */
+
+
+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);
+ ssize_t ret = 0;
+
+ if (bus_attr->show)
+ ret = bus_attr->show(bus, buf);
+ return ret;
+}
+
+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);
+ ssize_t ret = 0;
+
+ if (bus_attr->store)
+ ret = bus_attr->store(bus, buf, count);
+ return ret;
+}
+
+static struct sysfs_ops bus_sysfs_ops = {
+ .show = bus_attr_show,
+ .store = bus_attr_store,
+};
+
+int bus_create_file(struct bus_type * bus, struct bus_attribute * attr)
+{
+ int error;
+ if (get_bus(bus)) {
+ error = sysfs_create_file(&bus->subsys.kset.kobj, &attr->attr);
+ put_bus(bus);
+ } else
+ error = -EINVAL;
+ return error;
+}
+
+void bus_remove_file(struct bus_type * bus, struct bus_attribute * attr)
+{
+ if (get_bus(bus)) {
+ sysfs_remove_file(&bus->subsys.kset.kobj, &attr->attr);
+ put_bus(bus);
+ }
+}
+
+static struct kobj_type ktype_bus = {
+ .sysfs_ops = &bus_sysfs_ops,
+
+};
+
+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 *))
+{
+ 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;
+}
+
+/**
+ * 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.
+ *
+ * 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.
+ */
+
+int bus_for_each_dev(struct bus_type * bus, struct device * start,
+ void * data, int (*fn)(struct device *, void *))
+{
+ int ret;
+
+ down_read(&bus->subsys.rwsem);
+ ret = __bus_for_each_dev(bus, start, data, fn);
+ up_read(&bus->subsys.rwsem);
+ return ret;
+}
+
+/**
+ * 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.
+ *
+ * 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 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);
+ device_detach_shutdown(dev);
+ if (drv->remove)
+ drv->remove(dev);
+ dev->driver = NULL;
+ }
+}
+
+
+/**
+ * driver_detach - detach driver from all devices it controls.
+ * @drv: driver.
+ */
+
+static void driver_detach(struct device_driver * drv)
+{
+ struct list_head * entry, * next;
+ list_for_each_safe(entry, next, &drv->devices) {
+ struct device * dev = container_of(entry, struct device, driver_list);
+ device_release_driver(dev);
+ }
+}
+
+static int device_add_attrs(struct bus_type * bus, struct device * dev)
+{
+ int error = 0;
+ int i;
+
+ if (bus->dev_attrs) {
+ for (i = 0; attr_name(bus->dev_attrs[i]); i++) {
+ error = device_create_file(dev,&bus->dev_attrs[i]);
+ if (error)
+ goto Err;
+ }
+ }
+ Done:
+ return error;
+ Err:
+ while (--i >= 0)
+ device_remove_file(dev,&bus->dev_attrs[i]);
+ goto Done;
+}
+
+
+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]);
+ }
+}
+
+
+/**
+ * bus_add_device - add device to bus
+ * @dev: device being added
+ *
+ * - Add the device to its bus's list of devices.
+ * - Try to attach to driver.
+ * - Create link to device's physical location.
+ */
+int bus_add_device(struct device * dev)
+{
+ struct bus_type * bus = get_bus(dev->bus);
+ 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");
+ }
+ return error;
+}
+
+/**
+ * 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().
+ */
+void bus_remove_device(struct device * dev)
+{
+ if (dev->bus) {
+ 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);
+ 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);
+ }
+}
+
+static int driver_add_attrs(struct bus_type * bus, struct device_driver * drv)
+{
+ int error = 0;
+ int i;
+
+ if (bus->drv_attrs) {
+ for (i = 0; attr_name(bus->drv_attrs[i]); i++) {
+ error = driver_create_file(drv, &bus->drv_attrs[i]);
+ if (error)
+ goto Err;
+ }
+ }
+ Done:
+ return error;
+ Err:
+ while (--i >= 0)
+ driver_remove_file(drv, &bus->drv_attrs[i]);
+ goto Done;
+}
+
+
+static void driver_remove_attrs(struct bus_type * bus, struct device_driver * drv)
+{
+ int i;
+
+ if (bus->drv_attrs) {
+ for (i = 0; attr_name(bus->drv_attrs[i]); i++)
+ driver_remove_file(drv, &bus->drv_attrs[i]);
+ }
+}
+
+
+/**
+ * bus_add_driver - Add a driver to the bus.
+ * @drv: driver.
+ *
+ */
+int bus_add_driver(struct device_driver * drv)
+{
+ struct bus_type * bus = get_bus(drv->bus);
+ int error = 0;
+
+ if (bus) {
+ pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
+ error = kobject_set_name(&drv->kobj, "%s", drv->name);
+ if (error) {
+ put_bus(bus);
+ return error;
+ }
+ drv->kobj.kset = &bus->drivers;
+ if ((error = kobject_register(&drv->kobj))) {
+ put_bus(bus);
+ return error;
+ }
+
+ down_write(&bus->subsys.rwsem);
+ driver_attach(drv);
+ up_write(&bus->subsys.rwsem);
+ module_add_driver(drv->owner, drv);
+
+ driver_add_attrs(bus, drv);
+ }
+ return error;
+}
+
+
+/**
+ * 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().
+ */
+
+void bus_remove_driver(struct device_driver * drv)
+{
+ if (drv->bus) {
+ driver_remove_attrs(drv->bus, drv);
+ down_write(&drv->bus->subsys.rwsem);
+ 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);
+ }
+}
+
+
+/* Helper for bus_rescan_devices's iter */
+static int bus_rescan_devices_helper(struct device *dev, void *data)
+{
+ int *count = data;
+
+ if (!dev->driver && device_attach(dev))
+ (*count)++;
+
+ return 0;
+}
+
+
+/**
+ * bus_rescan_devices - rescan devices on the bus for possible drivers
+ * @bus: the bus to scan.
+ *
+ * This function will look for devices on the bus with no driver
+ * attached and rescan it against existing drivers to see if it
+ * matches any. Calls device_attach(). Returns the number of devices
+ * that were sucessfully bound to a driver.
+ */
+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);
+
+ return count;
+}
+
+
+struct bus_type * get_bus(struct bus_type * bus)
+{
+ return bus ? container_of(subsys_get(&bus->subsys), struct bus_type, subsys) : NULL;
+}
+
+void put_bus(struct bus_type * bus)
+{
+ subsys_put(&bus->subsys);
+}
+
+
+/**
+ * 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.
+ *
+ * Note that kset_find_obj increments bus' reference count.
+ */
+
+struct bus_type * find_bus(char * name)
+{
+ struct kobject * k = kset_find_obj(&bus_subsys.kset, name);
+ return k ? to_bus(k) : NULL;
+}
+
+
+/**
+ * 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)
+{
+ int error = 0;
+ int i;
+
+ if (bus->bus_attrs) {
+ for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
+ if ((error = bus_create_file(bus,&bus->bus_attrs[i])))
+ goto Err;
+ }
+ }
+ Done:
+ return error;
+ Err:
+ while (--i >= 0)
+ bus_remove_file(bus,&bus->bus_attrs[i]);
+ goto Done;
+}
+
+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_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.
+ */
+int bus_register(struct bus_type * bus)
+{
+ int retval;
+
+ retval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name);
+ if (retval)
+ goto out;
+
+ subsys_set_kset(bus, bus_subsys);
+ retval = subsystem_register(&bus->subsys);
+ if (retval)
+ goto out;
+
+ kobject_set_name(&bus->devices.kobj, "devices");
+ bus->devices.subsys = &bus->subsys;
+ retval = kset_register(&bus->devices);
+ if (retval)
+ goto bus_devices_fail;
+
+ kobject_set_name(&bus->drivers.kobj, "drivers");
+ bus->drivers.subsys = &bus->subsys;
+ bus->drivers.ktype = &ktype_driver;
+ retval = kset_register(&bus->drivers);
+ if (retval)
+ goto bus_drivers_fail;
+ bus_add_attrs(bus);
+
+ pr_debug("bus type '%s' registered\n", bus->name);
+ return 0;
+
+bus_drivers_fail:
+ kset_unregister(&bus->devices);
+bus_devices_fail:
+ subsystem_unregister(&bus->subsys);
+out:
+ return retval;
+}
+
+
+/**
+ * bus_unregister - remove a bus from the system
+ * @bus: bus.
+ *
+ * Unregister the child subsystems and the bus itself.
+ * Finally, we call put_bus() to release the refcount
+ */
+void bus_unregister(struct bus_type * bus)
+{
+ pr_debug("bus %s: unregistering\n", bus->name);
+ bus_remove_attrs(bus);
+ kset_unregister(&bus->drivers);
+ kset_unregister(&bus->devices);
+ subsystem_unregister(&bus->subsys);
+}
+
+int __init buses_init(void)
+{
+ return subsystem_register(&bus_subsys);
+}
+
+
+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);
+EXPORT_SYMBOL_GPL(bus_unregister);
+EXPORT_SYMBOL_GPL(bus_rescan_devices);
+EXPORT_SYMBOL_GPL(get_bus);
+EXPORT_SYMBOL_GPL(put_bus);
+EXPORT_SYMBOL_GPL(find_bus);
+
+EXPORT_SYMBOL_GPL(bus_create_file);
+EXPORT_SYMBOL_GPL(bus_remove_file);
diff --git a/drivers/base/class.c b/drivers/base/class.c
new file mode 100644
index 000000000000..6bf650fce78c
--- /dev/null
+++ b/drivers/base/class.c
@@ -0,0 +1,591 @@
+/*
+ * class.c - basic device class management
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2003-2004 Greg Kroah-Hartman
+ * Copyright (c) 2003-2004 IBM Corp.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/kdev_t.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.kset.kobj)
+
+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);
+ ssize_t ret = 0;
+
+ if (class_attr->show)
+ ret = class_attr->show(dc, buf);
+ return ret;
+}
+
+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);
+ ssize_t ret = 0;
+
+ if (class_attr->store)
+ ret = class_attr->store(dc, buf, count);
+ return ret;
+}
+
+static void class_release(struct kobject * kobj)
+{
+ struct class *class = to_class(kobj);
+
+ pr_debug("class '%s': release.\n", class->name);
+
+ if (class->class_release)
+ class->class_release(class);
+ else
+ pr_debug("class '%s' does not have a release() function, "
+ "be careful\n", class->name);
+}
+
+static struct sysfs_ops class_sysfs_ops = {
+ .show = class_attr_show,
+ .store = class_attr_store,
+};
+
+static struct kobj_type ktype_class = {
+ .sysfs_ops = &class_sysfs_ops,
+ .release = class_release,
+};
+
+/* Hotplug events for classes go to the class_obj subsys */
+static decl_subsys(class, &ktype_class, NULL);
+
+
+int class_create_file(struct class * cls, const struct class_attribute * attr)
+{
+ int error;
+ if (cls) {
+ error = sysfs_create_file(&cls->subsys.kset.kobj, &attr->attr);
+ } else
+ error = -EINVAL;
+ return error;
+}
+
+void class_remove_file(struct class * cls, const struct class_attribute * attr)
+{
+ if (cls)
+ sysfs_remove_file(&cls->subsys.kset.kobj, &attr->attr);
+}
+
+struct class * class_get(struct class * cls)
+{
+ if (cls)
+ return container_of(subsys_get(&cls->subsys), struct class, subsys);
+ return NULL;
+}
+
+void class_put(struct class * cls)
+{
+ subsys_put(&cls->subsys);
+}
+
+
+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]);
+ if (error)
+ goto Err;
+ }
+ }
+ Done:
+ return error;
+ Err:
+ while (--i >= 0)
+ class_remove_file(cls,&cls->class_attrs[i]);
+ goto Done;
+}
+
+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]);
+ }
+}
+
+int class_register(struct class * cls)
+{
+ int error;
+
+ pr_debug("device class '%s': registering\n", cls->name);
+
+ INIT_LIST_HEAD(&cls->children);
+ INIT_LIST_HEAD(&cls->interfaces);
+ init_MUTEX(&cls->sem);
+ error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
+ if (error)
+ return error;
+
+ subsys_set_kset(cls, class_subsys);
+
+ error = subsystem_register(&cls->subsys);
+ if (!error) {
+ error = add_class_attrs(class_get(cls));
+ class_put(cls);
+ }
+ return error;
+}
+
+void class_unregister(struct class * cls)
+{
+ pr_debug("device class '%s': unregistering\n", cls->name);
+ remove_class_attrs(cls);
+ subsystem_unregister(&cls->subsys);
+}
+
+
+/* Class Device Stuff */
+
+int class_device_create_file(struct class_device * class_dev,
+ const struct class_device_attribute * attr)
+{
+ int error = -EINVAL;
+ if (class_dev)
+ error = sysfs_create_file(&class_dev->kobj, &attr->attr);
+ return error;
+}
+
+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);
+}
+
+int class_device_create_bin_file(struct class_device *class_dev,
+ struct bin_attribute *attr)
+{
+ int error = -EINVAL;
+ if (class_dev)
+ error = sysfs_create_bin_file(&class_dev->kobj, attr);
+ return error;
+}
+
+void class_device_remove_bin_file(struct class_device *class_dev,
+ struct bin_attribute *attr)
+{
+ if (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)
+{
+ 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)
+ ret = class_dev_attr->show(cd, buf);
+ return ret;
+}
+
+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);
+ ssize_t ret = 0;
+
+ if (class_dev_attr->store)
+ ret = class_dev_attr->store(cd, buf, count);
+ return ret;
+}
+
+static struct sysfs_ops class_dev_sysfs_ops = {
+ .show = class_device_attr_show,
+ .store = class_device_attr_store,
+};
+
+static void class_dev_release(struct kobject * kobj)
+{
+ struct class_device *cd = to_class_dev(kobj);
+ struct class * cls = cd->class;
+
+ pr_debug("device class '%s': release.\n", cd->class_id);
+
+ if (cls->release)
+ cls->release(cd);
+ else {
+ printk(KERN_ERR "Device class '%s' does not have a release() function, "
+ "it is broken and must be fixed.\n",
+ cd->class_id);
+ WARN_ON(1);
+ }
+}
+
+static struct kobj_type ktype_class_device = {
+ .sysfs_ops = &class_dev_sysfs_ops,
+ .release = class_dev_release,
+};
+
+static int class_hotplug_filter(struct kset *kset, struct kobject *kobj)
+{
+ struct kobj_type *ktype = get_ktype(kobj);
+
+ if (ktype == &ktype_class_device) {
+ struct class_device *class_dev = to_class_dev(kobj);
+ if (class_dev->class)
+ return 1;
+ }
+ return 0;
+}
+
+static char *class_hotplug_name(struct kset *kset, struct kobject *kobj)
+{
+ struct class_device *class_dev = to_class_dev(kobj);
+
+ return class_dev->class->name;
+}
+
+static int class_hotplug(struct kset *kset, struct kobject *kobj, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ struct class_device *class_dev = to_class_dev(kobj);
+ int i = 0;
+ int length = 0;
+ int retval = 0;
+
+ pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
+
+ if (class_dev->dev) {
+ /* add physical device, backing this device */
+ struct device *dev = class_dev->dev;
+ char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);
+
+ add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size,
+ &length, "PHYSDEVPATH=%s", path);
+ kfree(path);
+
+ if (dev->bus)
+ add_hotplug_env_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "PHYSDEVBUS=%s", dev->bus->name);
+
+ if (dev->driver)
+ add_hotplug_env_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "PHYSDEVDRIVER=%s", dev->driver->name);
+ }
+
+ if (MAJOR(class_dev->devt)) {
+ add_hotplug_env_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MAJOR=%u", MAJOR(class_dev->devt));
+
+ add_hotplug_env_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MINOR=%u", MINOR(class_dev->devt));
+ }
+
+ /* terminate, set to next free slot, shrink available space */
+ envp[i] = NULL;
+ envp = &envp[i];
+ num_envp -= i;
+ buffer = &buffer[length];
+ buffer_size -= length;
+
+ if (class_dev->class->hotplug) {
+ /* have the bus specific function add its stuff */
+ retval = class_dev->class->hotplug (class_dev, envp, num_envp,
+ buffer, buffer_size);
+ if (retval) {
+ pr_debug ("%s - hotplug() returned %d\n",
+ __FUNCTION__, retval);
+ }
+ }
+
+ return retval;
+}
+
+static struct kset_hotplug_ops class_hotplug_ops = {
+ .filter = class_hotplug_filter,
+ .name = class_hotplug_name,
+ .hotplug = class_hotplug,
+};
+
+static decl_subsys(class_obj, &ktype_class_device, &class_hotplug_ops);
+
+
+static int class_device_add_attrs(struct class_device * cd)
+{
+ int i;
+ int error = 0;
+ 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]);
+ if (error)
+ goto Err;
+ }
+ }
+ Done:
+ return error;
+ Err:
+ while (--i >= 0)
+ class_device_remove_file(cd,&cls->class_dev_attrs[i]);
+ goto Done;
+}
+
+static void class_device_remove_attrs(struct class_device * cd)
+{
+ int i;
+ 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]);
+ }
+}
+
+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)
+{
+ kobj_set_kset_s(class_dev, class_obj_subsys);
+ kobject_init(&class_dev->kobj);
+ INIT_LIST_HEAD(&class_dev->node);
+}
+
+int class_device_add(struct class_device *class_dev)
+{
+ struct class * parent = NULL;
+ struct class_interface * class_intf;
+ int error;
+
+ class_dev = class_device_get(class_dev);
+ if (!class_dev)
+ return -EINVAL;
+
+ if (!strlen(class_dev->class_id)) {
+ error = -EINVAL;
+ goto register_done;
+ }
+
+ parent = class_get(class_dev->class);
+
+ pr_debug("CLASS: registering class device: ID = '%s'\n",
+ class_dev->class_id);
+
+ /* first, register with generic layer. */
+ kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
+ if (parent)
+ class_dev->kobj.parent = &parent->subsys.kset.kobj;
+
+ if ((error = kobject_add(&class_dev->kobj)))
+ goto register_done;
+
+ /* now take care of our own registration */
+ if (parent) {
+ down(&parent->sem);
+ list_add_tail(&class_dev->node, &parent->children);
+ list_for_each_entry(class_intf, &parent->interfaces, node)
+ if (class_intf->add)
+ 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");
+
+ register_done:
+ if (error && parent)
+ class_put(parent);
+ class_device_put(class_dev);
+ return error;
+}
+
+int class_device_register(struct class_device *class_dev)
+{
+ class_device_initialize(class_dev);
+ return class_device_add(class_dev);
+}
+
+void class_device_del(struct class_device *class_dev)
+{
+ struct class * parent = class_dev->class;
+ struct class_interface * class_intf;
+
+ if (parent) {
+ down(&parent->sem);
+ list_del_init(&class_dev->node);
+ list_for_each_entry(class_intf, &parent->interfaces, node)
+ if (class_intf->remove)
+ class_intf->remove(class_dev);
+ up(&parent->sem);
+ }
+
+ if (class_dev->dev)
+ sysfs_remove_link(&class_dev->kobj, "device");
+ class_device_remove_attrs(class_dev);
+
+ kobject_del(&class_dev->kobj);
+
+ if (parent)
+ class_put(parent);
+}
+
+void class_device_unregister(struct class_device *class_dev)
+{
+ pr_debug("CLASS: Unregistering class device. ID = '%s'\n",
+ class_dev->class_id);
+ class_device_del(class_dev);
+ class_device_put(class_dev);
+}
+
+int class_device_rename(struct class_device *class_dev, char *new_name)
+{
+ int error = 0;
+
+ class_dev = class_device_get(class_dev);
+ if (!class_dev)
+ return -EINVAL;
+
+ pr_debug("CLASS: renaming '%s' to '%s'\n", class_dev->class_id,
+ new_name);
+
+ strlcpy(class_dev->class_id, new_name, KOBJ_NAME_LEN);
+
+ error = kobject_rename(&class_dev->kobj, new_name);
+
+ class_device_put(class_dev);
+
+ return error;
+}
+
+struct class_device * class_device_get(struct class_device *class_dev)
+{
+ if (class_dev)
+ return to_class_dev(kobject_get(&class_dev->kobj));
+ return NULL;
+}
+
+void class_device_put(struct class_device *class_dev)
+{
+ kobject_put(&class_dev->kobj);
+}
+
+
+int class_interface_register(struct class_interface *class_intf)
+{
+ struct class *parent;
+ struct class_device *class_dev;
+
+ if (!class_intf || !class_intf->class)
+ return -ENODEV;
+
+ parent = class_get(class_intf->class);
+ if (!parent)
+ return -EINVAL;
+
+ down(&parent->sem);
+ list_add_tail(&class_intf->node, &parent->interfaces);
+ if (class_intf->add) {
+ list_for_each_entry(class_dev, &parent->children, node)
+ class_intf->add(class_dev);
+ }
+ up(&parent->sem);
+
+ return 0;
+}
+
+void class_interface_unregister(struct class_interface *class_intf)
+{
+ struct class * parent = class_intf->class;
+ struct class_device *class_dev;
+
+ if (!parent)
+ return;
+
+ down(&parent->sem);
+ list_del_init(&class_intf->node);
+ if (class_intf->remove) {
+ list_for_each_entry(class_dev, &parent->children, node)
+ class_intf->remove(class_dev);
+ }
+ up(&parent->sem);
+
+ class_put(parent);
+}
+
+
+
+int __init classes_init(void)
+{
+ int retval;
+
+ retval = subsystem_register(&class_subsys);
+ if (retval)
+ return retval;
+
+ /* ick, this is ugly, the things we go through to keep from showing up
+ * in sysfs... */
+ subsystem_init(&class_obj_subsys);
+ if (!class_obj_subsys.kset.subsys)
+ class_obj_subsys.kset.subsys = &class_obj_subsys;
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(class_create_file);
+EXPORT_SYMBOL_GPL(class_remove_file);
+EXPORT_SYMBOL_GPL(class_register);
+EXPORT_SYMBOL_GPL(class_unregister);
+EXPORT_SYMBOL_GPL(class_get);
+EXPORT_SYMBOL_GPL(class_put);
+
+EXPORT_SYMBOL_GPL(class_device_register);
+EXPORT_SYMBOL_GPL(class_device_unregister);
+EXPORT_SYMBOL_GPL(class_device_initialize);
+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_file);
+EXPORT_SYMBOL_GPL(class_device_remove_file);
+EXPORT_SYMBOL_GPL(class_device_create_bin_file);
+EXPORT_SYMBOL_GPL(class_device_remove_bin_file);
+
+EXPORT_SYMBOL_GPL(class_interface_register);
+EXPORT_SYMBOL_GPL(class_interface_unregister);
diff --git a/drivers/base/class_simple.c b/drivers/base/class_simple.c
new file mode 100644
index 000000000000..27699eb20a37
--- /dev/null
+++ b/drivers/base/class_simple.c
@@ -0,0 +1,199 @@
+/*
+ * 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
new file mode 100644
index 000000000000..4e6cce8e6d35
--- /dev/null
+++ b/drivers/base/core.c
@@ -0,0 +1,439 @@
+/*
+ * drivers/base/core.c - core driver model code (device registration, etc)
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.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;
+
+/*
+ * sysfs bindings for devices.
+ */
+
+#define to_dev(obj) container_of(obj, struct device, kobj)
+#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
+
+extern struct attribute * dev_default_attrs[];
+
+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);
+ ssize_t ret = 0;
+
+ if (dev_attr->show)
+ ret = dev_attr->show(dev, buf);
+ return ret;
+}
+
+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);
+ ssize_t ret = 0;
+
+ if (dev_attr->store)
+ ret = dev_attr->store(dev, buf, count);
+ return ret;
+}
+
+static struct sysfs_ops dev_sysfs_ops = {
+ .show = dev_attr_show,
+ .store = dev_attr_store,
+};
+
+
+/**
+ * device_release - free device structure.
+ * @kobj: device's kobject.
+ *
+ * This is called once the reference count for the object
+ * 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)
+{
+ struct device * dev = to_dev(kobj);
+
+ if (dev->release)
+ dev->release(dev);
+ else {
+ 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);
+ }
+}
+
+static struct kobj_type ktype_device = {
+ .release = device_release,
+ .sysfs_ops = &dev_sysfs_ops,
+ .default_attrs = dev_default_attrs,
+};
+
+
+static int dev_hotplug_filter(struct kset *kset, struct kobject *kobj)
+{
+ struct kobj_type *ktype = get_ktype(kobj);
+
+ if (ktype == &ktype_device) {
+ struct device *dev = to_dev(kobj);
+ if (dev->bus)
+ return 1;
+ }
+ return 0;
+}
+
+static char *dev_hotplug_name(struct kset *kset, struct kobject *kobj)
+{
+ struct device *dev = to_dev(kobj);
+
+ return dev->bus->name;
+}
+
+static int dev_hotplug(struct kset *kset, struct kobject *kobj, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ struct device *dev = to_dev(kobj);
+ int i = 0;
+ int length = 0;
+ int retval = 0;
+
+ /* add bus name of physical device */
+ if (dev->bus)
+ add_hotplug_env_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "PHYSDEVBUS=%s", dev->bus->name);
+
+ /* add driver name of physical device */
+ if (dev->driver)
+ add_hotplug_env_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "PHYSDEVDRIVER=%s", dev->driver->name);
+
+ /* terminate, set to next free slot, shrink available space */
+ envp[i] = NULL;
+ envp = &envp[i];
+ num_envp -= i;
+ buffer = &buffer[length];
+ buffer_size -= length;
+
+ if (dev->bus->hotplug) {
+ /* have the bus specific function add its stuff */
+ retval = dev->bus->hotplug (dev, envp, num_envp, buffer, buffer_size);
+ if (retval) {
+ pr_debug ("%s - hotplug() returned %d\n",
+ __FUNCTION__, retval);
+ }
+ }
+
+ return retval;
+}
+
+static struct kset_hotplug_ops device_hotplug_ops = {
+ .filter = dev_hotplug_filter,
+ .name = dev_hotplug_name,
+ .hotplug = dev_hotplug,
+};
+
+/**
+ * device_subsys - structure to be registered with kobject core.
+ */
+
+decl_subsys(devices, &ktype_device, &device_hotplug_ops);
+
+
+/**
+ * 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 error = 0;
+ if (get_device(dev)) {
+ error = sysfs_create_file(&dev->kobj, &attr->attr);
+ put_device(dev);
+ }
+ return error;
+}
+
+/**
+ * device_remove_file - remove sysfs attribute file.
+ * @dev: device.
+ * @attr: device attribute descriptor.
+ */
+
+void device_remove_file(struct device * dev, struct device_attribute * attr)
+{
+ if (get_device(dev)) {
+ sysfs_remove_file(&dev->kobj, &attr->attr);
+ put_device(dev);
+ }
+}
+
+
+/**
+ * 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).
+ */
+
+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);
+ INIT_LIST_HEAD(&dev->dma_pools);
+}
+
+/**
+ * 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 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;
+ int error = -EINVAL;
+
+ dev = get_device(dev);
+ if (!dev || !strlen(dev->bus_id))
+ goto Error;
+
+ parent = get_device(dev->parent);
+
+ pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
+
+ /* first, register with generic layer. */
+ kobject_set_name(&dev->kobj, "%s", dev->bus_id);
+ if (parent)
+ dev->kobj.parent = &parent->kobj;
+
+ if ((error = kobject_add(&dev->kobj)))
+ goto Error;
+ if ((error = device_pm_add(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);
+
+ /* notify platform of device entry */
+ if (platform_notify)
+ platform_notify(dev);
+ Done:
+ put_device(dev);
+ return error;
+ BusError:
+ device_pm_remove(dev);
+ PMError:
+ kobject_del(&dev->kobj);
+ Error:
+ if (parent)
+ put_device(parent);
+ goto Done;
+}
+
+
+/**
+ * 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.
+ */
+
+int device_register(struct device *dev)
+{
+ device_initialize(dev);
+ return device_add(dev);
+}
+
+
+/**
+ * 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.
+ */
+
+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.
+ */
+void put_device(struct device * dev)
+{
+ if (dev)
+ kobject_put(&dev->kobj);
+}
+
+
+/**
+ * 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.
+ *
+ * NOTE: this should be called manually _iff_ device_add() was
+ * also called manually.
+ */
+
+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);
+
+ /* Notify the platform of the removal, in case they
+ * need to do anything...
+ */
+ if (platform_notify_remove)
+ platform_notify_remove(dev);
+ bus_remove_device(dev);
+ device_pm_remove(dev);
+ kobject_del(&dev->kobj);
+ if (parent)
+ put_device(parent);
+}
+
+/**
+ * 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.
+ */
+void device_unregister(struct device * dev)
+{
+ pr_debug("DEV: Unregistering device. ID = '%s'\n", dev->bus_id);
+ device_del(dev);
+ put_device(dev);
+}
+
+
+/**
+ * device_for_each_child - device child iterator.
+ * @dev: parent struct device.
+ * @data: data for the callback.
+ * @fn: function to be called for each device.
+ *
+ * Iterate over @dev'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.
+ */
+int device_for_each_child(struct device * dev, void * data,
+ int (*fn)(struct device *, void *))
+{
+ 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);
+ 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);
+}
+
+EXPORT_SYMBOL_GPL(device_for_each_child);
+
+EXPORT_SYMBOL_GPL(device_initialize);
+EXPORT_SYMBOL_GPL(device_add);
+EXPORT_SYMBOL_GPL(device_register);
+
+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/cpu.c b/drivers/base/cpu.c
new file mode 100644
index 000000000000..6ef3069b5710
--- /dev/null
+++ b/drivers/base/cpu.c
@@ -0,0 +1,104 @@
+/*
+ * drivers/base/cpu.c - basic CPU class support
+ */
+
+#include <linux/sysdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/topology.h>
+#include <linux/device.h>
+
+
+struct sysdev_class cpu_sysdev_class = {
+ set_kset_name("cpu"),
+};
+EXPORT_SYMBOL(cpu_sysdev_class);
+
+#ifdef CONFIG_HOTPLUG_CPU
+static ssize_t show_online(struct sys_device *dev, char *buf)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, sysdev);
+
+ return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id));
+}
+
+static ssize_t store_online(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, sysdev);
+ ssize_t ret;
+
+ switch (buf[0]) {
+ case '0':
+ ret = cpu_down(cpu->sysdev.id);
+ if (!ret)
+ kobject_hotplug(&dev->kobj, KOBJ_OFFLINE);
+ break;
+ case '1':
+ ret = cpu_up(cpu->sysdev.id);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret >= 0)
+ ret = count;
+ return ret;
+}
+static SYSDEV_ATTR(online, 0600, show_online, store_online);
+
+static void __devinit register_cpu_control(struct cpu *cpu)
+{
+ sysdev_create_file(&cpu->sysdev, &attr_online);
+}
+void unregister_cpu(struct cpu *cpu, struct node *root)
+{
+
+ if (root)
+ sysfs_remove_link(&root->sysdev.kobj,
+ kobject_name(&cpu->sysdev.kobj));
+ sysdev_remove_file(&cpu->sysdev, &attr_online);
+
+ sysdev_unregister(&cpu->sysdev);
+
+ return;
+}
+#else /* ... !CONFIG_HOTPLUG_CPU */
+static inline void register_cpu_control(struct cpu *cpu)
+{
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+/*
+ * register_cpu - Setup a driverfs device for a CPU.
+ * @cpu - Callers can set the cpu->no_control field to 1, to indicate not to
+ * generate a control file in sysfs for this CPU.
+ * @num - CPU number to use when creating the device.
+ *
+ * Initialize and register the CPU device.
+ */
+int __devinit register_cpu(struct cpu *cpu, int num, struct node *root)
+{
+ int error;
+
+ cpu->node_id = cpu_to_node(num);
+ cpu->sysdev.id = num;
+ cpu->sysdev.cls = &cpu_sysdev_class;
+
+ error = sysdev_register(&cpu->sysdev);
+ if (!error && root)
+ error = sysfs_create_link(&root->sysdev.kobj,
+ &cpu->sysdev.kobj,
+ kobject_name(&cpu->sysdev.kobj));
+ if (!error && !cpu->no_control)
+ register_cpu_control(cpu);
+ return error;
+}
+
+
+
+int __init cpu_dev_init(void)
+{
+ return sysdev_class_register(&cpu_sysdev_class);
+}
diff --git a/drivers/base/dmapool.c b/drivers/base/dmapool.c
new file mode 100644
index 000000000000..f48833df61a2
--- /dev/null
+++ b/drivers/base/dmapool.c
@@ -0,0 +1,414 @@
+
+#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>
+#include <linux/module.h>
+
+/*
+ * Pool allocator ... wraps the dma_alloc_coherent page allocator, so
+ * small blocks are easily used by drivers for bus mastering controllers.
+ * This should probably be sharing the guts of the slab allocator.
+ */
+
+struct dma_pool { /* the pool */
+ struct list_head page_list;
+ spinlock_t lock;
+ size_t blocks_per_page;
+ size_t size;
+ struct device *dev;
+ size_t allocation;
+ char name [32];
+ wait_queue_head_t waitq;
+ struct list_head pools;
+};
+
+struct dma_page { /* cacheable header for 'allocation' bytes */
+ struct list_head page_list;
+ void *vaddr;
+ dma_addr_t dma;
+ unsigned in_use;
+ unsigned long bitmap [0];
+};
+
+#define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000)
+#define POOL_POISON_FREED 0xa7 /* !inuse */
+#define POOL_POISON_ALLOCATED 0xa9 /* !initted */
+
+static DECLARE_MUTEX (pools_lock);
+
+static ssize_t
+show_pools (struct device *dev, char *buf)
+{
+ unsigned temp;
+ unsigned size;
+ char *next;
+ struct dma_page *page;
+ struct dma_pool *pool;
+
+ next = buf;
+ size = PAGE_SIZE;
+
+ temp = scnprintf(next, size, "poolinfo - 0.1\n");
+ size -= temp;
+ next += temp;
+
+ down (&pools_lock);
+ list_for_each_entry(pool, &dev->dma_pools, pools) {
+ unsigned pages = 0;
+ unsigned blocks = 0;
+
+ list_for_each_entry(page, &pool->page_list, page_list) {
+ pages++;
+ blocks += page->in_use;
+ }
+
+ /* per-pool info, no real statistics yet */
+ temp = scnprintf(next, size, "%-16s %4u %4Zu %4Zu %2u\n",
+ pool->name,
+ blocks, pages * pool->blocks_per_page,
+ pool->size, pages);
+ size -= temp;
+ next += temp;
+ }
+ up (&pools_lock);
+
+ return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (pools, S_IRUGO, show_pools, NULL);
+
+/**
+ * dma_pool_create - Creates a pool of consistent memory blocks, for dma.
+ * @name: name of pool, for diagnostics
+ * @dev: device that will be doing the DMA
+ * @size: size of the blocks in this pool.
+ * @align: alignment requirement for blocks; must be a power of two
+ * @allocation: returned blocks won't cross this boundary (or zero)
+ * Context: !in_interrupt()
+ *
+ * Returns a dma allocation pool with the requested characteristics, or
+ * null if one can't be created. Given one of these pools, dma_pool_alloc()
+ * may be used to allocate memory. Such memory will all have "consistent"
+ * DMA mappings, accessible by the device and its driver without using
+ * cache flushing primitives. The actual size of blocks allocated may be
+ * larger than requested because of alignment.
+ *
+ * If allocation is nonzero, objects returned from dma_pool_alloc() won't
+ * cross that size boundary. This is useful for devices which have
+ * addressing restrictions on individual DMA transfers, such as not crossing
+ * boundaries of 4KBytes.
+ */
+struct dma_pool *
+dma_pool_create (const char *name, struct device *dev,
+ size_t size, size_t align, size_t allocation)
+{
+ struct dma_pool *retval;
+
+ if (align == 0)
+ align = 1;
+ if (size == 0)
+ return NULL;
+ else if (size < align)
+ size = align;
+ else if ((size % align) != 0) {
+ size += align + 1;
+ size &= ~(align - 1);
+ }
+
+ if (allocation == 0) {
+ if (PAGE_SIZE < size)
+ allocation = size;
+ else
+ allocation = PAGE_SIZE;
+ // FIXME: round up for less fragmentation
+ } else if (allocation < size)
+ return NULL;
+
+ if (!(retval = kmalloc (sizeof *retval, SLAB_KERNEL)))
+ return retval;
+
+ strlcpy (retval->name, name, sizeof retval->name);
+
+ retval->dev = dev;
+
+ INIT_LIST_HEAD (&retval->page_list);
+ spin_lock_init (&retval->lock);
+ retval->size = size;
+ retval->allocation = allocation;
+ retval->blocks_per_page = allocation / size;
+ init_waitqueue_head (&retval->waitq);
+
+ if (dev) {
+ down (&pools_lock);
+ if (list_empty (&dev->dma_pools))
+ device_create_file (dev, &dev_attr_pools);
+ /* note: not currently insisting "name" be unique */
+ list_add (&retval->pools, &dev->dma_pools);
+ up (&pools_lock);
+ } else
+ INIT_LIST_HEAD (&retval->pools);
+
+ return retval;
+}
+
+
+static struct dma_page *
+pool_alloc_page (struct dma_pool *pool, unsigned int __nocast mem_flags)
+{
+ struct dma_page *page;
+ int mapsize;
+
+ mapsize = pool->blocks_per_page;
+ mapsize = (mapsize + BITS_PER_LONG - 1) / BITS_PER_LONG;
+ mapsize *= sizeof (long);
+
+ page = (struct dma_page *) kmalloc (mapsize + sizeof *page, mem_flags);
+ if (!page)
+ return NULL;
+ page->vaddr = dma_alloc_coherent (pool->dev,
+ pool->allocation,
+ &page->dma,
+ mem_flags);
+ if (page->vaddr) {
+ memset (page->bitmap, 0xff, mapsize); // bit set == free
+#ifdef CONFIG_DEBUG_SLAB
+ memset (page->vaddr, POOL_POISON_FREED, pool->allocation);
+#endif
+ list_add (&page->page_list, &pool->page_list);
+ page->in_use = 0;
+ } else {
+ kfree (page);
+ page = NULL;
+ }
+ return page;
+}
+
+
+static inline int
+is_page_busy (int blocks, unsigned long *bitmap)
+{
+ while (blocks > 0) {
+ if (*bitmap++ != ~0UL)
+ return 1;
+ blocks -= BITS_PER_LONG;
+ }
+ return 0;
+}
+
+static void
+pool_free_page (struct dma_pool *pool, struct dma_page *page)
+{
+ dma_addr_t dma = page->dma;
+
+#ifdef CONFIG_DEBUG_SLAB
+ memset (page->vaddr, POOL_POISON_FREED, pool->allocation);
+#endif
+ dma_free_coherent (pool->dev, pool->allocation, page->vaddr, dma);
+ list_del (&page->page_list);
+ kfree (page);
+}
+
+
+/**
+ * dma_pool_destroy - destroys a pool of dma memory blocks.
+ * @pool: dma pool that will be destroyed
+ * Context: !in_interrupt()
+ *
+ * Caller guarantees that no more memory from the pool is in use,
+ * and that nothing will try to use the pool after this call.
+ */
+void
+dma_pool_destroy (struct dma_pool *pool)
+{
+ down (&pools_lock);
+ list_del (&pool->pools);
+ if (pool->dev && list_empty (&pool->dev->dma_pools))
+ device_remove_file (pool->dev, &dev_attr_pools);
+ up (&pools_lock);
+
+ while (!list_empty (&pool->page_list)) {
+ struct dma_page *page;
+ page = list_entry (pool->page_list.next,
+ struct dma_page, page_list);
+ if (is_page_busy (pool->blocks_per_page, page->bitmap)) {
+ if (pool->dev)
+ dev_err(pool->dev, "dma_pool_destroy %s, %p busy\n",
+ pool->name, page->vaddr);
+ else
+ printk (KERN_ERR "dma_pool_destroy %s, %p busy\n",
+ pool->name, page->vaddr);
+ /* leak the still-in-use consistent memory */
+ list_del (&page->page_list);
+ kfree (page);
+ } else
+ pool_free_page (pool, page);
+ }
+
+ kfree (pool);
+}
+
+
+/**
+ * dma_pool_alloc - get a block of consistent memory
+ * @pool: dma pool that will produce the block
+ * @mem_flags: GFP_* bitmask
+ * @handle: pointer to dma address of block
+ *
+ * This returns the kernel virtual address of a currently unused block,
+ * and reports its dma address through the handle.
+ * If such a memory block can't be allocated, null is returned.
+ */
+void *
+dma_pool_alloc (struct dma_pool *pool, int mem_flags, dma_addr_t *handle)
+{
+ unsigned long flags;
+ struct dma_page *page;
+ int map, block;
+ size_t offset;
+ void *retval;
+
+restart:
+ spin_lock_irqsave (&pool->lock, flags);
+ list_for_each_entry(page, &pool->page_list, page_list) {
+ int i;
+ /* only cachable accesses here ... */
+ for (map = 0, i = 0;
+ i < pool->blocks_per_page;
+ i += BITS_PER_LONG, map++) {
+ if (page->bitmap [map] == 0)
+ continue;
+ block = ffz (~ page->bitmap [map]);
+ if ((i + block) < pool->blocks_per_page) {
+ clear_bit (block, &page->bitmap [map]);
+ offset = (BITS_PER_LONG * map) + block;
+ offset *= pool->size;
+ goto ready;
+ }
+ }
+ }
+ if (!(page = pool_alloc_page (pool, SLAB_ATOMIC))) {
+ if (mem_flags & __GFP_WAIT) {
+ DECLARE_WAITQUEUE (wait, current);
+
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue (&pool->waitq, &wait);
+ spin_unlock_irqrestore (&pool->lock, flags);
+
+ schedule_timeout (POOL_TIMEOUT_JIFFIES);
+
+ remove_wait_queue (&pool->waitq, &wait);
+ goto restart;
+ }
+ retval = NULL;
+ goto done;
+ }
+
+ clear_bit (0, &page->bitmap [0]);
+ offset = 0;
+ready:
+ page->in_use++;
+ retval = offset + page->vaddr;
+ *handle = offset + page->dma;
+#ifdef CONFIG_DEBUG_SLAB
+ memset (retval, POOL_POISON_ALLOCATED, pool->size);
+#endif
+done:
+ spin_unlock_irqrestore (&pool->lock, flags);
+ return retval;
+}
+
+
+static struct dma_page *
+pool_find_page (struct dma_pool *pool, dma_addr_t dma)
+{
+ unsigned long flags;
+ struct dma_page *page;
+
+ spin_lock_irqsave (&pool->lock, flags);
+ list_for_each_entry(page, &pool->page_list, page_list) {
+ if (dma < page->dma)
+ continue;
+ if (dma < (page->dma + pool->allocation))
+ goto done;
+ }
+ page = NULL;
+done:
+ spin_unlock_irqrestore (&pool->lock, flags);
+ return page;
+}
+
+
+/**
+ * dma_pool_free - put block back into dma pool
+ * @pool: the dma pool holding the block
+ * @vaddr: virtual address of block
+ * @dma: dma address of block
+ *
+ * Caller promises neither device nor driver will again touch this block
+ * unless it is first re-allocated.
+ */
+void
+dma_pool_free (struct dma_pool *pool, void *vaddr, dma_addr_t dma)
+{
+ struct dma_page *page;
+ unsigned long flags;
+ int map, block;
+
+ if ((page = pool_find_page (pool, dma)) == 0) {
+ if (pool->dev)
+ dev_err(pool->dev, "dma_pool_free %s, %p/%lx (bad dma)\n",
+ pool->name, vaddr, (unsigned long) dma);
+ else
+ printk (KERN_ERR "dma_pool_free %s, %p/%lx (bad dma)\n",
+ pool->name, vaddr, (unsigned long) dma);
+ return;
+ }
+
+ block = dma - page->dma;
+ block /= pool->size;
+ map = block / BITS_PER_LONG;
+ block %= BITS_PER_LONG;
+
+#ifdef CONFIG_DEBUG_SLAB
+ if (((dma - page->dma) + (void *)page->vaddr) != vaddr) {
+ if (pool->dev)
+ dev_err(pool->dev, "dma_pool_free %s, %p (bad vaddr)/%Lx\n",
+ pool->name, vaddr, (unsigned long long) dma);
+ else
+ printk (KERN_ERR "dma_pool_free %s, %p (bad vaddr)/%Lx\n",
+ pool->name, vaddr, (unsigned long long) dma);
+ return;
+ }
+ if (page->bitmap [map] & (1UL << block)) {
+ if (pool->dev)
+ dev_err(pool->dev, "dma_pool_free %s, dma %Lx already free\n",
+ pool->name, (unsigned long long)dma);
+ else
+ printk (KERN_ERR "dma_pool_free %s, dma %Lx already free\n",
+ pool->name, (unsigned long long)dma);
+ return;
+ }
+ memset (vaddr, POOL_POISON_FREED, pool->size);
+#endif
+
+ spin_lock_irqsave (&pool->lock, flags);
+ page->in_use--;
+ set_bit (block, &page->bitmap [map]);
+ if (waitqueue_active (&pool->waitq))
+ wake_up (&pool->waitq);
+ /*
+ * Resist a temptation to do
+ * if (!is_page_busy(bpp, page->bitmap)) pool_free_page(pool, page);
+ * Better have a few empty pages hang around.
+ */
+ spin_unlock_irqrestore (&pool->lock, flags);
+}
+
+
+EXPORT_SYMBOL (dma_pool_create);
+EXPORT_SYMBOL (dma_pool_destroy);
+EXPORT_SYMBOL (dma_pool_alloc);
+EXPORT_SYMBOL (dma_pool_free);
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
new file mode 100644
index 000000000000..3b269f7e5213
--- /dev/null
+++ b/drivers/base/driver.c
@@ -0,0 +1,138 @@
+/*
+ * driver.c - centralized device driver management
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#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)
+
+/**
+ * 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 error;
+ if (get_driver(drv)) {
+ error = sysfs_create_file(&drv->kobj, &attr->attr);
+ put_driver(drv);
+ } else
+ error = -EINVAL;
+ return error;
+}
+
+
+/**
+ * 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)
+{
+ if (get_driver(drv)) {
+ sysfs_remove_file(&drv->kobj, &attr->attr);
+ put_driver(drv);
+ }
+}
+
+
+/**
+ * get_driver - increment driver reference count.
+ * @drv: driver.
+ */
+struct device_driver * get_driver(struct device_driver * drv)
+{
+ return drv ? to_drv(kobject_get(&drv->kobj)) : NULL;
+}
+
+
+/**
+ * put_driver - decrement driver's refcount.
+ * @drv: driver.
+ */
+void put_driver(struct device_driver * drv)
+{
+ kobject_put(&drv->kobj);
+}
+
+
+/**
+ * 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.
+ *
+ * The one interesting aspect is that we setup @drv->unloaded
+ * as a completion that gets complete when the driver reference
+ * count reaches 0.
+ */
+int driver_register(struct device_driver * drv)
+{
+ INIT_LIST_HEAD(&drv->devices);
+ init_completion(&drv->unloaded);
+ return bus_add_driver(drv);
+}
+
+
+/**
+ * driver_unregister - remove driver from system.
+ * @drv: driver.
+ *
+ * Again, we pass off most of the work to the bus-level call.
+ *
+ * Though, once that is done, we wait until @drv->unloaded is completed.
+ * This will block until the driver refcount reaches 0, and it is
+ * released. Only modular drivers will call this function, and we
+ * have to guarantee that it won't complete, letting the driver
+ * unload until all references are gone.
+ */
+
+void driver_unregister(struct device_driver * drv)
+{
+ bus_remove_driver(drv);
+ wait_for_completion(&drv->unloaded);
+}
+
+/**
+ * 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.
+ *
+ * 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);
+ 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
new file mode 100644
index 000000000000..88ab044932f2
--- /dev/null
+++ b/drivers/base/firmware.c
@@ -0,0 +1,34 @@
+/*
+ * firmware.c - firmware subsystem hoohaw.
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+static decl_subsys(firmware, NULL, NULL);
+
+int firmware_register(struct subsystem * s)
+{
+ kset_set_kset_s(s, firmware_subsys);
+ return subsystem_register(s);
+}
+
+void firmware_unregister(struct subsystem * s)
+{
+ subsystem_unregister(s);
+}
+
+int __init firmware_init(void)
+{
+ return subsystem_register(&firmware_subsys);
+}
+
+EXPORT_SYMBOL_GPL(firmware_register);
+EXPORT_SYMBOL_GPL(firmware_unregister);
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
new file mode 100644
index 000000000000..26c9464af80a
--- /dev/null
+++ b/drivers/base/firmware_class.c
@@ -0,0 +1,583 @@
+/*
+ * firmware_class.c - Multi purpose firmware loading support
+ *
+ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
+ *
+ * Please see Documentation/firmware_class/ for more information.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <asm/semaphore.h>
+
+#include <linux/firmware.h>
+#include "base.h"
+
+MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
+MODULE_DESCRIPTION("Multi purpose firmware loading support");
+MODULE_LICENSE("GPL");
+
+enum {
+ FW_STATUS_LOADING,
+ FW_STATUS_DONE,
+ FW_STATUS_ABORT,
+ FW_STATUS_READY,
+};
+
+static int loading_timeout = 10; /* In seconds */
+
+/* fw_lock could be moved to 'struct firmware_priv' but since it is just
+ * guarding for corner cases a global lock should be OK */
+static DECLARE_MUTEX(fw_lock);
+
+struct firmware_priv {
+ char fw_id[FIRMWARE_NAME_MAX];
+ struct completion completion;
+ struct bin_attribute attr_data;
+ struct firmware *fw;
+ unsigned long status;
+ int alloc_size;
+ struct timer_list timeout;
+};
+
+static inline void
+fw_load_abort(struct firmware_priv *fw_priv)
+{
+ set_bit(FW_STATUS_ABORT, &fw_priv->status);
+ wmb();
+ complete(&fw_priv->completion);
+}
+
+static ssize_t
+firmware_timeout_show(struct class *class, char *buf)
+{
+ return sprintf(buf, "%d\n", loading_timeout);
+}
+
+/**
+ * firmware_timeout_store:
+ * Description:
+ * Sets the number of seconds to wait for the firmware. Once
+ * this expires an error will be return to the driver and no
+ * firmware will be provided.
+ *
+ * Note: zero means 'wait for ever'
+ *
+ **/
+static ssize_t
+firmware_timeout_store(struct class *class, const char *buf, size_t count)
+{
+ loading_timeout = simple_strtol(buf, NULL, 10);
+ return count;
+}
+
+static CLASS_ATTR(timeout, 0644, firmware_timeout_show, firmware_timeout_store);
+
+static void fw_class_dev_release(struct class_device *class_dev);
+int firmware_class_hotplug(struct class_device *dev, char **envp,
+ int num_envp, char *buffer, int buffer_size);
+
+static struct class firmware_class = {
+ .name = "firmware",
+ .hotplug = firmware_class_hotplug,
+ .release = fw_class_dev_release,
+};
+
+int
+firmware_class_hotplug(struct class_device *class_dev, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+ int i = 0, len = 0;
+
+ if (!test_bit(FW_STATUS_READY, &fw_priv->status))
+ return -ENODEV;
+
+ if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "FIRMWARE=%s", fw_priv->fw_id))
+ return -ENOMEM;
+
+ envp[i] = NULL;
+
+ return 0;
+}
+
+static ssize_t
+firmware_loading_show(struct class_device *class_dev, char *buf)
+{
+ struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+ int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
+ return sprintf(buf, "%d\n", loading);
+}
+
+/**
+ * firmware_loading_store: - loading control file
+ * Description:
+ * The relevant values are:
+ *
+ * 1: Start a load, discarding any previous partial load.
+ * 0: Conclude the load and handle the data to the driver code.
+ * -1: Conclude the load with an error and discard any written data.
+ **/
+static ssize_t
+firmware_loading_store(struct class_device *class_dev,
+ const char *buf, size_t count)
+{
+ struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+ int loading = simple_strtol(buf, NULL, 10);
+
+ switch (loading) {
+ case 1:
+ down(&fw_lock);
+ vfree(fw_priv->fw->data);
+ fw_priv->fw->data = NULL;
+ fw_priv->fw->size = 0;
+ fw_priv->alloc_size = 0;
+ set_bit(FW_STATUS_LOADING, &fw_priv->status);
+ up(&fw_lock);
+ break;
+ case 0:
+ if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
+ complete(&fw_priv->completion);
+ clear_bit(FW_STATUS_LOADING, &fw_priv->status);
+ break;
+ }
+ /* fallthrough */
+ default:
+ printk(KERN_ERR "%s: unexpected value (%d)\n", __FUNCTION__,
+ loading);
+ /* fallthrough */
+ case -1:
+ fw_load_abort(fw_priv);
+ break;
+ }
+
+ return count;
+}
+
+static CLASS_DEVICE_ATTR(loading, 0644,
+ firmware_loading_show, firmware_loading_store);
+
+static ssize_t
+firmware_data_read(struct kobject *kobj,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct class_device *class_dev = to_class_dev(kobj);
+ struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+ struct firmware *fw;
+ ssize_t ret_count = count;
+
+ down(&fw_lock);
+ fw = fw_priv->fw;
+ if (test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+ ret_count = -ENODEV;
+ goto out;
+ }
+ if (offset > fw->size) {
+ ret_count = 0;
+ goto out;
+ }
+ if (offset + ret_count > fw->size)
+ ret_count = fw->size - offset;
+
+ memcpy(buffer, fw->data + offset, ret_count);
+out:
+ up(&fw_lock);
+ return ret_count;
+}
+static int
+fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
+{
+ u8 *new_data;
+
+ if (min_size <= fw_priv->alloc_size)
+ return 0;
+
+ new_data = vmalloc(fw_priv->alloc_size + PAGE_SIZE);
+ if (!new_data) {
+ printk(KERN_ERR "%s: unable to alloc buffer\n", __FUNCTION__);
+ /* Make sure that we don't keep incomplete data */
+ fw_load_abort(fw_priv);
+ return -ENOMEM;
+ }
+ fw_priv->alloc_size += PAGE_SIZE;
+ if (fw_priv->fw->data) {
+ memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
+ vfree(fw_priv->fw->data);
+ }
+ fw_priv->fw->data = new_data;
+ BUG_ON(min_size > fw_priv->alloc_size);
+ return 0;
+}
+
+/**
+ * firmware_data_write:
+ *
+ * Description:
+ *
+ * Data written to the 'data' attribute will be later handled to
+ * the driver as a firmware image.
+ **/
+static ssize_t
+firmware_data_write(struct kobject *kobj,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct class_device *class_dev = to_class_dev(kobj);
+ struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+ struct firmware *fw;
+ ssize_t retval;
+
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+ down(&fw_lock);
+ fw = fw_priv->fw;
+ if (test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+ retval = -ENODEV;
+ goto out;
+ }
+ retval = fw_realloc_buffer(fw_priv, offset + count);
+ if (retval)
+ goto out;
+
+ memcpy(fw->data + offset, buffer, count);
+
+ fw->size = max_t(size_t, offset + count, fw->size);
+ retval = count;
+out:
+ up(&fw_lock);
+ return retval;
+}
+static struct bin_attribute firmware_attr_data_tmpl = {
+ .attr = {.name = "data", .mode = 0644, .owner = THIS_MODULE},
+ .size = 0,
+ .read = firmware_data_read,
+ .write = firmware_data_write,
+};
+
+static void
+fw_class_dev_release(struct class_device *class_dev)
+{
+ struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+
+ kfree(fw_priv);
+ kfree(class_dev);
+
+ module_put(THIS_MODULE);
+}
+
+static void
+firmware_class_timeout(u_long data)
+{
+ struct firmware_priv *fw_priv = (struct firmware_priv *) data;
+ fw_load_abort(fw_priv);
+}
+
+static inline void
+fw_setup_class_device_id(struct class_device *class_dev, struct device *dev)
+{
+ /* XXX warning we should watch out for name collisions */
+ strlcpy(class_dev->class_id, dev->bus_id, BUS_ID_SIZE);
+}
+
+static int
+fw_register_class_device(struct class_device **class_dev_p,
+ const char *fw_name, struct device *device)
+{
+ int retval;
+ struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv),
+ GFP_KERNEL);
+ struct class_device *class_dev = kmalloc(sizeof (struct class_device),
+ GFP_KERNEL);
+
+ *class_dev_p = NULL;
+
+ if (!fw_priv || !class_dev) {
+ printk(KERN_ERR "%s: kmalloc failed\n", __FUNCTION__);
+ retval = -ENOMEM;
+ goto error_kfree;
+ }
+ memset(fw_priv, 0, sizeof (*fw_priv));
+ memset(class_dev, 0, sizeof (*class_dev));
+
+ init_completion(&fw_priv->completion);
+ fw_priv->attr_data = firmware_attr_data_tmpl;
+ strlcpy(fw_priv->fw_id, fw_name, FIRMWARE_NAME_MAX);
+
+ fw_priv->timeout.function = firmware_class_timeout;
+ fw_priv->timeout.data = (u_long) fw_priv;
+ init_timer(&fw_priv->timeout);
+
+ fw_setup_class_device_id(class_dev, device);
+ class_dev->dev = device;
+ class_dev->class = &firmware_class;
+ class_set_devdata(class_dev, fw_priv);
+ retval = class_device_register(class_dev);
+ if (retval) {
+ printk(KERN_ERR "%s: class_device_register failed\n",
+ __FUNCTION__);
+ goto error_kfree;
+ }
+ *class_dev_p = class_dev;
+ return 0;
+
+error_kfree:
+ kfree(fw_priv);
+ kfree(class_dev);
+ return retval;
+}
+
+static int
+fw_setup_class_device(struct firmware *fw, struct class_device **class_dev_p,
+ const char *fw_name, struct device *device)
+{
+ struct class_device *class_dev;
+ struct firmware_priv *fw_priv;
+ int retval;
+
+ *class_dev_p = NULL;
+ retval = fw_register_class_device(&class_dev, fw_name, device);
+ if (retval)
+ goto out;
+
+ /* Need to pin this module until class device is destroyed */
+ __module_get(THIS_MODULE);
+
+ fw_priv = class_get_devdata(class_dev);
+
+ fw_priv->fw = fw;
+ retval = sysfs_create_bin_file(&class_dev->kobj, &fw_priv->attr_data);
+ if (retval) {
+ printk(KERN_ERR "%s: sysfs_create_bin_file failed\n",
+ __FUNCTION__);
+ goto error_unreg;
+ }
+
+ retval = class_device_create_file(class_dev,
+ &class_device_attr_loading);
+ if (retval) {
+ printk(KERN_ERR "%s: class_device_create_file failed\n",
+ __FUNCTION__);
+ goto error_unreg;
+ }
+
+ set_bit(FW_STATUS_READY, &fw_priv->status);
+ *class_dev_p = class_dev;
+ goto out;
+
+error_unreg:
+ class_device_unregister(class_dev);
+out:
+ return retval;
+}
+
+/**
+ * request_firmware: - request firmware to hotplug and wait for it
+ * Description:
+ * @firmware will be used to return a firmware image by the name
+ * of @name for device @device.
+ *
+ * Should be called from user context where sleeping is allowed.
+ *
+ * @name will be use as $FIRMWARE in the hotplug environment and
+ * should be distinctive enough not to be confused with any other
+ * firmware image for this or any other device.
+ **/
+int
+request_firmware(const struct firmware **firmware_p, const char *name,
+ struct device *device)
+{
+ struct class_device *class_dev;
+ struct firmware_priv *fw_priv;
+ struct firmware *firmware;
+ int retval;
+
+ if (!firmware_p)
+ return -EINVAL;
+
+ *firmware_p = firmware = kmalloc(sizeof (struct firmware), GFP_KERNEL);
+ if (!firmware) {
+ printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
+ __FUNCTION__);
+ retval = -ENOMEM;
+ goto out;
+ }
+ memset(firmware, 0, sizeof (*firmware));
+
+ retval = fw_setup_class_device(firmware, &class_dev, name, device);
+ if (retval)
+ goto error_kfree_fw;
+
+ fw_priv = class_get_devdata(class_dev);
+
+ if (loading_timeout) {
+ fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
+ add_timer(&fw_priv->timeout);
+ }
+
+ kobject_hotplug(&class_dev->kobj, KOBJ_ADD);
+ wait_for_completion(&fw_priv->completion);
+ set_bit(FW_STATUS_DONE, &fw_priv->status);
+
+ del_timer_sync(&fw_priv->timeout);
+
+ down(&fw_lock);
+ if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) {
+ retval = -ENOENT;
+ release_firmware(fw_priv->fw);
+ *firmware_p = NULL;
+ }
+ fw_priv->fw = NULL;
+ up(&fw_lock);
+ class_device_unregister(class_dev);
+ goto out;
+
+error_kfree_fw:
+ kfree(firmware);
+ *firmware_p = NULL;
+out:
+ return retval;
+}
+
+/**
+ * release_firmware: - release the resource associated with a firmware image
+ **/
+void
+release_firmware(const struct firmware *fw)
+{
+ if (fw) {
+ vfree(fw->data);
+ kfree(fw);
+ }
+}
+
+/**
+ * register_firmware: - provide a firmware image for later usage
+ *
+ * Description:
+ * Make sure that @data will be available by requesting firmware @name.
+ *
+ * Note: This will not be possible until some kind of persistence
+ * is available.
+ **/
+void
+register_firmware(const char *name, const u8 *data, size_t size)
+{
+ /* This is meaningless without firmware caching, so until we
+ * decide if firmware caching is reasonable just leave it as a
+ * noop */
+}
+
+/* Async support */
+struct firmware_work {
+ struct work_struct work;
+ struct module *module;
+ const char *name;
+ struct device *device;
+ void *context;
+ void (*cont)(const struct firmware *fw, void *context);
+};
+
+static int
+request_firmware_work_func(void *arg)
+{
+ struct firmware_work *fw_work = arg;
+ const struct firmware *fw;
+ if (!arg) {
+ WARN_ON(1);
+ return 0;
+ }
+ daemonize("%s/%s", "firmware", fw_work->name);
+ request_firmware(&fw, fw_work->name, fw_work->device);
+ fw_work->cont(fw, fw_work->context);
+ release_firmware(fw);
+ module_put(fw_work->module);
+ kfree(fw_work);
+ return 0;
+}
+
+/**
+ * request_firmware_nowait:
+ *
+ * Description:
+ * Asynchronous variant of request_firmware() for contexts where
+ * it is not possible to sleep.
+ *
+ * @cont will be called asynchronously when the firmware request is over.
+ *
+ * @context will be passed over to @cont.
+ *
+ * @fw may be %NULL if firmware request fails.
+ *
+ **/
+int
+request_firmware_nowait(
+ struct module *module,
+ const char *name, struct device *device, void *context,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
+ GFP_ATOMIC);
+ int ret;
+
+ if (!fw_work)
+ return -ENOMEM;
+ if (!try_module_get(module)) {
+ kfree(fw_work);
+ return -EFAULT;
+ }
+
+ *fw_work = (struct firmware_work) {
+ .module = module,
+ .name = name,
+ .device = device,
+ .context = context,
+ .cont = cont,
+ };
+
+ ret = kernel_thread(request_firmware_work_func, fw_work,
+ CLONE_FS | CLONE_FILES);
+
+ if (ret < 0) {
+ fw_work->cont(NULL, fw_work->context);
+ return ret;
+ }
+ return 0;
+}
+
+static int __init
+firmware_class_init(void)
+{
+ int error;
+ error = class_register(&firmware_class);
+ if (error) {
+ printk(KERN_ERR "%s: class_register failed\n", __FUNCTION__);
+ return error;
+ }
+ error = class_create_file(&firmware_class, &class_attr_timeout);
+ if (error) {
+ printk(KERN_ERR "%s: class_create_file failed\n",
+ __FUNCTION__);
+ class_unregister(&firmware_class);
+ }
+ return error;
+
+}
+static void __exit
+firmware_class_exit(void)
+{
+ class_unregister(&firmware_class);
+}
+
+module_init(firmware_class_init);
+module_exit(firmware_class_exit);
+
+EXPORT_SYMBOL(release_firmware);
+EXPORT_SYMBOL(request_firmware);
+EXPORT_SYMBOL(request_firmware_nowait);
+EXPORT_SYMBOL(register_firmware);
diff --git a/drivers/base/init.c b/drivers/base/init.c
new file mode 100644
index 000000000000..a76ae5a221f3
--- /dev/null
+++ b/drivers/base/init.c
@@ -0,0 +1,43 @@
+/*
+ *
+ * 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>
+#include <linux/init.h>
+
+extern int devices_init(void);
+extern int buses_init(void);
+extern int classes_init(void);
+extern int firmware_init(void);
+extern int platform_bus_init(void);
+extern int system_bus_init(void);
+extern int cpu_dev_init(void);
+extern int attribute_container_init(void);
+/**
+ * driver_init - initialize driver model.
+ *
+ * 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 */
+ devices_init();
+ buses_init();
+ classes_init();
+ firmware_init();
+
+ /* These are also core pieces, but must come after the
+ * core core pieces.
+ */
+ platform_bus_init();
+ system_bus_init();
+ cpu_dev_init();
+ attribute_container_init();
+}
diff --git a/drivers/base/interface.c b/drivers/base/interface.c
new file mode 100644
index 000000000000..bd515843a0cb
--- /dev/null
+++ b/drivers/base/interface.c
@@ -0,0 +1,51 @@
+/*
+ * drivers/base/interface.c - common driverfs interface that's exported to
+ * the world for all devices.
+ *
+ * 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>
+#include <linux/err.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+
+/**
+ * detach_state - control the default power state for the device.
+ *
+ * This is the state the device enters when it's driver module is
+ * unloaded. The value is an unsigned integer, in the range of 0-4.
+ * '0' indicates 'On', so no action will be taken when the driver is
+ * unloaded. This is the default behavior.
+ * '4' indicates 'Off', meaning the driver core will call the driver's
+ * shutdown method to quiesce the device.
+ * 1-3 indicate a low-power state for the device to enter via the
+ * driver's suspend method.
+ */
+
+static ssize_t detach_show(struct device * dev, char * buf)
+{
+ return sprintf(buf, "%u\n", dev->detach_state);
+}
+
+static ssize_t detach_store(struct device * dev, const char * buf, size_t n)
+{
+ u32 state;
+ state = simple_strtoul(buf, NULL, 10);
+ if (state > 4)
+ return -EINVAL;
+ dev->detach_state = state;
+ return n;
+}
+
+static DEVICE_ATTR(detach_state, 0644, detach_show, detach_store);
+
+
+struct attribute * dev_default_attrs[] = {
+ &dev_attr_detach_state.attr,
+ NULL,
+};
diff --git a/drivers/base/map.c b/drivers/base/map.c
new file mode 100644
index 000000000000..2f455d86793c
--- /dev/null
+++ b/drivers/base/map.c
@@ -0,0 +1,155 @@
+/*
+ * linux/drivers/base/map.c
+ *
+ * (C) Copyright Al Viro 2002,2003
+ * Released under GPL v2.
+ *
+ * NOTE: data structure needs to be changed. It works, but for large dev_t
+ * it will be too slow. It is isolated, though, so these changes will be
+ * local to that file.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kdev_t.h>
+#include <linux/kobject.h>
+#include <linux/kobj_map.h>
+
+struct kobj_map {
+ struct probe {
+ struct probe *next;
+ dev_t dev;
+ unsigned long range;
+ struct module *owner;
+ kobj_probe_t *get;
+ int (*lock)(dev_t, void *);
+ void *data;
+ } *probes[255];
+ struct semaphore *sem;
+};
+
+int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
+ struct module *module, kobj_probe_t *probe,
+ int (*lock)(dev_t, void *), void *data)
+{
+ unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
+ unsigned index = MAJOR(dev);
+ unsigned i;
+ struct probe *p;
+
+ if (n > 255)
+ n = 255;
+
+ p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);
+
+ if (p == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++, p++) {
+ p->owner = module;
+ p->get = probe;
+ p->lock = lock;
+ p->dev = dev;
+ p->range = range;
+ p->data = data;
+ }
+ down(domain->sem);
+ for (i = 0, p -= n; i < n; i++, p++, index++) {
+ struct probe **s = &domain->probes[index % 255];
+ while (*s && (*s)->range < range)
+ s = &(*s)->next;
+ p->next = *s;
+ *s = p;
+ }
+ up(domain->sem);
+ return 0;
+}
+
+void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range)
+{
+ unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
+ unsigned index = MAJOR(dev);
+ unsigned i;
+ struct probe *found = NULL;
+
+ if (n > 255)
+ n = 255;
+
+ down(domain->sem);
+ for (i = 0; i < n; i++, index++) {
+ struct probe **s;
+ for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) {
+ struct probe *p = *s;
+ if (p->dev == dev && p->range == range) {
+ *s = p->next;
+ if (!found)
+ found = p;
+ break;
+ }
+ }
+ }
+ up(domain->sem);
+ kfree(found);
+}
+
+struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
+{
+ struct kobject *kobj;
+ struct probe *p;
+ unsigned long best = ~0UL;
+
+retry:
+ down(domain->sem);
+ for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
+ struct kobject *(*probe)(dev_t, int *, void *);
+ struct module *owner;
+ void *data;
+
+ if (p->dev > dev || p->dev + p->range - 1 < dev)
+ continue;
+ if (p->range - 1 >= best)
+ break;
+ if (!try_module_get(p->owner))
+ continue;
+ owner = p->owner;
+ data = p->data;
+ probe = p->get;
+ best = p->range - 1;
+ *index = dev - p->dev;
+ if (p->lock && p->lock(dev, data) < 0) {
+ module_put(owner);
+ continue;
+ }
+ up(domain->sem);
+ kobj = probe(dev, index, data);
+ /* Currently ->owner protects _only_ ->probe() itself. */
+ module_put(owner);
+ if (kobj)
+ return kobj;
+ goto retry;
+ }
+ up(domain->sem);
+ return NULL;
+}
+
+struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct semaphore *sem)
+{
+ struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
+ struct probe *base = kmalloc(sizeof(struct probe), GFP_KERNEL);
+ int i;
+
+ if ((p == NULL) || (base == NULL)) {
+ kfree(p);
+ kfree(base);
+ return NULL;
+ }
+
+ memset(base, 0, sizeof(struct probe));
+ base->dev = 1;
+ base->range = ~0;
+ base->get = base_probe;
+ for (i = 0; i < 255; i++)
+ p->probes[i] = base;
+ p->sem = sem;
+ return p;
+}
diff --git a/drivers/base/node.c b/drivers/base/node.c
new file mode 100644
index 000000000000..583d57ec49a8
--- /dev/null
+++ b/drivers/base/node.c
@@ -0,0 +1,161 @@
+/*
+ * drivers/base/node.c - basic Node class support
+ */
+
+#include <linux/sysdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/node.h>
+#include <linux/hugetlb.h>
+#include <linux/cpumask.h>
+#include <linux/topology.h>
+#include <linux/nodemask.h>
+
+static struct sysdev_class node_class = {
+ set_kset_name("node"),
+};
+
+
+static ssize_t node_read_cpumap(struct sys_device * dev, char * buf)
+{
+ struct node *node_dev = to_node(dev);
+ cpumask_t mask = node_to_cpumask(node_dev->sysdev.id);
+ int len;
+
+ /* 2004/06/03: buf currently PAGE_SIZE, need > 1 char per 4 bits. */
+ BUILD_BUG_ON(MAX_NUMNODES/4 > PAGE_SIZE/2);
+
+ len = cpumask_scnprintf(buf, PAGE_SIZE-1, mask);
+ len += sprintf(buf + len, "\n");
+ return len;
+}
+
+static SYSDEV_ATTR(cpumap, S_IRUGO, node_read_cpumap, NULL);
+
+#define K(x) ((x) << (PAGE_SHIFT - 10))
+static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
+{
+ int n;
+ int nid = dev->id;
+ struct sysinfo i;
+ unsigned long inactive;
+ unsigned long active;
+ unsigned long free;
+
+ si_meminfo_node(&i, nid);
+ __get_zone_counts(&active, &inactive, &free, NODE_DATA(nid));
+
+ n = sprintf(buf, "\n"
+ "Node %d MemTotal: %8lu kB\n"
+ "Node %d MemFree: %8lu kB\n"
+ "Node %d MemUsed: %8lu kB\n"
+ "Node %d Active: %8lu kB\n"
+ "Node %d Inactive: %8lu kB\n"
+ "Node %d HighTotal: %8lu kB\n"
+ "Node %d HighFree: %8lu kB\n"
+ "Node %d LowTotal: %8lu kB\n"
+ "Node %d LowFree: %8lu kB\n",
+ nid, K(i.totalram),
+ nid, K(i.freeram),
+ nid, K(i.totalram - i.freeram),
+ nid, K(active),
+ nid, K(inactive),
+ nid, K(i.totalhigh),
+ nid, K(i.freehigh),
+ nid, K(i.totalram - i.totalhigh),
+ nid, K(i.freeram - i.freehigh));
+ n += hugetlb_report_node_meminfo(nid, buf + n);
+ return n;
+}
+
+#undef K
+static SYSDEV_ATTR(meminfo, S_IRUGO, node_read_meminfo, NULL);
+
+static ssize_t node_read_numastat(struct sys_device * dev, char * buf)
+{
+ unsigned long numa_hit, numa_miss, interleave_hit, numa_foreign;
+ unsigned long local_node, other_node;
+ int i, cpu;
+ pg_data_t *pg = NODE_DATA(dev->id);
+ numa_hit = 0;
+ numa_miss = 0;
+ interleave_hit = 0;
+ numa_foreign = 0;
+ local_node = 0;
+ other_node = 0;
+ 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];
+ numa_hit += ps->numa_hit;
+ numa_miss += ps->numa_miss;
+ numa_foreign += ps->numa_foreign;
+ interleave_hit += ps->interleave_hit;
+ local_node += ps->local_node;
+ other_node += ps->other_node;
+ }
+ }
+ return sprintf(buf,
+ "numa_hit %lu\n"
+ "numa_miss %lu\n"
+ "numa_foreign %lu\n"
+ "interleave_hit %lu\n"
+ "local_node %lu\n"
+ "other_node %lu\n",
+ numa_hit,
+ numa_miss,
+ numa_foreign,
+ interleave_hit,
+ local_node,
+ other_node);
+}
+static SYSDEV_ATTR(numastat, S_IRUGO, node_read_numastat, NULL);
+
+static ssize_t node_read_distance(struct sys_device * dev, char * buf)
+{
+ int nid = dev->id;
+ int len = 0;
+ int i;
+
+ /* buf currently PAGE_SIZE, need ~4 chars per node */
+ BUILD_BUG_ON(MAX_NUMNODES*4 > PAGE_SIZE/2);
+
+ for_each_online_node(i)
+ len += sprintf(buf + len, "%s%d", i ? " " : "", node_distance(nid, i));
+
+ len += sprintf(buf + len, "\n");
+ return len;
+}
+static SYSDEV_ATTR(distance, S_IRUGO, node_read_distance, NULL);
+
+
+/*
+ * register_node - Setup a driverfs device for a node.
+ * @num - Node number to use when creating the device.
+ *
+ * Initialize and register the node device.
+ */
+int __init register_node(struct node *node, int num, struct node *parent)
+{
+ int error;
+
+ node->sysdev.id = num;
+ node->sysdev.cls = &node_class;
+ error = sysdev_register(&node->sysdev);
+
+ if (!error){
+ sysdev_create_file(&node->sysdev, &attr_cpumap);
+ sysdev_create_file(&node->sysdev, &attr_meminfo);
+ sysdev_create_file(&node->sysdev, &attr_numastat);
+ sysdev_create_file(&node->sysdev, &attr_distance);
+ }
+ return error;
+}
+
+
+int __init register_node_type(void)
+{
+ return sysdev_class_register(&node_class);
+}
+postcore_initcall(register_node_type);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
new file mode 100644
index 000000000000..996cbb4d5087
--- /dev/null
+++ b/drivers/base/platform.c
@@ -0,0 +1,350 @@
+/*
+ * platform.c - platform 'pseudo' bus for legacy devices
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ *
+ * Please see Documentation/driver-model/platform.txt for more
+ * information.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/bootmem.h>
+#include <linux/err.h>
+
+struct device platform_bus = {
+ .bus_id = "platform",
+};
+
+/**
+ * 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)
+{
+ int i;
+
+ for (i = 0; i < dev->num_resources; i++) {
+ struct resource *r = &dev->resource[i];
+
+ if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
+ IORESOURCE_IRQ|IORESOURCE_DMA))
+ == type)
+ if (num-- == 0)
+ return r;
+ }
+ return NULL;
+}
+
+/**
+ * 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)
+{
+ struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);
+
+ return r ? r->start : 0;
+}
+
+/**
+ * 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)
+{
+ int i;
+
+ for (i = 0; i < dev->num_resources; i++) {
+ struct resource *r = &dev->resource[i];
+
+ if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
+ IORESOURCE_IRQ|IORESOURCE_DMA)) == type)
+ if (!strcmp(r->name, name))
+ return r;
+ }
+ return NULL;
+}
+
+/**
+ * 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);
+
+ return r ? r->start : 0;
+}
+
+/**
+ * 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)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < num; i++) {
+ ret = platform_device_register(devs[i]);
+ if (ret) {
+ while (--i >= 0)
+ platform_device_unregister(devs[i]);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * platform_device_register - add a platform-level device
+ * @dev: platform device we're adding
+ *
+ */
+int platform_device_register(struct platform_device * pdev)
+{
+ int i, ret = 0;
+
+ if (!pdev)
+ return -EINVAL;
+
+ if (!pdev->dev.parent)
+ pdev->dev.parent = &platform_bus;
+
+ pdev->dev.bus = &platform_bus_type;
+
+ if (pdev->id != -1)
+ snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);
+ else
+ strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
+
+ for (i = 0; i < pdev->num_resources; i++) {
+ struct resource *p, *r = &pdev->resource[i];
+
+ if (r->name == NULL)
+ r->name = pdev->dev.bus_id;
+
+ p = r->parent;
+ if (!p) {
+ if (r->flags & IORESOURCE_MEM)
+ p = &iomem_resource;
+ else if (r->flags & IORESOURCE_IO)
+ p = &ioport_resource;
+ }
+
+ if (p && request_resource(p, r)) {
+ printk(KERN_ERR
+ "%s: failed to claim resource %d\n",
+ pdev->dev.bus_id, i);
+ ret = -EBUSY;
+ goto failed;
+ }
+ }
+
+ pr_debug("Registering platform device '%s'. Parent at %s\n",
+ pdev->dev.bus_id, pdev->dev.parent->bus_id);
+
+ ret = device_register(&pdev->dev);
+ if (ret == 0)
+ return ret;
+
+ failed:
+ while (--i >= 0)
+ if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
+ release_resource(&pdev->resource[i]);
+ return ret;
+}
+
+/**
+ * platform_device_unregister - remove a platform-level device
+ * @dev: platform device we're removing
+ *
+ * Note that this function will also release all memory- and port-based
+ * resources owned by the device (@dev->resource).
+ */
+void platform_device_unregister(struct platform_device * pdev)
+{
+ int i;
+
+ if (pdev) {
+ for (i = 0; i < pdev->num_resources; i++) {
+ struct resource *r = &pdev->resource[i];
+ if (r->flags & (IORESOURCE_MEM|IORESOURCE_IO))
+ release_resource(r);
+ }
+
+ device_unregister(&pdev->dev);
+ }
+}
+
+struct platform_object {
+ struct platform_device pdev;
+ struct resource resources[0];
+};
+
+static void platform_device_release_simple(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ kfree(container_of(pdev, struct platform_object, pdev));
+}
+
+/**
+ * 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 iwithout waiting for the last reference to the device
+ * to be dropped.
+ */
+struct platform_device *platform_device_register_simple(char *name, unsigned int id,
+ struct resource *res, unsigned int num)
+{
+ struct platform_object *pobj;
+ int retval;
+
+ pobj = kmalloc(sizeof(struct platform_object) + sizeof(struct resource) * num, GFP_KERNEL);
+ if (!pobj) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ memset(pobj, 0, sizeof(*pobj));
+ pobj->pdev.name = name;
+ pobj->pdev.id = id;
+ pobj->pdev.dev.release = platform_device_release_simple;
+
+ if (num) {
+ memcpy(pobj->resources, res, sizeof(struct resource) * num);
+ pobj->pdev.resource = pobj->resources;
+ pobj->pdev.num_resources = num;
+ }
+
+ retval = platform_device_register(&pobj->pdev);
+ if (retval)
+ goto error;
+
+ return &pobj->pdev;
+
+error:
+ kfree(pobj);
+ return ERR_PTR(retval);
+}
+
+
+/**
+ * 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.
+ */
+
+static int platform_match(struct device * dev, struct device_driver * drv)
+{
+ struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+
+ return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
+}
+
+static int platform_suspend(struct device * dev, pm_message_t state)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->suspend) {
+ ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE);
+ if (ret == 0)
+ ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE);
+ if (ret == 0)
+ ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN);
+ }
+ return ret;
+}
+
+static int platform_resume(struct device * dev)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->resume) {
+ ret = dev->driver->resume(dev, RESUME_POWER_ON);
+ if (ret == 0)
+ ret = dev->driver->resume(dev, RESUME_RESTORE_STATE);
+ if (ret == 0)
+ ret = dev->driver->resume(dev, RESUME_ENABLE);
+ }
+ return ret;
+}
+
+struct bus_type platform_bus_type = {
+ .name = "platform",
+ .match = platform_match,
+ .suspend = platform_suspend,
+ .resume = platform_resume,
+};
+
+int __init platform_bus_init(void)
+{
+ device_register(&platform_bus);
+ return bus_register(&platform_bus_type);
+}
+
+#ifndef ARCH_HAS_DMA_GET_REQUIRED_MASK
+u64 dma_get_required_mask(struct device *dev)
+{
+ u32 low_totalram = ((max_pfn - 1) << PAGE_SHIFT);
+ u32 high_totalram = ((max_pfn - 1) >> (32 - PAGE_SHIFT));
+ u64 mask;
+
+ if (!high_totalram) {
+ /* convert to mask just covering totalram */
+ low_totalram = (1 << (fls(low_totalram) - 1));
+ low_totalram += low_totalram - 1;
+ mask = low_totalram;
+ } else {
+ high_totalram = (1 << (fls(high_totalram) - 1));
+ high_totalram += high_totalram - 1;
+ mask = (((u64)high_totalram) << 32) + 0xffffffff;
+ }
+ return mask & *dev->dma_mask;
+}
+EXPORT_SYMBOL_GPL(dma_get_required_mask);
+#endif
+
+EXPORT_SYMBOL_GPL(platform_bus);
+EXPORT_SYMBOL_GPL(platform_bus_type);
+EXPORT_SYMBOL_GPL(platform_device_register);
+EXPORT_SYMBOL_GPL(platform_device_register_simple);
+EXPORT_SYMBOL_GPL(platform_device_unregister);
+EXPORT_SYMBOL_GPL(platform_get_irq);
+EXPORT_SYMBOL_GPL(platform_get_resource);
+EXPORT_SYMBOL_GPL(platform_get_irq_byname);
+EXPORT_SYMBOL_GPL(platform_get_resource_byname);
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
new file mode 100644
index 000000000000..c0219ad94aca
--- /dev/null
+++ b/drivers/base/power/Makefile
@@ -0,0 +1,6 @@
+obj-y := shutdown.o
+obj-$(CONFIG_PM) += main.o suspend.o resume.o runtime.o sysfs.o
+
+ifeq ($(CONFIG_DEBUG_DRIVER),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
new file mode 100644
index 000000000000..15e6a8f951f1
--- /dev/null
+++ b/drivers/base/power/main.c
@@ -0,0 +1,99 @@
+/*
+ * drivers/base/power/main.c - Where the driver meets power management.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ *
+ * This file is released under the GPLv2
+ *
+ *
+ * The driver model core calls device_pm_add() when a device is registered.
+ * This will intialize the embedded device_pm_info object in the device
+ * and add it to the list of power-controlled devices. sysfs entries for
+ * controlling device power management will also be added.
+ *
+ * A different set of lists than the global subsystem list are used to
+ * keep track of power info because we use different lists to hold
+ * devices based on what stage of the power management process they
+ * are in. The power domain dependencies may also differ from the
+ * ancestral dependencies that the subsystem list maintains.
+ */
+
+#include <linux/config.h>
+#include <linux/device.h>
+#include "power.h"
+
+LIST_HEAD(dpm_active);
+LIST_HEAD(dpm_off);
+LIST_HEAD(dpm_off_irq);
+
+DECLARE_MUTEX(dpm_sem);
+DECLARE_MUTEX(dpm_list_sem);
+
+/*
+ * PM Reference Counting.
+ */
+
+static inline void device_pm_hold(struct device * dev)
+{
+ if (dev)
+ atomic_inc(&dev->power.pm_users);
+}
+
+static inline void device_pm_release(struct device * dev)
+{
+ if (dev)
+ atomic_dec(&dev->power.pm_users);
+}
+
+
+/**
+ * device_pm_set_parent - Specify power dependency.
+ * @dev: Device who needs power.
+ * @parent: Device that supplies power.
+ *
+ * This function is used to manually describe a power-dependency
+ * relationship. It may be used to specify a transversal relationship
+ * (where the power supplier is not the physical (or electrical)
+ * ancestor of a specific device.
+ * The effect of this is that the supplier will not be powered down
+ * before the power dependent.
+ */
+
+void device_pm_set_parent(struct device * dev, struct device * parent)
+{
+ struct device * old_parent = dev->power.pm_parent;
+ device_pm_release(old_parent);
+ dev->power.pm_parent = parent;
+ device_pm_hold(parent);
+}
+EXPORT_SYMBOL_GPL(device_pm_set_parent);
+
+int device_pm_add(struct device * dev)
+{
+ int error;
+
+ pr_debug("PM: Adding info for %s:%s\n",
+ dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
+ atomic_set(&dev->power.pm_users, 0);
+ down(&dpm_list_sem);
+ list_add_tail(&dev->power.entry, &dpm_active);
+ device_pm_set_parent(dev, dev->parent);
+ if ((error = dpm_sysfs_add(dev)))
+ list_del(&dev->power.entry);
+ up(&dpm_list_sem);
+ return error;
+}
+
+void device_pm_remove(struct device * dev)
+{
+ pr_debug("PM: Removing info for %s:%s\n",
+ dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
+ down(&dpm_list_sem);
+ dpm_sysfs_remove(dev);
+ device_pm_release(dev->power.pm_parent);
+ list_del_init(&dev->power.entry);
+ up(&dpm_list_sem);
+}
+
+
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
new file mode 100644
index 000000000000..e5eda746f2a6
--- /dev/null
+++ b/drivers/base/power/power.h
@@ -0,0 +1,106 @@
+
+
+enum {
+ DEVICE_PM_ON,
+ DEVICE_PM1,
+ DEVICE_PM2,
+ DEVICE_PM3,
+ DEVICE_PM_OFF,
+};
+
+/*
+ * shutdown.c
+ */
+
+extern int device_detach_shutdown(struct device *);
+extern void device_shutdown(void);
+
+
+#ifdef CONFIG_PM
+
+/*
+ * main.c
+ */
+
+/*
+ * Used to synchronize global power management operations.
+ */
+extern struct semaphore dpm_sem;
+
+/*
+ * Used to serialize changes to the dpm_* lists.
+ */
+extern struct semaphore dpm_list_sem;
+
+/*
+ * The PM lists.
+ */
+extern struct list_head dpm_active;
+extern struct list_head dpm_off;
+extern struct list_head dpm_off_irq;
+
+
+static inline struct dev_pm_info * to_pm_info(struct list_head * entry)
+{
+ return container_of(entry, struct dev_pm_info, entry);
+}
+
+static inline struct device * to_device(struct list_head * entry)
+{
+ return container_of(to_pm_info(entry), struct device, power);
+}
+
+extern int device_pm_add(struct device *);
+extern void device_pm_remove(struct device *);
+
+/*
+ * sysfs.c
+ */
+
+extern int dpm_sysfs_add(struct device *);
+extern void dpm_sysfs_remove(struct device *);
+
+/*
+ * resume.c
+ */
+
+extern void dpm_resume(void);
+extern void dpm_power_up(void);
+extern int resume_device(struct device *);
+
+/*
+ * suspend.c
+ */
+extern int suspend_device(struct device *, pm_message_t);
+
+
+/*
+ * runtime.c
+ */
+
+extern int dpm_runtime_suspend(struct device *, pm_message_t);
+extern void dpm_runtime_resume(struct device *);
+
+#else /* CONFIG_PM */
+
+
+static inline int device_pm_add(struct device * dev)
+{
+ return 0;
+}
+static inline void device_pm_remove(struct device * dev)
+{
+
+}
+
+static inline int dpm_runtime_suspend(struct device * dev, pm_message_t state)
+{
+ return 0;
+}
+
+static inline void dpm_runtime_resume(struct device * dev)
+{
+
+}
+
+#endif
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
new file mode 100644
index 000000000000..f8f5055754d6
--- /dev/null
+++ b/drivers/base/power/resume.c
@@ -0,0 +1,112 @@
+/*
+ * resume.c - Functions for waking devices up.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/device.h>
+#include "power.h"
+
+extern int sysdev_resume(void);
+
+
+/**
+ * resume_device - Restore state for one device.
+ * @dev: Device.
+ *
+ */
+
+int resume_device(struct device * dev)
+{
+ if (dev->bus && dev->bus->resume)
+ return dev->bus->resume(dev);
+ return 0;
+}
+
+
+
+void dpm_resume(void)
+{
+ down(&dpm_list_sem);
+ while(!list_empty(&dpm_off)) {
+ struct list_head * entry = dpm_off.next;
+ struct device * dev = to_device(entry);
+
+ get_device(dev);
+ list_del_init(entry);
+ list_add_tail(entry, &dpm_active);
+
+ up(&dpm_list_sem);
+ if (!dev->power.prev_state)
+ resume_device(dev);
+ down(&dpm_list_sem);
+ put_device(dev);
+ }
+ up(&dpm_list_sem);
+}
+
+
+/**
+ * device_resume - Restore state of each device in system.
+ *
+ * Walk the dpm_off list, remove each entry, resume the device,
+ * then add it to the dpm_active list.
+ */
+
+void device_resume(void)
+{
+ down(&dpm_sem);
+ dpm_resume();
+ up(&dpm_sem);
+}
+
+EXPORT_SYMBOL_GPL(device_resume);
+
+
+/**
+ * device_power_up_irq - 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_suspended list.
+ *
+ * Interrupts must be disabled when calling this.
+ */
+
+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);
+
+ get_device(dev);
+ list_del_init(entry);
+ list_add_tail(entry, &dpm_active);
+ resume_device(dev);
+ put_device(dev);
+ }
+}
+
+
+/**
+ * device_pm_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.
+ * Called with interrupts disabled.
+ */
+
+void device_power_up(void)
+{
+ sysdev_resume();
+ dpm_power_up();
+}
+
+EXPORT_SYMBOL_GPL(device_power_up);
+
+
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
new file mode 100644
index 000000000000..325962d80191
--- /dev/null
+++ b/drivers/base/power/runtime.c
@@ -0,0 +1,81 @@
+/*
+ * drivers/base/power/runtime.c - Handling dynamic device power management.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ *
+ */
+
+#include <linux/device.h>
+#include "power.h"
+
+
+static void runtime_resume(struct device * dev)
+{
+ dev_dbg(dev, "resuming\n");
+ if (!dev->power.power_state)
+ return;
+ if (!resume_device(dev))
+ dev->power.power_state = 0;
+}
+
+
+/**
+ * dpm_runtime_resume - Power one device back on.
+ * @dev: Device.
+ *
+ * Bring one device back to the on state by first powering it
+ * on, then restoring state. We only operate on devices that aren't
+ * already on.
+ * FIXME: We need to handle devices that are in an unknown state.
+ */
+
+void dpm_runtime_resume(struct device * dev)
+{
+ down(&dpm_sem);
+ runtime_resume(dev);
+ up(&dpm_sem);
+}
+
+
+/**
+ * dpm_runtime_suspend - Put one device in low-power state.
+ * @dev: Device.
+ * @state: State to enter.
+ */
+
+int dpm_runtime_suspend(struct device * dev, pm_message_t state)
+{
+ int error = 0;
+
+ down(&dpm_sem);
+ if (dev->power.power_state == state)
+ goto Done;
+
+ if (dev->power.power_state)
+ runtime_resume(dev);
+
+ if (!(error = suspend_device(dev, state)))
+ dev->power.power_state = state;
+ Done:
+ up(&dpm_sem);
+ return error;
+}
+
+
+/**
+ * dpm_set_power_state - Update power_state field.
+ * @dev: Device.
+ * @state: Power state device is in.
+ *
+ * This is an update mechanism for drivers to notify the core
+ * what power state a device is in. Device probing code may not
+ * always be able to tell, but we need accurate information to
+ * work reliably.
+ */
+void dpm_set_power_state(struct device * dev, pm_message_t state)
+{
+ down(&dpm_sem);
+ dev->power.power_state = state;
+ up(&dpm_sem);
+}
diff --git a/drivers/base/power/shutdown.c b/drivers/base/power/shutdown.c
new file mode 100644
index 000000000000..d1e023fbe169
--- /dev/null
+++ b/drivers/base/power/shutdown.c
@@ -0,0 +1,67 @@
+/*
+ * 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/config.h>
+#include <linux/device.h>
+#include <asm/semaphore.h>
+
+#include "power.h"
+
+#define to_dev(node) container_of(node, struct device, kobj.entry)
+
+extern struct subsystem devices_subsys;
+
+
+int device_detach_shutdown(struct device * dev)
+{
+ if (!dev->detach_state)
+ return 0;
+
+ if (dev->detach_state == DEVICE_PM_OFF) {
+ if (dev->driver && dev->driver->shutdown)
+ dev->driver->shutdown(dev);
+ return 0;
+ }
+ return dpm_runtime_suspend(dev, dev->detach_state);
+}
+
+
+/**
+ * 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.
+ */
+
+extern int sysdev_shutdown(void);
+
+/**
+ * device_shutdown - call ->shutdown() on each device to shutdown.
+ */
+void device_shutdown(void)
+{
+ struct device * dev;
+
+ down_write(&devices_subsys.rwsem);
+ list_for_each_entry_reverse(dev, &devices_subsys.kset.list, kobj.entry) {
+ pr_debug("shutting down %s: ", dev->bus_id);
+ if (dev->driver && dev->driver->shutdown) {
+ pr_debug("Ok\n");
+ dev->driver->shutdown(dev);
+ } else
+ pr_debug("Ignored.\n");
+ }
+ up_write(&devices_subsys.rwsem);
+
+ sysdev_shutdown();
+}
+
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
new file mode 100644
index 000000000000..a0b5cf689e63
--- /dev/null
+++ b/drivers/base/power/suspend.c
@@ -0,0 +1,144 @@
+/*
+ * suspend.c - Functions for putting devices to sleep.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/device.h>
+#include "power.h"
+
+extern int sysdev_suspend(pm_message_t state);
+
+/*
+ * 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.
+ */
+
+
+/**
+ * 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;
+
+ dev_dbg(dev, "suspending\n");
+
+ dev->power.prev_state = dev->power.power_state;
+
+ if (dev->bus && dev->bus->suspend && !dev->power.power_state)
+ error = dev->bus->suspend(dev, state);
+
+ return error;
+}
+
+
+/**
+ * device_suspend - Save state and stop all devices in system.
+ * @state: Power state to put each device in.
+ *
+ * Walk the dpm_active list, call ->suspend() for each device, and move
+ * it to dpm_off.
+ * Check the return value for each. If it returns 0, then we move the
+ * the device to the dpm_off list. If it returns -EAGAIN, we move it to
+ * the dpm_off_irq list. 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)
+{
+ int error = 0;
+
+ down(&dpm_sem);
+ down(&dpm_list_sem);
+ while (!list_empty(&dpm_active) && error == 0) {
+ struct list_head * entry = dpm_active.prev;
+ struct device * dev = to_device(entry);
+
+ get_device(dev);
+ up(&dpm_list_sem);
+
+ error = suspend_device(dev, state);
+
+ down(&dpm_list_sem);
+
+ /* Check if the device got removed */
+ if (!list_empty(&dev->power.entry)) {
+ /* Move it to the dpm_off or dpm_off_irq list */
+ if (!error) {
+ list_del(&dev->power.entry);
+ list_add(&dev->power.entry, &dpm_off);
+ } else if (error == -EAGAIN) {
+ list_del(&dev->power.entry);
+ list_add(&dev->power.entry, &dpm_off_irq);
+ error = 0;
+ }
+ }
+ if (error)
+ printk(KERN_ERR "Could not suspend device %s: "
+ "error %d\n", kobject_name(&dev->kobj), error);
+ put_device(dev);
+ }
+ up(&dpm_list_sem);
+ if (error)
+ dpm_resume();
+ up(&dpm_sem);
+ return error;
+}
+
+EXPORT_SYMBOL_GPL(device_suspend);
+
+
+/**
+ * device_power_down - Shut down special devices.
+ * @state: Power state to enter.
+ *
+ * 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.
+ */
+
+int device_power_down(pm_message_t state)
+{
+ int error = 0;
+ struct device * dev;
+
+ list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) {
+ if ((error = suspend_device(dev, state)))
+ break;
+ }
+ if (error)
+ goto Error;
+ if ((error = sysdev_suspend(state)))
+ goto Error;
+ Done:
+ 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);
+
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
new file mode 100644
index 000000000000..6ac96349a8e8
--- /dev/null
+++ b/drivers/base/power/sysfs.c
@@ -0,0 +1,68 @@
+/*
+ * drivers/base/power/sysfs.c - sysfs entries for device PM
+ */
+
+#include <linux/device.h>
+#include "power.h"
+
+
+/**
+ * state - Control current power state of device
+ *
+ * show() returns the current power state of the device. '0' indicates
+ * the device is on. Other values (1-3) indicate the device is in a low
+ * power state.
+ *
+ * store() sets the current power state, which is an integer value
+ * between 0-3. If the device is on ('0'), and the value written is
+ * greater than 0, then the device is placed directly into the low-power
+ * state (via its driver's ->suspend() method).
+ * If the device is currently in a low-power state, and the value is 0,
+ * the device is powered back on (via the ->resume() method).
+ * If the device is in a low-power state, and a different low-power state
+ * is requested, the device is first resumed, then suspended into the new
+ * low-power state.
+ */
+
+static ssize_t state_show(struct device * dev, 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)
+{
+ u32 state;
+ char * rest;
+ int error = 0;
+
+ state = simple_strtoul(buf, &rest, 10);
+ if (*rest)
+ return -EINVAL;
+ if (state)
+ error = dpm_runtime_suspend(dev, state);
+ else
+ dpm_runtime_resume(dev);
+ return error ? error : n;
+}
+
+static DEVICE_ATTR(state, 0644, state_show, state_store);
+
+
+static struct attribute * power_attrs[] = {
+ &dev_attr_state.attr,
+ NULL,
+};
+static struct attribute_group pm_attr_group = {
+ .name = "power",
+ .attrs = power_attrs,
+};
+
+int dpm_sysfs_add(struct device * dev)
+{
+ return sysfs_create_group(&dev->kobj, &pm_attr_group);
+}
+
+void dpm_sysfs_remove(struct device * dev)
+{
+ sysfs_remove_group(&dev->kobj, &pm_attr_group);
+}
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
new file mode 100644
index 000000000000..cff5a6a2c784
--- /dev/null
+++ b/drivers/base/sys.c
@@ -0,0 +1,397 @@
+/*
+ * sys.c - pseudo-bus for system 'devices' (cpus, PICs, timers, etc)
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * 2002-3 Open Source Development Lab
+ *
+ * This file is released under the GPLv2
+ *
+ * This exports a 'system' bus type.
+ * By default, a 'sys' bus gets added to the root of the system. There will
+ * always be core system devices. Devices can use sysdev_register() to
+ * add themselves as children of the system bus.
+ */
+
+#include <linux/config.h>
+#include <linux/sysdev.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+
+extern struct subsystem 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)
+
+
+static ssize_t
+sysdev_show(struct kobject * kobj, struct attribute * attr, char * buffer)
+{
+ struct sys_device * sysdev = to_sysdev(kobj);
+ struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr);
+
+ if (sysdev_attr->show)
+ return sysdev_attr->show(sysdev, buffer);
+ return 0;
+}
+
+
+static ssize_t
+sysdev_store(struct kobject * kobj, struct attribute * attr,
+ const char * buffer, size_t count)
+{
+ struct sys_device * sysdev = to_sysdev(kobj);
+ struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr);
+
+ if (sysdev_attr->store)
+ return sysdev_attr->store(sysdev, buffer, count);
+ return 0;
+}
+
+static struct sysfs_ops sysfs_ops = {
+ .show = sysdev_show,
+ .store = sysdev_store,
+};
+
+static struct kobj_type ktype_sysdev = {
+ .sysfs_ops = &sysfs_ops,
+};
+
+
+int sysdev_create_file(struct sys_device * s, struct sysdev_attribute * a)
+{
+ return sysfs_create_file(&s->kobj, &a->attr);
+}
+
+
+void sysdev_remove_file(struct sys_device * s, struct sysdev_attribute * a)
+{
+ sysfs_remove_file(&s->kobj, &a->attr);
+}
+
+EXPORT_SYMBOL_GPL(sysdev_create_file);
+EXPORT_SYMBOL_GPL(sysdev_remove_file);
+
+/*
+ * declare system_subsys
+ */
+static decl_subsys(system, &ktype_sysdev, NULL);
+
+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.subsys = &system_subsys;
+ kset_set_kset_s(cls, system_subsys);
+ return kset_register(&cls->kset);
+}
+
+void sysdev_class_unregister(struct sysdev_class * cls)
+{
+ pr_debug("Unregistering sysdev class '%s'\n",
+ kobject_name(&cls->kset.kobj));
+ kset_unregister(&cls->kset);
+}
+
+EXPORT_SYMBOL_GPL(sysdev_class_register);
+EXPORT_SYMBOL_GPL(sysdev_class_unregister);
+
+
+static LIST_HEAD(sysdev_drivers);
+static DECLARE_MUTEX(sysdev_drivers_lock);
+
+/**
+ * sysdev_driver_register - Register auxillary driver
+ * @cls: Device class driver belongs to.
+ * @drv: Driver.
+ *
+ * If @cls is valid, then @drv is inserted into @cls->drivers to be
+ * called on each operation on devices of that class. The refcount
+ * of @cls is incremented.
+ * Otherwise, @drv is inserted into sysdev_drivers, and called for
+ * each device.
+ */
+
+int sysdev_driver_register(struct sysdev_class * cls,
+ struct sysdev_driver * drv)
+{
+ down(&sysdev_drivers_lock);
+ if (cls && kset_get(&cls->kset)) {
+ list_add_tail(&drv->entry, &cls->drivers);
+
+ /* If devices of this class already exist, tell the driver */
+ if (drv->add) {
+ struct sys_device *dev;
+ list_for_each_entry(dev, &cls->kset.list, kobj.entry)
+ drv->add(dev);
+ }
+ } else
+ list_add_tail(&drv->entry, &sysdev_drivers);
+ up(&sysdev_drivers_lock);
+ return 0;
+}
+
+
+/**
+ * sysdev_driver_unregister - Remove an auxillary driver.
+ * @cls: Class driver belongs to.
+ * @drv: Driver.
+ */
+void sysdev_driver_unregister(struct sysdev_class * cls,
+ struct sysdev_driver * drv)
+{
+ down(&sysdev_drivers_lock);
+ list_del_init(&drv->entry);
+ if (cls) {
+ if (drv->remove) {
+ struct sys_device *dev;
+ list_for_each_entry(dev, &cls->kset.list, kobj.entry)
+ drv->remove(dev);
+ }
+ kset_put(&cls->kset);
+ }
+ up(&sysdev_drivers_lock);
+}
+
+EXPORT_SYMBOL_GPL(sysdev_driver_register);
+EXPORT_SYMBOL_GPL(sysdev_driver_unregister);
+
+
+
+/**
+ * sysdev_register - add a system device to the tree
+ * @sysdev: device in question
+ *
+ */
+int sysdev_register(struct sys_device * sysdev)
+{
+ int error;
+ struct sysdev_class * cls = sysdev->cls;
+
+ if (!cls)
+ return -EINVAL;
+
+ /* 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);
+
+ if (!error) {
+ struct sysdev_driver * drv;
+
+ down(&sysdev_drivers_lock);
+ /* Generic notification is implicit, because it's that
+ * code that should have called us.
+ */
+
+ /* Notify global drivers */
+ list_for_each_entry(drv, &sysdev_drivers, entry) {
+ if (drv->add)
+ drv->add(sysdev);
+ }
+
+ /* Notify class auxillary drivers */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->add)
+ drv->add(sysdev);
+ }
+ up(&sysdev_drivers_lock);
+ }
+ return error;
+}
+
+void sysdev_unregister(struct sys_device * sysdev)
+{
+ struct sysdev_driver * drv;
+
+ down(&sysdev_drivers_lock);
+ list_for_each_entry(drv, &sysdev_drivers, entry) {
+ if (drv->remove)
+ drv->remove(sysdev);
+ }
+
+ list_for_each_entry(drv, &sysdev->cls->drivers, entry) {
+ if (drv->remove)
+ drv->remove(sysdev);
+ }
+ up(&sysdev_drivers_lock);
+
+ kobject_unregister(&sysdev->kobj);
+}
+
+
+
+/**
+ * sysdev_shutdown - Shut down all system devices.
+ *
+ * Loop over each class of system devices, and the devices in each
+ * of those classes. For each device, we call the shutdown method for
+ * each driver registered for the device - the globals, the auxillaries,
+ * and the class driver.
+ *
+ * Note: The list is iterated in reverse order, so that we shut down
+ * child devices before we shut down thier parents. The list ordering
+ * is guaranteed by virtue of the fact that child devices are registered
+ * after their parents.
+ */
+
+void sysdev_shutdown(void)
+{
+ struct sysdev_class * cls;
+
+ pr_debug("Shutting Down System Devices\n");
+
+ down(&sysdev_drivers_lock);
+ list_for_each_entry_reverse(cls, &system_subsys.kset.list,
+ kset.kobj.entry) {
+ struct sys_device * sysdev;
+
+ pr_debug("Shutting down type '%s':\n",
+ kobject_name(&cls->kset.kobj));
+
+ list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
+ struct sysdev_driver * drv;
+ pr_debug(" %s\n", kobject_name(&sysdev->kobj));
+
+ /* Call global drivers first. */
+ list_for_each_entry(drv, &sysdev_drivers, entry) {
+ if (drv->shutdown)
+ drv->shutdown(sysdev);
+ }
+
+ /* Call auxillary drivers next. */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->shutdown)
+ drv->shutdown(sysdev);
+ }
+
+ /* Now call the generic one */
+ if (cls->shutdown)
+ cls->shutdown(sysdev);
+ }
+ }
+ up(&sysdev_drivers_lock);
+}
+
+
+/**
+ * sysdev_suspend - Suspend all system devices.
+ * @state: Power state to enter.
+ *
+ * We perform an almost identical operation as sys_device_shutdown()
+ * above, though calling ->suspend() instead. Interrupts are disabled
+ * when this called. Devices are responsible for both saving state and
+ * quiescing or powering down the device.
+ *
+ * This is only called by the device PM core, so we let them handle
+ * all synchronization.
+ */
+
+int sysdev_suspend(u32 state)
+{
+ struct sysdev_class * cls;
+
+ pr_debug("Suspending System Devices\n");
+
+ list_for_each_entry_reverse(cls, &system_subsys.kset.list,
+ kset.kobj.entry) {
+ struct sys_device * sysdev;
+
+ pr_debug("Suspending type '%s':\n",
+ kobject_name(&cls->kset.kobj));
+
+ list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
+ struct sysdev_driver * drv;
+ pr_debug(" %s\n", kobject_name(&sysdev->kobj));
+
+ /* Call global drivers first. */
+ list_for_each_entry(drv, &sysdev_drivers, entry) {
+ if (drv->suspend)
+ drv->suspend(sysdev, state);
+ }
+
+ /* Call auxillary drivers next. */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->suspend)
+ drv->suspend(sysdev, state);
+ }
+
+ /* Now call the generic one */
+ if (cls->suspend)
+ cls->suspend(sysdev, state);
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * sysdev_resume - Bring system devices back to life.
+ *
+ * Similar to sys_device_suspend(), but we iterate the list forwards
+ * to guarantee that parent devices are resumed before their children.
+ *
+ * Note: Interrupts are disabled when called.
+ */
+
+int sysdev_resume(void)
+{
+ struct sysdev_class * cls;
+
+ pr_debug("Resuming System Devices\n");
+
+ list_for_each_entry(cls, &system_subsys.kset.list, kset.kobj.entry) {
+ struct sys_device * sysdev;
+
+ pr_debug("Resuming type '%s':\n",
+ kobject_name(&cls->kset.kobj));
+
+ list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
+ struct sysdev_driver * drv;
+ pr_debug(" %s\n", kobject_name(&sysdev->kobj));
+
+ /* First, call the class-specific one */
+ if (cls->resume)
+ cls->resume(sysdev);
+
+ /* Call auxillary drivers next. */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->resume)
+ drv->resume(sysdev);
+ }
+
+ /* Call global drivers. */
+ list_for_each_entry(drv, &sysdev_drivers, entry) {
+ if (drv->resume)
+ drv->resume(sysdev);
+ }
+
+ }
+ }
+ return 0;
+}
+
+
+int __init system_bus_init(void)
+{
+ system_subsys.kset.kobj.parent = &devices_subsys.kset.kobj;
+ return subsystem_register(&system_subsys);
+}
+
+EXPORT_SYMBOL_GPL(sysdev_register);
+EXPORT_SYMBOL_GPL(sysdev_unregister);
diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
new file mode 100644
index 000000000000..6c2b447a3336
--- /dev/null
+++ b/drivers/base/transport_class.c
@@ -0,0 +1,275 @@
+/*
+ * transport_class.c - implementation of generic transport classes
+ * using attribute_containers
+ *
+ * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
+ *
+ * This file is licensed under GPLv2
+ *
+ * The basic idea here is to allow any "device controller" (which
+ * would most often be a Host Bus Adapter" to use the services of one
+ * or more tranport classes for performing transport specific
+ * services. Transport specific services are things that the generic
+ * command layer doesn't want to know about (speed settings, line
+ * condidtioning, etc), but which the user might be interested in.
+ * Thus, the HBA's use the routines exported by the transport classes
+ * to perform these functions. The transport classes export certain
+ * values to the user via sysfs using attribute containers.
+ *
+ * Note: because not every HBA will care about every transport
+ * attribute, there's a many to one relationship that goes like this:
+ *
+ * transport class<-----attribute container<----class device
+ *
+ * Usually the attribute container is per-HBA, but the design doesn't
+ * mandate that. Although most of the services will be specific to
+ * the actual external storage connection used by the HBA, the generic
+ * transport class is framed entirely in terms of generic devices to
+ * allow it to be used by any physical HBA in the system.
+ */
+#include <linux/attribute_container.h>
+#include <linux/transport_class.h>
+
+/**
+ * transport_class_register - register an initial transport class
+ *
+ * @tclass: a pointer to the transport class structure to be initialised
+ *
+ * The transport class contains an embedded class which is used to
+ * identify it. The caller should initialise this structure with
+ * zeros and then generic class must have been initialised with the
+ * actual transport class unique name. There's a macro
+ * DECLARE_TRANSPORT_CLASS() to do this (declared classes still must
+ * be registered).
+ *
+ * Returns 0 on success or error on failure.
+ */
+int transport_class_register(struct transport_class *tclass)
+{
+ return class_register(&tclass->class);
+}
+EXPORT_SYMBOL_GPL(transport_class_register);
+
+/**
+ * transport_class_unregister - unregister a previously registered class
+ *
+ * @tclass: The transport class to unregister
+ *
+ * Must be called prior to deallocating the memory for the transport
+ * class.
+ */
+void transport_class_unregister(struct transport_class *tclass)
+{
+ class_unregister(&tclass->class);
+}
+EXPORT_SYMBOL_GPL(transport_class_unregister);
+
+static int anon_transport_dummy_function(struct device *dev)
+{
+ /* do nothing */
+ return 0;
+}
+
+/**
+ * anon_transport_class_register - register an anonymous class
+ *
+ * @atc: The anon transport class to register
+ *
+ * The anonymous transport class contains both a transport class and a
+ * container. The idea of an anonymous class is that it never
+ * actually has any device attributes associated with it (and thus
+ * saves on container storage). So it can only be used for triggering
+ * events. Use prezero and then use DECLARE_ANON_TRANSPORT_CLASS() to
+ * initialise the anon transport class storage.
+ */
+int anon_transport_class_register(struct anon_transport_class *atc)
+{
+ int error;
+ atc->container.class = &atc->tclass.class;
+ attribute_container_set_no_classdevs(&atc->container);
+ error = attribute_container_register(&atc->container);
+ if (error)
+ return error;
+ atc->tclass.setup = anon_transport_dummy_function;
+ atc->tclass.remove = anon_transport_dummy_function;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(anon_transport_class_register);
+
+/**
+ * anon_transport_class_unregister - unregister an anon class
+ *
+ * @atc: Pointer to the anon transport class to unregister
+ *
+ * Must be called prior to deallocating the memory for the anon
+ * transport class.
+ */
+void anon_transport_class_unregister(struct anon_transport_class *atc)
+{
+ attribute_container_unregister(&atc->container);
+}
+EXPORT_SYMBOL_GPL(anon_transport_class_unregister);
+
+static int transport_setup_classdev(struct attribute_container *cont,
+ struct device *dev,
+ struct class_device *classdev)
+{
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+
+ if (tclass->setup)
+ tclass->setup(dev);
+
+ return 0;
+}
+
+/**
+ * transport_setup_device - declare a new dev for transport class association
+ * but don't make it visible yet.
+ *
+ * @dev: the generic device representing the entity being added
+ *
+ * Usually, dev represents some component in the HBA system (either
+ * the HBA itself or a device remote across the HBA bus). This
+ * routine is simply a trigger point to see if any set of transport
+ * classes wishes to associate with the added device. This allocates
+ * storage for the class device and initialises it, but does not yet
+ * add it to the system or add attributes to it (you do this with
+ * transport_add_device). If you have no need for a separate setup
+ * and add operations, use transport_register_device (see
+ * transport_class.h).
+ */
+
+void transport_setup_device(struct device *dev)
+{
+ attribute_container_add_device(dev, transport_setup_classdev);
+}
+EXPORT_SYMBOL_GPL(transport_setup_device);
+
+static int transport_add_class_device(struct attribute_container *cont,
+ struct device *dev,
+ struct class_device *classdev)
+{
+ int error = attribute_container_add_class_device(classdev);
+ struct transport_container *tcont =
+ attribute_container_to_transport_container(cont);
+
+ if (!error && tcont->statistics)
+ error = sysfs_create_group(&classdev->kobj, tcont->statistics);
+
+ return error;
+}
+
+
+/**
+ * transport_add_device - declare a new dev for transport class association
+ *
+ * @dev: the generic device representing the entity being added
+ *
+ * Usually, dev represents some component in the HBA system (either
+ * the HBA itself or a device remote across the HBA bus). This
+ * routine is simply a trigger point used to add the device to the
+ * system and register attributes for it.
+ */
+
+void transport_add_device(struct device *dev)
+{
+ attribute_container_device_trigger(dev, transport_add_class_device);
+}
+EXPORT_SYMBOL_GPL(transport_add_device);
+
+static int transport_configure(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+
+ if (tclass->configure)
+ tclass->configure(dev);
+
+ return 0;
+}
+
+/**
+ * transport_configure_device - configure an already set up device
+ *
+ * @dev: generic device representing device to be configured
+ *
+ * The idea of configure is simply to provide a point within the setup
+ * process to allow the transport class to extract information from a
+ * device after it has been setup. This is used in SCSI because we
+ * have to have a setup device to begin using the HBA, but after we
+ * send the initial inquiry, we use configure to extract the device
+ * parameters. The device need not have been added to be configured.
+ */
+void transport_configure_device(struct device *dev)
+{
+ attribute_container_trigger(dev, transport_configure);
+}
+EXPORT_SYMBOL_GPL(transport_configure_device);
+
+static int transport_remove_classdev(struct attribute_container *cont,
+ struct device *dev,
+ struct class_device *classdev)
+{
+ struct transport_container *tcont =
+ attribute_container_to_transport_container(cont);
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+
+ if (tclass->remove)
+ tclass->remove(dev);
+
+ if (tclass->remove != anon_transport_dummy_function) {
+ if (tcont->statistics)
+ sysfs_remove_group(&classdev->kobj, tcont->statistics);
+ attribute_container_class_device_del(classdev);
+ }
+
+ return 0;
+}
+
+
+/**
+ * transport_remove_device - remove the visibility of a device
+ *
+ * @dev: generic device to remove
+ *
+ * This call removes the visibility of the device (to the user from
+ * sysfs), but does not destroy it. To eliminate a device entirely
+ * you must also call transport_destroy_device. If you don't need to
+ * do remove and destroy as separate operations, use
+ * transport_unregister_device() (see transport_class.h) which will
+ * perform both calls for you.
+ */
+void transport_remove_device(struct device *dev)
+{
+ attribute_container_device_trigger(dev, transport_remove_classdev);
+}
+EXPORT_SYMBOL_GPL(transport_remove_device);
+
+static void transport_destroy_classdev(struct attribute_container *cont,
+ struct device *dev,
+ struct class_device *classdev)
+{
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+
+ if (tclass->remove != anon_transport_dummy_function)
+ class_device_put(classdev);
+}
+
+
+/**
+ * transport_destroy_device - destroy a removed device
+ *
+ * @dev: device to eliminate from the transport class.
+ *
+ * This call triggers the elimination of storage associated with the
+ * transport classdev. Note: all it really does is relinquish a
+ * reference to the classdev. The memory will not be freed until the
+ * last reference goes to zero. Note also that the classdev retains a
+ * reference count on dev, so dev too will remain for as long as the
+ * transport class device remains around.
+ */
+void transport_destroy_device(struct device *dev)
+{
+ attribute_container_remove_device(dev, transport_destroy_classdev);
+}
+EXPORT_SYMBOL_GPL(transport_destroy_device);