summaryrefslogtreecommitdiff
path: root/libdde-linux26/contrib/drivers/base
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2013-07-27 22:15:01 +0000
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2013-07-27 22:15:01 +0000
commit7996a3d79d55b7f879dfd62e202bbfe2963718d3 (patch)
tree8d9f6759fec4099b9be503c11c7ed174f7204980 /libdde-linux26/contrib/drivers/base
parent4fbe7358c7747a9165f776eb19addbb9baf7def2 (diff)
really properly move files
Diffstat (limited to 'libdde-linux26/contrib/drivers/base')
-rw-r--r--libdde-linux26/contrib/drivers/base/attribute_container.c440
-rw-r--r--libdde-linux26/contrib/drivers/base/base.h105
-rw-r--r--libdde-linux26/contrib/drivers/base/bus.c1039
-rw-r--r--libdde-linux26/contrib/drivers/base/cpu.c250
-rw-r--r--libdde-linux26/contrib/drivers/base/dd.c383
-rw-r--r--libdde-linux26/contrib/drivers/base/devres.c646
-rw-r--r--libdde-linux26/contrib/drivers/base/driver.c277
-rw-r--r--libdde-linux26/contrib/drivers/base/map.c155
-rw-r--r--libdde-linux26/contrib/drivers/base/platform.c988
-rw-r--r--libdde-linux26/contrib/drivers/base/power/power.h49
-rw-r--r--libdde-linux26/contrib/drivers/base/sys.c525
11 files changed, 4857 insertions, 0 deletions
diff --git a/libdde-linux26/contrib/drivers/base/attribute_container.c b/libdde-linux26/contrib/drivers/base/attribute_container.c
new file mode 100644
index 00000000..b9cda053
--- /dev/null
+++ b/libdde-linux26/contrib/drivers/base/attribute_container.c
@@ -0,0 +1,440 @@
+/*
+ * 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>
+#include <linux/mutex.h>
+
+#include "base.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 klist_node node;
+ struct attribute_container *cont;
+ struct device classdev;
+};
+
+static void internal_container_klist_get(struct klist_node *n)
+{
+ struct internal_container *ic =
+ container_of(n, struct internal_container, node);
+ get_device(&ic->classdev);
+}
+
+static void internal_container_klist_put(struct klist_node *n)
+{
+ struct internal_container *ic =
+ container_of(n, struct internal_container, node);
+ put_device(&ic->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 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 LIST_HEAD(attribute_container_list);
+
+static DEFINE_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);
+ klist_init(&cont->containers,internal_container_klist_get,
+ internal_container_klist_put);
+
+ mutex_lock(&attribute_container_mutex);
+ list_add_tail(&cont->node, &attribute_container_list);
+ mutex_unlock(&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;
+ mutex_lock(&attribute_container_mutex);
+ spin_lock(&cont->containers.k_lock);
+ if (!list_empty(&cont->containers.k_list))
+ goto out;
+ retval = 0;
+ list_del(&cont->node);
+ out:
+ spin_unlock(&cont->containers.k_lock);
+ mutex_unlock(&attribute_container_mutex);
+ return retval;
+
+}
+EXPORT_SYMBOL_GPL(attribute_container_unregister);
+
+/* private function used as class release */
+static void attribute_container_release(struct device *classdev)
+{
+ struct internal_container *ic
+ = container_of(classdev, struct internal_container, classdev);
+ struct device *dev = classdev->parent;
+
+ 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
+ * 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 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 device *))
+{
+ struct attribute_container *cont;
+
+ mutex_lock(&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 = kzalloc(sizeof(*ic), GFP_KERNEL);
+ if (!ic) {
+ dev_printk(KERN_ERR, dev, "failed to allocate class container\n");
+ continue;
+ }
+
+ ic->cont = cont;
+ device_initialize(&ic->classdev);
+ ic->classdev.parent = get_device(dev);
+ ic->classdev.class = cont->class;
+ cont->class->dev_release = attribute_container_release;
+ dev_set_name(&ic->classdev, dev_name(dev));
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else
+ attribute_container_add_class_device(&ic->classdev);
+ klist_add_tail(&ic->node, &cont->containers);
+ }
+ mutex_unlock(&attribute_container_mutex);
+}
+
+/* FIXME: can't break out of this unless klist_iter_exit is also
+ * called before doing the break
+ */
+#define klist_for_each_entry(pos, head, member, iter) \
+ for (klist_iter_init(head, iter); (pos = ({ \
+ struct klist_node *n = klist_next(iter); \
+ n ? container_of(n, typeof(*pos), member) : \
+ ({ klist_iter_exit(iter) ; NULL; }); \
+ }) ) != NULL; )
+
+
+/**
+ * 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 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
+ * 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 device *))
+{
+ struct attribute_container *cont;
+
+ mutex_lock(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ struct internal_container *ic;
+ struct klist_iter iter;
+
+ if (attribute_container_no_classdevs(cont))
+ continue;
+
+ if (!cont->match(cont, dev))
+ continue;
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+ if (dev != ic->classdev.parent)
+ continue;
+ klist_del(&ic->node);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else {
+ attribute_container_remove_attrs(&ic->classdev);
+ device_unregister(&ic->classdev);
+ }
+ }
+ }
+ mutex_unlock(&attribute_container_mutex);
+}
+
+/**
+ * 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 device *))
+{
+ struct attribute_container *cont;
+
+ mutex_lock(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ struct internal_container *ic;
+ struct klist_iter iter;
+
+ if (!cont->match(cont, dev))
+ continue;
+
+ if (attribute_container_no_classdevs(cont)) {
+ fn(cont, dev, NULL);
+ continue;
+ }
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+ if (dev == ic->classdev.parent)
+ fn(cont, dev, &ic->classdev);
+ }
+ }
+ mutex_unlock(&attribute_container_mutex);
+}
+
+/**
+ * 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;
+
+ mutex_lock(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ if (cont->match(cont, dev))
+ fn(cont, dev);
+ }
+ mutex_unlock(&attribute_container_mutex);
+}
+
+/**
+ * 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 device *classdev)
+{
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+ struct device_attribute **attrs = cont->attrs;
+ int i, error;
+
+ BUG_ON(attrs && cont->grp);
+
+ if (!attrs && !cont->grp)
+ return 0;
+
+ if (cont->grp)
+ return sysfs_create_group(&classdev->kobj, cont->grp);
+
+ for (i = 0; attrs[i]; i++) {
+ error = device_create_file(classdev, attrs[i]);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+/**
+ * attribute_container_add_class_device - same function as device_add
+ *
+ * @classdev: the class device to add
+ *
+ * This performs essentially the same function as 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 device *classdev)
+{
+ int error = device_add(classdev);
+ if (error)
+ return error;
+ return attribute_container_add_attrs(classdev);
+}
+
+/**
+ * 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 device *classdev)
+{
+ return attribute_container_add_class_device(classdev);
+}
+
+/**
+ * attribute_container_remove_attrs - remove any attribute files
+ *
+ * @classdev: The class device to remove the files from
+ *
+ */
+void
+attribute_container_remove_attrs(struct device *classdev)
+{
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+ struct device_attribute **attrs = cont->attrs;
+ int i;
+
+ if (!attrs && !cont->grp)
+ return;
+
+ if (cont->grp) {
+ sysfs_remove_group(&classdev->kobj, cont->grp);
+ return ;
+ }
+
+ for (i = 0; attrs[i]; i++)
+ device_remove_file(classdev, attrs[i]);
+}
+
+/**
+ * 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
+ * device_del.
+ */
+void
+attribute_container_class_device_del(struct device *classdev)
+{
+ attribute_container_remove_attrs(classdev);
+ device_del(classdev);
+}
+
+/**
+ * attribute_container_find_class_device - find the corresponding class_device
+ *
+ * @cont: the container
+ * @dev: the generic device
+ *
+ * Looks up the device in the container's list of class devices and returns
+ * the corresponding class_device.
+ */
+struct device *
+attribute_container_find_class_device(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct device *cdev = NULL;
+ struct internal_container *ic;
+ struct klist_iter iter;
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+ if (ic->classdev.parent == dev) {
+ cdev = &ic->classdev;
+ /* FIXME: must exit iterator then break */
+ klist_iter_exit(&iter);
+ break;
+ }
+ }
+
+ return cdev;
+}
+EXPORT_SYMBOL_GPL(attribute_container_find_class_device);
diff --git a/libdde-linux26/contrib/drivers/base/base.h b/libdde-linux26/contrib/drivers/base/base.h
new file mode 100644
index 00000000..9f50f1b5
--- /dev/null
+++ b/libdde-linux26/contrib/drivers/base/base.h
@@ -0,0 +1,105 @@
+
+/**
+ * struct bus_type_private - structure to hold the private to the driver core portions of the bus_type structure.
+ *
+ * @subsys - the struct kset that defines this bus. This is the main kobject
+ * @drivers_kset - the list of drivers associated with this bus
+ * @devices_kset - the list of devices associated with this bus
+ * @klist_devices - the klist to iterate over the @devices_kset
+ * @klist_drivers - the klist to iterate over the @drivers_kset
+ * @bus_notifier - the bus notifier list for anything that cares about things
+ * on this bus.
+ * @bus - pointer back to the struct bus_type that this structure is associated
+ * with.
+ *
+ * This structure is the one that is the actual kobject allowing struct
+ * bus_type to be statically allocated safely. Nothing outside of the driver
+ * core should ever touch these fields.
+ */
+struct bus_type_private {
+ struct kset subsys;
+ struct kset *drivers_kset;
+ struct kset *devices_kset;
+ struct klist klist_devices;
+ struct klist klist_drivers;
+ struct blocking_notifier_head bus_notifier;
+ unsigned int drivers_autoprobe:1;
+ struct bus_type *bus;
+};
+
+struct driver_private {
+ struct kobject kobj;
+ struct klist klist_devices;
+ struct klist_node knode_bus;
+ struct module_kobject *mkobj;
+ struct device_driver *driver;
+};
+#define to_driver(obj) container_of(obj, struct driver_private, kobj)
+
+
+/**
+ * struct class_private - structure to hold the private to the driver core portions of the class structure.
+ *
+ * @class_subsys - the struct kset that defines this class. This is the main kobject
+ * @class_devices - list of devices associated with this class
+ * @class_interfaces - list of class_interfaces associated with this class
+ * @class_dirs - "glue" directory for virtual devices associated with this class
+ * @class_mutex - mutex to protect the children, devices, and interfaces lists.
+ * @class - pointer back to the struct class that this structure is associated
+ * with.
+ *
+ * This structure is the one that is the actual kobject allowing struct
+ * class to be statically allocated safely. Nothing outside of the driver
+ * core should ever touch these fields.
+ */
+struct class_private {
+ struct kset class_subsys;
+ struct klist class_devices;
+ struct list_head class_interfaces;
+ struct kset class_dirs;
+ struct mutex class_mutex;
+ struct class *class;
+};
+#define to_class(obj) \
+ container_of(obj, struct class_private, class_subsys.kobj)
+
+/* initialisation functions */
+extern int devices_init(void);
+extern int buses_init(void);
+extern int classes_init(void);
+extern int firmware_init(void);
+#ifdef CONFIG_SYS_HYPERVISOR
+extern int hypervisor_init(void);
+#else
+static inline int hypervisor_init(void) { return 0; }
+#endif
+extern int platform_bus_init(void);
+extern int system_bus_init(void);
+extern int cpu_dev_init(void);
+
+extern int bus_add_device(struct device *dev);
+extern void bus_attach_device(struct device *dev);
+extern void bus_remove_device(struct device *dev);
+
+extern int bus_add_driver(struct device_driver *drv);
+extern void bus_remove_driver(struct device_driver *drv);
+
+extern void driver_detach(struct device_driver *drv);
+extern int driver_probe_device(struct device_driver *drv, struct device *dev);
+
+extern void sysdev_shutdown(void);
+
+extern char *make_class_name(const char *name, struct kobject *kobj);
+
+extern int devres_release_all(struct device *dev);
+
+extern struct kset *devices_kset;
+
+#if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS)
+extern void module_add_driver(struct module *mod, struct device_driver *drv);
+extern void module_remove_driver(struct device_driver *drv);
+#else
+static inline void module_add_driver(struct module *mod,
+ struct device_driver *drv) { }
+static inline void module_remove_driver(struct device_driver *drv) { }
+#endif
diff --git a/libdde-linux26/contrib/drivers/base/bus.c b/libdde-linux26/contrib/drivers/base/bus.c
new file mode 100644
index 00000000..83f32b89
--- /dev/null
+++ b/libdde-linux26/contrib/drivers/base/bus.c
@@ -0,0 +1,1039 @@
+/*
+ * bus.c - bus driver management
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/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_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
+#define to_bus(obj) container_of(obj, struct bus_type_private, subsys.kobj)
+
+/*
+ * sysfs bindings for drivers
+ */
+
+#define to_drv_attr(_attr) container_of(_attr, struct driver_attribute, attr)
+
+
+static int __must_check bus_rescan_devices_helper(struct device *dev,
+ void *data);
+
+static struct bus_type *bus_get(struct bus_type *bus)
+{
+ if (bus) {
+ kset_get(&bus->p->subsys);
+ return bus;
+ }
+ return NULL;
+}
+
+static void bus_put(struct bus_type *bus)
+{
+ if (bus)
+ kset_put(&bus->p->subsys);
+}
+
+static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct driver_attribute *drv_attr = to_drv_attr(attr);
+ struct driver_private *drv_priv = to_driver(kobj);
+ ssize_t ret = -EIO;
+
+ if (drv_attr->show)
+ ret = drv_attr->show(drv_priv->driver, 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 driver_private *drv_priv = to_driver(kobj);
+ ssize_t ret = -EIO;
+
+ if (drv_attr->store)
+ ret = drv_attr->store(drv_priv->driver, 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 driver_private *drv_priv = to_driver(kobj);
+
+ pr_debug("driver: '%s': %s\n", kobject_name(kobj), __func__);
+ kfree(drv_priv);
+}
+
+static struct kobj_type driver_ktype = {
+ .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_private *bus_priv = to_bus(kobj);
+ ssize_t ret = 0;
+
+ if (bus_attr->show)
+ ret = bus_attr->show(bus_priv->bus, buf);
+ return ret;
+}
+
+static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bus_attribute *bus_attr = to_bus_attr(attr);
+ struct bus_type_private *bus_priv = to_bus(kobj);
+ ssize_t ret = 0;
+
+ if (bus_attr->store)
+ ret = bus_attr->store(bus_priv->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 (bus_get(bus)) {
+ error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
+ bus_put(bus);
+ } else
+ error = -EINVAL;
+ return error;
+}
+EXPORT_SYMBOL_GPL(bus_create_file);
+
+void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
+{
+ if (bus_get(bus)) {
+ sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);
+ bus_put(bus);
+ }
+}
+EXPORT_SYMBOL_GPL(bus_remove_file);
+
+static struct kobj_type bus_ktype = {
+ .sysfs_ops = &bus_sysfs_ops,
+};
+
+static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
+{
+ struct kobj_type *ktype = get_ktype(kobj);
+
+ if (ktype == &bus_ktype)
+ return 1;
+ return 0;
+}
+
+static struct kset_uevent_ops bus_uevent_ops = {
+ .filter = bus_uevent_filter,
+};
+
+static struct kset *bus_kset;
+
+
+#ifdef CONFIG_HOTPLUG
+/* Manually detach a device from its associated driver. */
+static ssize_t driver_unbind(struct device_driver *drv,
+ const char *buf, size_t count)
+{
+ struct bus_type *bus = bus_get(drv->bus);
+ struct device *dev;
+ int err = -ENODEV;
+
+ dev = bus_find_device_by_name(bus, NULL, buf);
+ if (dev && dev->driver == drv) {
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ device_release_driver(dev);
+ if (dev->parent)
+ up(&dev->parent->sem);
+ err = count;
+ }
+ put_device(dev);
+ bus_put(bus);
+ return err;
+}
+static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind);
+
+/*
+ * Manually attach a device to a driver.
+ * Note: the driver must want to bind to the device,
+ * it is not possible to override the driver's id table.
+ */
+static ssize_t driver_bind(struct device_driver *drv,
+ const char *buf, size_t count)
+{
+ struct bus_type *bus = bus_get(drv->bus);
+ struct device *dev;
+ int err = -ENODEV;
+
+ dev = bus_find_device_by_name(bus, NULL, buf);
+ if (dev && dev->driver == NULL) {
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ down(&dev->sem);
+ err = driver_probe_device(drv, dev);
+ up(&dev->sem);
+ if (dev->parent)
+ up(&dev->parent->sem);
+
+ if (err > 0) {
+ /* success */
+ err = count;
+ } else if (err == 0) {
+ /* driver didn't accept device */
+ err = -ENODEV;
+ }
+ }
+ put_device(dev);
+ bus_put(bus);
+ return err;
+}
+static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);
+
+static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
+{
+ return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
+}
+
+static ssize_t store_drivers_autoprobe(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ if (buf[0] == '0')
+ bus->p->drivers_autoprobe = 0;
+ else
+ bus->p->drivers_autoprobe = 1;
+ return count;
+}
+
+static ssize_t store_drivers_probe(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ struct device *dev;
+
+ dev = bus_find_device_by_name(bus, NULL, buf);
+ if (!dev)
+ return -ENODEV;
+ if (bus_rescan_devices_helper(dev, NULL) != 0)
+ return -EINVAL;
+ return count;
+}
+#endif
+
+static struct device *next_device(struct klist_iter *i)
+{
+ struct klist_node *n = klist_next(i);
+ return n ? container_of(n, struct device, knode_bus) : NULL;
+}
+
+/**
+ * bus_for_each_dev - device iterator.
+ * @bus: bus type.
+ * @start: device to start iterating from.
+ * @data: data for the callback.
+ * @fn: function to be called for each device.
+ *
+ * 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 *))
+{
+ struct klist_iter i;
+ struct device *dev;
+ int error = 0;
+
+ if (!bus)
+ return -EINVAL;
+
+ klist_iter_init_node(&bus->p->klist_devices, &i,
+ (start ? &start->knode_bus : NULL));
+ while ((dev = next_device(&i)) && !error)
+ error = fn(dev, data);
+ klist_iter_exit(&i);
+ return error;
+}
+EXPORT_SYMBOL_GPL(bus_for_each_dev);
+
+/**
+ * bus_find_device - device iterator for locating a particular device.
+ * @bus: bus type
+ * @start: Device to begin with
+ * @data: Data to pass to match function
+ * @match: Callback function to check device
+ *
+ * This is similar to the bus_for_each_dev() function above, but it
+ * returns a reference to a device that is 'found' for later use, as
+ * determined by the @match callback.
+ *
+ * The callback should return 0 if the device doesn't match and non-zero
+ * if it does. If the callback returns non-zero, this function will
+ * return to the caller and not iterate over any more devices.
+ */
+struct device *bus_find_device(struct bus_type *bus,
+ struct device *start, void *data,
+ int (*match)(struct device *dev, void *data))
+{
+ struct klist_iter i;
+ struct device *dev;
+
+ if (!bus)
+ return NULL;
+
+ klist_iter_init_node(&bus->p->klist_devices, &i,
+ (start ? &start->knode_bus : NULL));
+ while ((dev = next_device(&i)))
+ if (match(dev, data) && get_device(dev))
+ break;
+ klist_iter_exit(&i);
+ return dev;
+}
+EXPORT_SYMBOL_GPL(bus_find_device);
+
+static int match_name(struct device *dev, void *data)
+{
+ const char *name = data;
+
+ return sysfs_streq(name, dev_name(dev));
+}
+
+/**
+ * bus_find_device_by_name - device iterator for locating a particular device of a specific name
+ * @bus: bus type
+ * @start: Device to begin with
+ * @name: name of the device to match
+ *
+ * This is similar to the bus_find_device() function above, but it handles
+ * searching by a name automatically, no need to write another strcmp matching
+ * function.
+ */
+struct device *bus_find_device_by_name(struct bus_type *bus,
+ struct device *start, const char *name)
+{
+ return bus_find_device(bus, start, (void *)name, match_name);
+}
+EXPORT_SYMBOL_GPL(bus_find_device_by_name);
+
+static struct device_driver *next_driver(struct klist_iter *i)
+{
+ struct klist_node *n = klist_next(i);
+ struct driver_private *drv_priv;
+
+ if (n) {
+ drv_priv = container_of(n, struct driver_private, knode_bus);
+ return drv_priv->driver;
+ }
+ return NULL;
+}
+
+/**
+ * bus_for_each_drv - driver iterator
+ * @bus: bus we're dealing with.
+ * @start: driver to start iterating on.
+ * @data: data to pass to the callback.
+ * @fn: function to call for each driver.
+ *
+ * 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 *))
+{
+ struct klist_iter i;
+ struct device_driver *drv;
+ int error = 0;
+
+ if (!bus)
+ return -EINVAL;
+
+ klist_iter_init_node(&bus->p->klist_drivers, &i,
+ start ? &start->p->knode_bus : NULL);
+ while ((drv = next_driver(&i)) && !error)
+ error = fn(drv, data);
+ klist_iter_exit(&i);
+ return error;
+}
+EXPORT_SYMBOL_GPL(bus_for_each_drv);
+
+static int device_add_attrs(struct bus_type *bus, struct device *dev)
+{
+ int error = 0;
+ int i;
+
+ if (!bus->dev_attrs)
+ return 0;
+
+ for (i = 0; attr_name(bus->dev_attrs[i]); i++) {
+ error = device_create_file(dev, &bus->dev_attrs[i]);
+ if (error) {
+ while (--i >= 0)
+ device_remove_file(dev, &bus->dev_attrs[i]);
+ break;
+ }
+ }
+ return error;
+}
+
+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]);
+ }
+}
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+static int make_deprecated_bus_links(struct device *dev)
+{
+ return sysfs_create_link(&dev->kobj,
+ &dev->bus->p->subsys.kobj, "bus");
+}
+
+static void remove_deprecated_bus_links(struct device *dev)
+{
+ sysfs_remove_link(&dev->kobj, "bus");
+}
+#else
+static inline int make_deprecated_bus_links(struct device *dev) { return 0; }
+static inline void remove_deprecated_bus_links(struct device *dev) { }
+#endif
+
+/**
+ * bus_add_device - add device to bus
+ * @dev: device being added
+ *
+ * - Add the device to its bus's list of devices.
+ * - Create link to device's bus.
+ */
+int bus_add_device(struct device *dev)
+{
+ struct bus_type *bus = bus_get(dev->bus);
+ int error = 0;
+
+ if (bus) {
+ pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
+ error = device_add_attrs(bus, dev);
+ if (error)
+ goto out_put;
+ error = sysfs_create_link(&bus->p->devices_kset->kobj,
+ &dev->kobj, dev_name(dev));
+ if (error)
+ goto out_id;
+ error = sysfs_create_link(&dev->kobj,
+ &dev->bus->p->subsys.kobj, "subsystem");
+ if (error)
+ goto out_subsys;
+ error = make_deprecated_bus_links(dev);
+ if (error)
+ goto out_deprecated;
+ }
+ return 0;
+
+out_deprecated:
+ sysfs_remove_link(&dev->kobj, "subsystem");
+out_subsys:
+ sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
+out_id:
+ device_remove_attrs(bus, dev);
+out_put:
+ bus_put(dev->bus);
+ return error;
+}
+
+/**
+ * bus_attach_device - add device to bus
+ * @dev: device tried to attach to a driver
+ *
+ * - Add device to bus's list of devices.
+ * - Try to attach to driver.
+ */
+void bus_attach_device(struct device *dev)
+{
+ struct bus_type *bus = dev->bus;
+ int ret = 0;
+
+ if (bus) {
+ if (bus->p->drivers_autoprobe)
+ ret = device_attach(dev);
+ WARN_ON(ret < 0);
+ if (ret >= 0)
+ klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
+ }
+}
+
+/**
+ * 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, "subsystem");
+ remove_deprecated_bus_links(dev);
+ sysfs_remove_link(&dev->bus->p->devices_kset->kobj,
+ dev_name(dev));
+ device_remove_attrs(dev->bus, dev);
+ if (klist_node_attached(&dev->knode_bus))
+ klist_del(&dev->knode_bus);
+
+ pr_debug("bus: '%s': remove device %s\n",
+ dev->bus->name, dev_name(dev));
+ device_release_driver(dev);
+ bus_put(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]);
+ }
+}
+
+#ifdef CONFIG_HOTPLUG
+/*
+ * Thanks to drivers making their tables __devinit, we can't allow manual
+ * bind and unbind from userspace unless CONFIG_HOTPLUG is enabled.
+ */
+static int __must_check add_bind_files(struct device_driver *drv)
+{
+ int ret;
+
+ ret = driver_create_file(drv, &driver_attr_unbind);
+ if (ret == 0) {
+ ret = driver_create_file(drv, &driver_attr_bind);
+ if (ret)
+ driver_remove_file(drv, &driver_attr_unbind);
+ }
+ return ret;
+}
+
+static void remove_bind_files(struct device_driver *drv)
+{
+ driver_remove_file(drv, &driver_attr_bind);
+ driver_remove_file(drv, &driver_attr_unbind);
+}
+
+static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
+static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
+ show_drivers_autoprobe, store_drivers_autoprobe);
+
+static int add_probe_files(struct bus_type *bus)
+{
+ int retval;
+
+ retval = bus_create_file(bus, &bus_attr_drivers_probe);
+ if (retval)
+ goto out;
+
+ retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
+ if (retval)
+ bus_remove_file(bus, &bus_attr_drivers_probe);
+out:
+ return retval;
+}
+
+static void remove_probe_files(struct bus_type *bus)
+{
+ bus_remove_file(bus, &bus_attr_drivers_autoprobe);
+ bus_remove_file(bus, &bus_attr_drivers_probe);
+}
+#else
+static inline int add_bind_files(struct device_driver *drv) { return 0; }
+static inline void remove_bind_files(struct device_driver *drv) {}
+static inline int add_probe_files(struct bus_type *bus) { return 0; }
+static inline void remove_probe_files(struct bus_type *bus) {}
+#endif
+
+static ssize_t driver_uevent_store(struct device_driver *drv,
+ const char *buf, size_t count)
+{
+ enum kobject_action action;
+
+ if (kobject_action_type(buf, count, &action) == 0)
+ kobject_uevent(&drv->p->kobj, action);
+ return count;
+}
+static DRIVER_ATTR(uevent, S_IWUSR, NULL, driver_uevent_store);
+
+/**
+ * bus_add_driver - Add a driver to the bus.
+ * @drv: driver.
+ */
+int bus_add_driver(struct device_driver *drv)
+{
+ struct bus_type *bus;
+ struct driver_private *priv;
+ int error = 0;
+
+ bus = bus_get(drv->bus);
+ if (!bus)
+ return -EINVAL;
+
+ pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ error = -ENOMEM;
+ goto out_put_bus;
+ }
+ klist_init(&priv->klist_devices, NULL, NULL);
+ priv->driver = drv;
+ drv->p = priv;
+ priv->kobj.kset = bus->p->drivers_kset;
+ error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
+ "%s", drv->name);
+ if (error)
+ goto out_unregister;
+
+ if (drv->bus->p->drivers_autoprobe) {
+ error = driver_attach(drv);
+ if (error)
+ goto out_unregister;
+ }
+ klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
+ module_add_driver(drv->owner, drv);
+
+ error = driver_create_file(drv, &driver_attr_uevent);
+ if (error) {
+ printk(KERN_ERR "%s: uevent attr (%s) failed\n",
+ __func__, drv->name);
+ }
+ error = driver_add_attrs(bus, drv);
+ if (error) {
+ /* How the hell do we get out of this pickle? Give up */
+ printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
+ __func__, drv->name);
+ }
+ error = add_bind_files(drv);
+ if (error) {
+ /* Ditto */
+ printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
+ __func__, drv->name);
+ }
+
+ kobject_uevent(&priv->kobj, KOBJ_ADD);
+ return error;
+out_unregister:
+ kobject_put(&priv->kobj);
+out_put_bus:
+ bus_put(bus);
+ 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)
+ return;
+
+ remove_bind_files(drv);
+ driver_remove_attrs(drv->bus, drv);
+ driver_remove_file(drv, &driver_attr_uevent);
+ klist_remove(&drv->p->knode_bus);
+ pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name);
+ driver_detach(drv);
+ module_remove_driver(drv);
+ kobject_put(&drv->p->kobj);
+ bus_put(drv->bus);
+}
+
+/* Helper for bus_rescan_devices's iter */
+static int __must_check bus_rescan_devices_helper(struct device *dev,
+ void *data)
+{
+ int ret = 0;
+
+ if (!dev->driver) {
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ ret = device_attach(dev);
+ if (dev->parent)
+ up(&dev->parent->sem);
+ }
+ return ret < 0 ? ret : 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 by calling device_attach() for the unbound devices.
+ */
+int bus_rescan_devices(struct bus_type *bus)
+{
+ return bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
+}
+EXPORT_SYMBOL_GPL(bus_rescan_devices);
+
+/**
+ * device_reprobe - remove driver for a device and probe for a new driver
+ * @dev: the device to reprobe
+ *
+ * This function detaches the attached driver (if any) for the given
+ * device and restarts the driver probing process. It is intended
+ * to use if probing criteria changed during a devices lifetime and
+ * driver attachment should change accordingly.
+ */
+int device_reprobe(struct device *dev)
+{
+ if (dev->driver) {
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ device_release_driver(dev);
+ if (dev->parent)
+ up(&dev->parent->sem);
+ }
+ return bus_rescan_devices_helper(dev, NULL);
+}
+EXPORT_SYMBOL_GPL(device_reprobe);
+
+/**
+ * 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.
+ */
+#if 0
+struct bus_type *find_bus(char *name)
+{
+ struct kobject *k = kset_find_obj(bus_kset, name);
+ return k ? to_bus(k) : NULL;
+}
+#endif /* 0 */
+
+
+/**
+ * bus_add_attrs - Add default attributes for this bus.
+ * @bus: Bus that has just been registered.
+ */
+
+static int bus_add_attrs(struct bus_type *bus)
+{
+ int error = 0;
+ int i;
+
+ if (bus->bus_attrs) {
+ for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
+ error = bus_create_file(bus, &bus->bus_attrs[i]);
+ if (error)
+ 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]);
+ }
+}
+
+static void klist_devices_get(struct klist_node *n)
+{
+ struct device *dev = container_of(n, struct device, knode_bus);
+
+ get_device(dev);
+}
+
+static void klist_devices_put(struct klist_node *n)
+{
+ struct device *dev = container_of(n, struct device, knode_bus);
+
+ put_device(dev);
+}
+
+static ssize_t bus_uevent_store(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ enum kobject_action action;
+
+ if (kobject_action_type(buf, count, &action) == 0)
+ kobject_uevent(&bus->p->subsys.kobj, action);
+ return count;
+}
+static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
+
+/**
+ * bus_register - register a bus with the system.
+ * @bus: bus.
+ *
+ * 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;
+ struct bus_type_private *priv;
+
+ priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->bus = bus;
+ bus->p = priv;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
+
+ retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
+ if (retval)
+ goto out;
+
+ priv->subsys.kobj.kset = bus_kset;
+ priv->subsys.kobj.ktype = &bus_ktype;
+ priv->drivers_autoprobe = 1;
+
+ retval = kset_register(&priv->subsys);
+ if (retval)
+ goto out;
+
+ retval = bus_create_file(bus, &bus_attr_uevent);
+ if (retval)
+ goto bus_uevent_fail;
+
+ priv->devices_kset = kset_create_and_add("devices", NULL,
+ &priv->subsys.kobj);
+ if (!priv->devices_kset) {
+ retval = -ENOMEM;
+ goto bus_devices_fail;
+ }
+
+ priv->drivers_kset = kset_create_and_add("drivers", NULL,
+ &priv->subsys.kobj);
+ if (!priv->drivers_kset) {
+ retval = -ENOMEM;
+ goto bus_drivers_fail;
+ }
+
+ klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
+ klist_init(&priv->klist_drivers, NULL, NULL);
+
+ retval = add_probe_files(bus);
+ if (retval)
+ goto bus_probe_files_fail;
+
+ retval = bus_add_attrs(bus);
+ if (retval)
+ goto bus_attrs_fail;
+
+ pr_debug("bus: '%s': registered\n", bus->name);
+ return 0;
+
+bus_attrs_fail:
+ remove_probe_files(bus);
+bus_probe_files_fail:
+ kset_unregister(bus->p->drivers_kset);
+bus_drivers_fail:
+ kset_unregister(bus->p->devices_kset);
+bus_devices_fail:
+ bus_remove_file(bus, &bus_attr_uevent);
+bus_uevent_fail:
+ kset_unregister(&bus->p->subsys);
+ kfree(bus->p);
+out:
+ return retval;
+}
+EXPORT_SYMBOL_GPL(bus_register);
+
+/**
+ * bus_unregister - remove a bus from the system
+ * @bus: bus.
+ *
+ * Unregister the child subsystems and the bus itself.
+ * Finally, we call bus_put() to release the refcount
+ */
+void bus_unregister(struct bus_type *bus)
+{
+ pr_debug("bus: '%s': unregistering\n", bus->name);
+ bus_remove_attrs(bus);
+ remove_probe_files(bus);
+ kset_unregister(bus->p->drivers_kset);
+ kset_unregister(bus->p->devices_kset);
+ bus_remove_file(bus, &bus_attr_uevent);
+ kset_unregister(&bus->p->subsys);
+ kfree(bus->p);
+}
+EXPORT_SYMBOL_GPL(bus_unregister);
+
+int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(bus_register_notifier);
+
+int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&bus->p->bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(bus_unregister_notifier);
+
+struct kset *bus_get_kset(struct bus_type *bus)
+{
+ return &bus->p->subsys;
+}
+EXPORT_SYMBOL_GPL(bus_get_kset);
+
+struct klist *bus_get_device_klist(struct bus_type *bus)
+{
+ return &bus->p->klist_devices;
+}
+EXPORT_SYMBOL_GPL(bus_get_device_klist);
+
+/*
+ * Yes, this forcably breaks the klist abstraction temporarily. It
+ * just wants to sort the klist, not change reference counts and
+ * take/drop locks rapidly in the process. It does all this while
+ * holding the lock for the list, so objects can't otherwise be
+ * added/removed while we're swizzling.
+ */
+static void device_insertion_sort_klist(struct device *a, struct list_head *list,
+ int (*compare)(const struct device *a,
+ const struct device *b))
+{
+ struct list_head *pos;
+ struct klist_node *n;
+ struct device *b;
+
+ list_for_each(pos, list) {
+ n = container_of(pos, struct klist_node, n_node);
+ b = container_of(n, struct device, knode_bus);
+ if (compare(a, b) <= 0) {
+ list_move_tail(&a->knode_bus.n_node,
+ &b->knode_bus.n_node);
+ return;
+ }
+ }
+ list_move_tail(&a->knode_bus.n_node, list);
+}
+
+void bus_sort_breadthfirst(struct bus_type *bus,
+ int (*compare)(const struct device *a,
+ const struct device *b))
+{
+ LIST_HEAD(sorted_devices);
+ struct list_head *pos, *tmp;
+ struct klist_node *n;
+ struct device *dev;
+ struct klist *device_klist;
+
+ device_klist = bus_get_device_klist(bus);
+
+ spin_lock(&device_klist->k_lock);
+ list_for_each_safe(pos, tmp, &device_klist->k_list) {
+ n = container_of(pos, struct klist_node, n_node);
+ dev = container_of(n, struct device, knode_bus);
+ device_insertion_sort_klist(dev, &sorted_devices, compare);
+ }
+ list_splice(&sorted_devices, &device_klist->k_list);
+ spin_unlock(&device_klist->k_lock);
+}
+EXPORT_SYMBOL_GPL(bus_sort_breadthfirst);
+
+int __init buses_init(void)
+{
+ bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
+ if (!bus_kset)
+ return -ENOMEM;
+ return 0;
+}
diff --git a/libdde-linux26/contrib/drivers/base/cpu.c b/libdde-linux26/contrib/drivers/base/cpu.c
new file mode 100644
index 00000000..719ee5c1
--- /dev/null
+++ b/libdde-linux26/contrib/drivers/base/cpu.c
@@ -0,0 +1,250 @@
+/*
+ * drivers/base/cpu.c - basic CPU class support
+ */
+
+#include <linux/sysdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/topology.h>
+#include <linux/device.h>
+#include <linux/node.h>
+
+#include "base.h"
+
+struct sysdev_class cpu_sysdev_class = {
+ .name = "cpu",
+};
+EXPORT_SYMBOL(cpu_sysdev_class);
+
+static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices);
+
+#ifdef CONFIG_HOTPLUG_CPU
+static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr,
+ char *buf)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, sysdev);
+
+ return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id));
+}
+
+static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr,
+ 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_uevent(&dev->kobj, KOBJ_OFFLINE);
+ break;
+ case '1':
+ ret = cpu_up(cpu->sysdev.id);
+ if (!ret)
+ kobject_uevent(&dev->kobj, KOBJ_ONLINE);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret >= 0)
+ ret = count;
+ return ret;
+}
+static SYSDEV_ATTR(online, 0644, show_online, store_online);
+
+static void __cpuinit register_cpu_control(struct cpu *cpu)
+{
+ sysdev_create_file(&cpu->sysdev, &attr_online);
+}
+void unregister_cpu(struct cpu *cpu)
+{
+ int logical_cpu = cpu->sysdev.id;
+
+ unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
+
+ sysdev_remove_file(&cpu->sysdev, &attr_online);
+
+ sysdev_unregister(&cpu->sysdev);
+ per_cpu(cpu_sys_devices, logical_cpu) = NULL;
+ return;
+}
+#else /* ... !CONFIG_HOTPLUG_CPU */
+static inline void register_cpu_control(struct cpu *cpu)
+{
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+#ifdef CONFIG_KEXEC
+#include <linux/kexec.h>
+
+static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr,
+ char *buf)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, sysdev);
+ ssize_t rc;
+ unsigned long long addr;
+ int cpunum;
+
+ cpunum = cpu->sysdev.id;
+
+ /*
+ * Might be reading other cpu's data based on which cpu read thread
+ * has been scheduled. But cpu data (memory) is allocated once during
+ * boot up and this data does not change there after. Hence this
+ * operation should be safe. No locking required.
+ */
+ addr = __pa(per_cpu_ptr(crash_notes, cpunum));
+ rc = sprintf(buf, "%Lx\n", addr);
+ return rc;
+}
+static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL);
+#endif
+
+/*
+ * Print cpu online, possible, present, and system maps
+ */
+static ssize_t print_cpus_map(char *buf, cpumask_t *map)
+{
+ int n = cpulist_scnprintf(buf, PAGE_SIZE-2, map);
+
+ buf[n++] = '\n';
+ buf[n] = '\0';
+ return n;
+}
+
+#define print_cpus_func(type) \
+static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf) \
+{ \
+ return print_cpus_map(buf, &cpu_##type##_map); \
+} \
+static struct sysdev_class_attribute attr_##type##_map = \
+ _SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL)
+
+print_cpus_func(online);
+print_cpus_func(possible);
+print_cpus_func(present);
+
+/*
+ * Print values for NR_CPUS and offlined cpus
+ */
+static ssize_t print_cpus_kernel_max(struct sysdev_class *class, char *buf)
+{
+ int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1);
+ return n;
+}
+static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
+
+/* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */
+unsigned int total_cpus;
+
+static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf)
+{
+ int n = 0, len = PAGE_SIZE-2;
+ cpumask_var_t offline;
+
+ /* display offline cpus < nr_cpu_ids */
+ if (!alloc_cpumask_var(&offline, GFP_KERNEL))
+ return -ENOMEM;
+ cpumask_complement(offline, cpu_online_mask);
+ n = cpulist_scnprintf(buf, len, offline);
+ free_cpumask_var(offline);
+
+ /* display offline cpus >= nr_cpu_ids */
+ if (total_cpus && nr_cpu_ids < total_cpus) {
+ if (n && n < len)
+ buf[n++] = ',';
+
+ if (nr_cpu_ids == total_cpus-1)
+ n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids);
+ else
+ n += snprintf(&buf[n], len - n, "%d-%d",
+ nr_cpu_ids, total_cpus-1);
+ }
+
+ n += snprintf(&buf[n], len - n, "\n");
+ return n;
+}
+static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL);
+
+static struct sysdev_class_attribute *cpu_state_attr[] = {
+ &attr_online_map,
+ &attr_possible_map,
+ &attr_present_map,
+ &attr_kernel_max,
+ &attr_offline,
+};
+
+static int cpu_states_init(void)
+{
+ int i;
+ int err = 0;
+
+ for (i = 0; i < ARRAY_SIZE(cpu_state_attr); i++) {
+ int ret;
+ ret = sysdev_class_create_file(&cpu_sysdev_class,
+ cpu_state_attr[i]);
+ if (!err)
+ err = ret;
+ }
+ return err;
+}
+
+/*
+ * register_cpu - Setup a sysfs device for a CPU.
+ * @cpu - cpu->hotpluggable field set to 1 will 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 __cpuinit register_cpu(struct cpu *cpu, int num)
+{
+ 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 && cpu->hotpluggable)
+ register_cpu_control(cpu);
+ if (!error)
+ per_cpu(cpu_sys_devices, num) = &cpu->sysdev;
+ if (!error)
+ register_cpu_under_node(num, cpu_to_node(num));
+
+#ifdef CONFIG_KEXEC
+ if (!error)
+ error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes);
+#endif
+ return error;
+}
+
+struct sys_device *get_cpu_sysdev(unsigned cpu)
+{
+ if (cpu < nr_cpu_ids && cpu_possible(cpu))
+ return per_cpu(cpu_sys_devices, cpu);
+ else
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(get_cpu_sysdev);
+
+int __init cpu_dev_init(void)
+{
+ int err;
+
+ err = sysdev_class_register(&cpu_sysdev_class);
+ if (!err)
+ err = cpu_states_init();
+
+#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
+ if (!err)
+ err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
+#endif
+
+ return err;
+}
diff --git a/libdde-linux26/contrib/drivers/base/dd.c b/libdde-linux26/contrib/drivers/base/dd.c
new file mode 100644
index 00000000..13523123
--- /dev/null
+++ b/libdde-linux26/contrib/drivers/base/dd.c
@@ -0,0 +1,383 @@
+/*
+ * drivers/base/dd.c - The core device/driver interactions.
+ *
+ * This file contains the (sometimes tricky) code that controls the
+ * interactions between devices and drivers, which primarily includes
+ * driver binding and unbinding.
+ *
+ * All of this code used to exist in drivers/base/bus.c, but was
+ * relocated to here in the name of compartmentalization (since it wasn't
+ * strictly code just for the 'struct bus_type'.
+ *
+ * Copyright (c) 2002-5 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/async.h>
+
+#include "base.h"
+#include "power/power.h"
+
+
+static void driver_bound(struct device *dev)
+{
+ if (klist_node_attached(&dev->knode_driver)) {
+ printk(KERN_WARNING "%s: device %s already bound\n",
+ __func__, kobject_name(&dev->kobj));
+ return;
+ }
+
+ pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
+ __func__, dev->driver->name);
+
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
+ BUS_NOTIFY_BOUND_DRIVER, dev);
+
+ klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices);
+}
+
+static int driver_sysfs_add(struct device *dev)
+{
+ int ret;
+
+ ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
+ kobject_name(&dev->kobj));
+ if (ret == 0) {
+ ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
+ "driver");
+ if (ret)
+ sysfs_remove_link(&dev->driver->p->kobj,
+ kobject_name(&dev->kobj));
+ }
+ return ret;
+}
+
+static void driver_sysfs_remove(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+
+ if (drv) {
+ sysfs_remove_link(&drv->p->kobj, kobject_name(&dev->kobj));
+ sysfs_remove_link(&dev->kobj, "driver");
+ }
+}
+
+/**
+ * device_bind_driver - bind a driver to one device.
+ * @dev: device.
+ *
+ * Allow manual attachment of a driver to a device.
+ * Caller must have already set @dev->driver.
+ *
+ * Note that this does not modify the bus reference count
+ * nor take the bus's rwsem. Please verify those are accounted
+ * for before calling this. (It is ok to call with no other effort
+ * from a driver's probe() method.)
+ *
+ * This function must be called with @dev->sem held.
+ */
+int device_bind_driver(struct device *dev)
+{
+ int ret;
+
+ ret = driver_sysfs_add(dev);
+ if (!ret)
+ driver_bound(dev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(device_bind_driver);
+
+static atomic_t probe_count = ATOMIC_INIT(0);
+static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
+
+static int really_probe(struct device *dev, struct device_driver *drv)
+{
+ int ret = 0;
+
+ atomic_inc(&probe_count);
+ pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
+ drv->bus->name, __func__, drv->name, dev_name(dev));
+ WARN_ON(!list_empty(&dev->devres_head));
+
+ dev->driver = drv;
+ if (driver_sysfs_add(dev)) {
+ printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
+ __func__, dev_name(dev));
+ goto probe_failed;
+ }
+
+ if (dev->bus->probe) {
+ ret = dev->bus->probe(dev);
+ if (ret)
+ goto probe_failed;
+ } else if (drv->probe) {
+ ret = drv->probe(dev);
+ if (ret)
+ goto probe_failed;
+ }
+
+ driver_bound(dev);
+ ret = 1;
+ pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
+ drv->bus->name, __func__, dev_name(dev), drv->name);
+ goto done;
+
+probe_failed:
+ devres_release_all(dev);
+ driver_sysfs_remove(dev);
+ dev->driver = NULL;
+
+ if (ret != -ENODEV && ret != -ENXIO) {
+ /* driver matched but the probe failed */
+ printk(KERN_WARNING
+ "%s: probe of %s failed with error %d\n",
+ drv->name, dev_name(dev), ret);
+ }
+ /*
+ * Ignore errors returned by ->probe so that the next driver can try
+ * its luck.
+ */
+ ret = 0;
+done:
+ atomic_dec(&probe_count);
+ wake_up(&probe_waitqueue);
+ return ret;
+}
+
+/**
+ * driver_probe_done
+ * Determine if the probe sequence is finished or not.
+ *
+ * Should somehow figure out how to use a semaphore, not an atomic variable...
+ */
+int driver_probe_done(void)
+{
+ pr_debug("%s: probe_count = %d\n", __func__,
+ atomic_read(&probe_count));
+ if (atomic_read(&probe_count))
+ return -EBUSY;
+ return 0;
+}
+
+/**
+ * wait_for_device_probe
+ * Wait for device probing to be completed.
+ *
+ * Note: this function polls at 100 msec intervals.
+ */
+int wait_for_device_probe(void)
+{
+ /* wait for the known devices to complete their probing */
+ while (driver_probe_done() != 0)
+ msleep(100);
+ async_synchronize_full();
+ return 0;
+}
+
+/**
+ * driver_probe_device - attempt to bind device & driver together
+ * @drv: driver to bind a device to
+ * @dev: device to try to bind to the driver
+ *
+ * First, we call the bus's match function, if one present, which should
+ * compare the device IDs the driver supports with the device IDs of the
+ * device. Note we don't do this ourselves because we don't know the
+ * format of the ID structures, nor what is to be considered a match and
+ * what is not.
+ *
+ * This function returns 1 if a match is found, -ENODEV if the device is
+ * not registered, and 0 otherwise.
+ *
+ * This function must be called with @dev->sem held. When called for a
+ * USB interface, @dev->parent->sem must be held as well.
+ */
+int driver_probe_device(struct device_driver *drv, struct device *dev)
+{
+ int ret = 0;
+
+ if (!device_is_registered(dev))
+ return -ENODEV;
+ if (drv->bus->match && !drv->bus->match(dev, drv))
+ goto done;
+
+ pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
+ drv->bus->name, __func__, dev_name(dev), drv->name);
+
+ ret = really_probe(dev, drv);
+
+done:
+ return ret;
+}
+
+static int __device_attach(struct device_driver *drv, void *data)
+{
+ struct device *dev = data;
+ return driver_probe_device(drv, dev);
+}
+
+/**
+ * device_attach - try to attach device to a driver.
+ * @dev: device.
+ *
+ * Walk the list of drivers that the bus has and call
+ * driver_probe_device() for each pair. If a compatible
+ * pair is found, break out and return.
+ *
+ * Returns 1 if the device was bound to a driver;
+ * 0 if no matching device was found;
+ * -ENODEV if the device is not registered.
+ *
+ * When called for a USB interface, @dev->parent->sem must be held.
+ */
+int device_attach(struct device *dev)
+{
+ int ret = 0;
+
+ down(&dev->sem);
+ if (dev->driver) {
+ ret = device_bind_driver(dev);
+ if (ret == 0)
+ ret = 1;
+ else {
+ dev->driver = NULL;
+ ret = 0;
+ }
+ } else {
+ ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
+ }
+ up(&dev->sem);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(device_attach);
+
+static int __driver_attach(struct device *dev, void *data)
+{
+ struct device_driver *drv = data;
+
+ /*
+ * Lock device and try to bind to it. We drop the error
+ * here and always return 0, because we need to keep trying
+ * to bind to devices and some drivers will return an error
+ * simply if it didn't support the device.
+ *
+ * driver_probe_device() will spit a warning if there
+ * is an error.
+ */
+
+ if (drv->bus->match && !drv->bus->match(dev, drv))
+ return 0;
+
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ down(&dev->sem);
+ if (!dev->driver)
+ driver_probe_device(drv, dev);
+ up(&dev->sem);
+ if (dev->parent)
+ up(&dev->parent->sem);
+
+ return 0;
+}
+
+/**
+ * driver_attach - try to bind driver to devices.
+ * @drv: driver.
+ *
+ * Walk the list of devices that the bus has on it and try to
+ * match the driver with each one. If driver_probe_device()
+ * returns 0 and the @dev->driver is set, we've found a
+ * compatible pair.
+ */
+int driver_attach(struct device_driver *drv)
+{
+ return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
+}
+EXPORT_SYMBOL_GPL(driver_attach);
+
+/*
+ * __device_release_driver() must be called with @dev->sem held.
+ * When called for a USB interface, @dev->parent->sem must be held as well.
+ */
+static void __device_release_driver(struct device *dev)
+{
+ struct device_driver *drv;
+
+ drv = dev->driver;
+ if (drv) {
+ driver_sysfs_remove(dev);
+
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
+ BUS_NOTIFY_UNBIND_DRIVER,
+ dev);
+
+ if (dev->bus && dev->bus->remove)
+ dev->bus->remove(dev);
+ else if (drv->remove)
+ drv->remove(dev);
+ devres_release_all(dev);
+ dev->driver = NULL;
+ klist_remove(&dev->knode_driver);
+ }
+}
+
+/**
+ * device_release_driver - manually detach device from driver.
+ * @dev: device.
+ *
+ * Manually detach device from driver.
+ * When called for a USB interface, @dev->parent->sem must be held.
+ */
+void device_release_driver(struct device *dev)
+{
+ /*
+ * If anyone calls device_release_driver() recursively from
+ * within their ->remove callback for the same device, they
+ * will deadlock right here.
+ */
+ down(&dev->sem);
+ __device_release_driver(dev);
+ up(&dev->sem);
+}
+EXPORT_SYMBOL_GPL(device_release_driver);
+
+/**
+ * driver_detach - detach driver from all devices it controls.
+ * @drv: driver.
+ */
+void driver_detach(struct device_driver *drv)
+{
+ struct device *dev;
+
+ for (;;) {
+ spin_lock(&drv->p->klist_devices.k_lock);
+ if (list_empty(&drv->p->klist_devices.k_list)) {
+ spin_unlock(&drv->p->klist_devices.k_lock);
+ break;
+ }
+ dev = list_entry(drv->p->klist_devices.k_list.prev,
+ struct device, knode_driver.n_node);
+ get_device(dev);
+ spin_unlock(&drv->p->klist_devices.k_lock);
+
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ down(&dev->sem);
+ if (dev->driver == drv)
+ __device_release_driver(dev);
+ up(&dev->sem);
+ if (dev->parent)
+ up(&dev->parent->sem);
+ put_device(dev);
+ }
+}
diff --git a/libdde-linux26/contrib/drivers/base/devres.c b/libdde-linux26/contrib/drivers/base/devres.c
new file mode 100644
index 00000000..e8beb8e5
--- /dev/null
+++ b/libdde-linux26/contrib/drivers/base/devres.c
@@ -0,0 +1,646 @@
+/*
+ * drivers/base/devres.c - device resource management
+ *
+ * Copyright (c) 2006 SUSE Linux Products GmbH
+ * Copyright (c) 2006 Tejun Heo <teheo@suse.de>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include "base.h"
+
+struct devres_node {
+ struct list_head entry;
+ dr_release_t release;
+#ifdef CONFIG_DEBUG_DEVRES
+ const char *name;
+ size_t size;
+#endif
+};
+
+struct devres {
+ struct devres_node node;
+ /* -- 3 pointers */
+ unsigned long long data[]; /* guarantee ull alignment */
+};
+
+struct devres_group {
+ struct devres_node node[2];
+ void *id;
+ int color;
+ /* -- 8 pointers */
+};
+
+#ifdef CONFIG_DEBUG_DEVRES
+static int log_devres = 0;
+module_param_named(log, log_devres, int, S_IRUGO | S_IWUSR);
+
+static void set_node_dbginfo(struct devres_node *node, const char *name,
+ size_t size)
+{
+ node->name = name;
+ node->size = size;
+}
+
+static void devres_log(struct device *dev, struct devres_node *node,
+ const char *op)
+{
+ if (unlikely(log_devres))
+ dev_printk(KERN_ERR, dev, "DEVRES %3s %p %s (%lu bytes)\n",
+ op, node, node->name, (unsigned long)node->size);
+}
+#else /* CONFIG_DEBUG_DEVRES */
+#define set_node_dbginfo(node, n, s) do {} while (0)
+#define devres_log(dev, node, op) do {} while (0)
+#endif /* CONFIG_DEBUG_DEVRES */
+
+/*
+ * Release functions for devres group. These callbacks are used only
+ * for identification.
+ */
+static void group_open_release(struct device *dev, void *res)
+{
+ /* noop */
+}
+
+static void group_close_release(struct device *dev, void *res)
+{
+ /* noop */
+}
+
+static struct devres_group * node_to_group(struct devres_node *node)
+{
+ if (node->release == &group_open_release)
+ return container_of(node, struct devres_group, node[0]);
+ if (node->release == &group_close_release)
+ return container_of(node, struct devres_group, node[1]);
+ return NULL;
+}
+
+static __always_inline struct devres * alloc_dr(dr_release_t release,
+ size_t size, gfp_t gfp)
+{
+ size_t tot_size = sizeof(struct devres) + size;
+ struct devres *dr;
+
+ dr = kmalloc_track_caller(tot_size, gfp);
+ if (unlikely(!dr))
+ return NULL;
+
+ memset(dr, 0, tot_size);
+ INIT_LIST_HEAD(&dr->node.entry);
+ dr->node.release = release;
+ return dr;
+}
+
+static void add_dr(struct device *dev, struct devres_node *node)
+{
+ devres_log(dev, node, "ADD");
+ BUG_ON(!list_empty(&node->entry));
+ list_add_tail(&node->entry, &dev->devres_head);
+}
+
+#ifdef CONFIG_DEBUG_DEVRES
+void * __devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
+ const char *name)
+{
+ struct devres *dr;
+
+ dr = alloc_dr(release, size, gfp);
+ if (unlikely(!dr))
+ return NULL;
+ set_node_dbginfo(&dr->node, name, size);
+ return dr->data;
+}
+EXPORT_SYMBOL_GPL(__devres_alloc);
+#else
+/**
+ * devres_alloc - Allocate device resource data
+ * @release: Release function devres will be associated with
+ * @size: Allocation size
+ * @gfp: Allocation flags
+ *
+ * Allocate devres of @size bytes. The allocated area is zeroed, then
+ * associated with @release. The returned pointer can be passed to
+ * other devres_*() functions.
+ *
+ * RETURNS:
+ * Pointer to allocated devres on success, NULL on failure.
+ */
+void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
+{
+ struct devres *dr;
+
+ dr = alloc_dr(release, size, gfp);
+ if (unlikely(!dr))
+ return NULL;
+ return dr->data;
+}
+EXPORT_SYMBOL_GPL(devres_alloc);
+#endif
+
+/**
+ * devres_free - Free device resource data
+ * @res: Pointer to devres data to free
+ *
+ * Free devres created with devres_alloc().
+ */
+void devres_free(void *res)
+{
+ if (res) {
+ struct devres *dr = container_of(res, struct devres, data);
+
+ BUG_ON(!list_empty(&dr->node.entry));
+ kfree(dr);
+ }
+}
+EXPORT_SYMBOL_GPL(devres_free);
+
+/**
+ * devres_add - Register device resource
+ * @dev: Device to add resource to
+ * @res: Resource to register
+ *
+ * Register devres @res to @dev. @res should have been allocated
+ * using devres_alloc(). On driver detach, the associated release
+ * function will be invoked and devres will be freed automatically.
+ */
+void devres_add(struct device *dev, void *res)
+{
+ struct devres *dr = container_of(res, struct devres, data);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ add_dr(dev, &dr->node);
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+}
+EXPORT_SYMBOL_GPL(devres_add);
+
+static struct devres *find_dr(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
+{
+ struct devres_node *node;
+
+ list_for_each_entry_reverse(node, &dev->devres_head, entry) {
+ struct devres *dr = container_of(node, struct devres, node);
+
+ if (node->release != release)
+ continue;
+ if (match && !match(dev, dr->data, match_data))
+ continue;
+ return dr;
+ }
+
+ return NULL;
+}
+
+/**
+ * devres_find - Find device resource
+ * @dev: Device to lookup resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev which is associated with @release
+ * and for which @match returns 1. If @match is NULL, it's considered
+ * to match all.
+ *
+ * RETURNS:
+ * Pointer to found devres, NULL if not found.
+ */
+void * devres_find(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
+{
+ struct devres *dr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ dr = find_dr(dev, release, match, match_data);
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+
+ if (dr)
+ return dr->data;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(devres_find);
+
+/**
+ * devres_get - Find devres, if non-existent, add one atomically
+ * @dev: Device to lookup or add devres for
+ * @new_res: Pointer to new initialized devres to add if not found
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev which has the same release function
+ * as @new_res and for which @match return 1. If found, @new_res is
+ * freed; otherwise, @new_res is added atomically.
+ *
+ * RETURNS:
+ * Pointer to found or added devres.
+ */
+void * devres_get(struct device *dev, void *new_res,
+ dr_match_t match, void *match_data)
+{
+ struct devres *new_dr = container_of(new_res, struct devres, data);
+ struct devres *dr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ dr = find_dr(dev, new_dr->node.release, match, match_data);
+ if (!dr) {
+ add_dr(dev, &new_dr->node);
+ dr = new_dr;
+ new_dr = NULL;
+ }
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+ devres_free(new_dr);
+
+ return dr->data;
+}
+EXPORT_SYMBOL_GPL(devres_get);
+
+/**
+ * devres_remove - Find a device resource and remove it
+ * @dev: Device to find resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev associated with @release and for
+ * which @match returns 1. If @match is NULL, it's considered to
+ * match all. If found, the resource is removed atomically and
+ * returned.
+ *
+ * RETURNS:
+ * Pointer to removed devres on success, NULL if not found.
+ */
+void * devres_remove(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
+{
+ struct devres *dr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ dr = find_dr(dev, release, match, match_data);
+ if (dr) {
+ list_del_init(&dr->node.entry);
+ devres_log(dev, &dr->node, "REM");
+ }
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+
+ if (dr)
+ return dr->data;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(devres_remove);
+
+/**
+ * devres_destroy - Find a device resource and destroy it
+ * @dev: Device to find resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev associated with @release and for
+ * which @match returns 1. If @match is NULL, it's considered to
+ * match all. If found, the resource is removed atomically and freed.
+ *
+ * RETURNS:
+ * 0 if devres is found and freed, -ENOENT if not found.
+ */
+int devres_destroy(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
+{
+ void *res;
+
+ res = devres_remove(dev, release, match, match_data);
+ if (unlikely(!res))
+ return -ENOENT;
+
+ devres_free(res);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devres_destroy);
+
+static int remove_nodes(struct device *dev,
+ struct list_head *first, struct list_head *end,
+ struct list_head *todo)
+{
+ int cnt = 0, nr_groups = 0;
+ struct list_head *cur;
+
+ /* First pass - move normal devres entries to @todo and clear
+ * devres_group colors.
+ */
+ cur = first;
+ while (cur != end) {
+ struct devres_node *node;
+ struct devres_group *grp;
+
+ node = list_entry(cur, struct devres_node, entry);
+ cur = cur->next;
+
+ grp = node_to_group(node);
+ if (grp) {
+ /* clear color of group markers in the first pass */
+ grp->color = 0;
+ nr_groups++;
+ } else {
+ /* regular devres entry */
+ if (&node->entry == first)
+ first = first->next;
+ list_move_tail(&node->entry, todo);
+ cnt++;
+ }
+ }
+
+ if (!nr_groups)
+ return cnt;
+
+ /* Second pass - Scan groups and color them. A group gets
+ * color value of two iff the group is wholly contained in
+ * [cur, end). That is, for a closed group, both opening and
+ * closing markers should be in the range, while just the
+ * opening marker is enough for an open group.
+ */
+ cur = first;
+ while (cur != end) {
+ struct devres_node *node;
+ struct devres_group *grp;
+
+ node = list_entry(cur, struct devres_node, entry);
+ cur = cur->next;
+
+ grp = node_to_group(node);
+ BUG_ON(!grp || list_empty(&grp->node[0].entry));
+
+ grp->color++;
+ if (list_empty(&grp->node[1].entry))
+ grp->color++;
+
+ BUG_ON(grp->color <= 0 || grp->color > 2);
+ if (grp->color == 2) {
+ /* No need to update cur or end. The removed
+ * nodes are always before both.
+ */
+ list_move_tail(&grp->node[0].entry, todo);
+ list_del_init(&grp->node[1].entry);
+ }
+ }
+
+ return cnt;
+}
+
+static int release_nodes(struct device *dev, struct list_head *first,
+ struct list_head *end, unsigned long flags)
+{
+ LIST_HEAD(todo);
+ int cnt;
+ struct devres *dr, *tmp;
+
+ cnt = remove_nodes(dev, first, end, &todo);
+
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+
+ /* Release. Note that both devres and devres_group are
+ * handled as devres in the following loop. This is safe.
+ */
+ list_for_each_entry_safe_reverse(dr, tmp, &todo, node.entry) {
+ devres_log(dev, &dr->node, "REL");
+ dr->node.release(dev, dr->data);
+ kfree(dr);
+ }
+
+ return cnt;
+}
+
+/**
+ * devres_release_all - Release all managed resources
+ * @dev: Device to release resources for
+ *
+ * Release all resources associated with @dev. This function is
+ * called on driver detach.
+ */
+int devres_release_all(struct device *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ return release_nodes(dev, dev->devres_head.next, &dev->devres_head,
+ flags);
+}
+
+/**
+ * devres_open_group - Open a new devres group
+ * @dev: Device to open devres group for
+ * @id: Separator ID
+ * @gfp: Allocation flags
+ *
+ * Open a new devres group for @dev with @id. For @id, using a
+ * pointer to an object which won't be used for another group is
+ * recommended. If @id is NULL, address-wise unique ID is created.
+ *
+ * RETURNS:
+ * ID of the new group, NULL on failure.
+ */
+void * devres_open_group(struct device *dev, void *id, gfp_t gfp)
+{
+ struct devres_group *grp;
+ unsigned long flags;
+
+ grp = kmalloc(sizeof(*grp), gfp);
+ if (unlikely(!grp))
+ return NULL;
+
+ grp->node[0].release = &group_open_release;
+ grp->node[1].release = &group_close_release;
+ INIT_LIST_HEAD(&grp->node[0].entry);
+ INIT_LIST_HEAD(&grp->node[1].entry);
+ set_node_dbginfo(&grp->node[0], "grp<", 0);
+ set_node_dbginfo(&grp->node[1], "grp>", 0);
+ grp->id = grp;
+ if (id)
+ grp->id = id;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ add_dr(dev, &grp->node[0]);
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+ return grp->id;
+}
+EXPORT_SYMBOL_GPL(devres_open_group);
+
+/* Find devres group with ID @id. If @id is NULL, look for the latest. */
+static struct devres_group * find_group(struct device *dev, void *id)
+{
+ struct devres_node *node;
+
+ list_for_each_entry_reverse(node, &dev->devres_head, entry) {
+ struct devres_group *grp;
+
+ if (node->release != &group_open_release)
+ continue;
+
+ grp = container_of(node, struct devres_group, node[0]);
+
+ if (id) {
+ if (grp->id == id)
+ return grp;
+ } else if (list_empty(&grp->node[1].entry))
+ return grp;
+ }
+
+ return NULL;
+}
+
+/**
+ * devres_close_group - Close a devres group
+ * @dev: Device to close devres group for
+ * @id: ID of target group, can be NULL
+ *
+ * Close the group identified by @id. If @id is NULL, the latest open
+ * group is selected.
+ */
+void devres_close_group(struct device *dev, void *id)
+{
+ struct devres_group *grp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+
+ grp = find_group(dev, id);
+ if (grp)
+ add_dr(dev, &grp->node[1]);
+ else
+ WARN_ON(1);
+
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+}
+EXPORT_SYMBOL_GPL(devres_close_group);
+
+/**
+ * devres_remove_group - Remove a devres group
+ * @dev: Device to remove group for
+ * @id: ID of target group, can be NULL
+ *
+ * Remove the group identified by @id. If @id is NULL, the latest
+ * open group is selected. Note that removing a group doesn't affect
+ * any other resources.
+ */
+void devres_remove_group(struct device *dev, void *id)
+{
+ struct devres_group *grp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+
+ grp = find_group(dev, id);
+ if (grp) {
+ list_del_init(&grp->node[0].entry);
+ list_del_init(&grp->node[1].entry);
+ devres_log(dev, &grp->node[0], "REM");
+ } else
+ WARN_ON(1);
+
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+
+ kfree(grp);
+}
+EXPORT_SYMBOL_GPL(devres_remove_group);
+
+/**
+ * devres_release_group - Release resources in a devres group
+ * @dev: Device to release group for
+ * @id: ID of target group, can be NULL
+ *
+ * Release all resources in the group identified by @id. If @id is
+ * NULL, the latest open group is selected. The selected group and
+ * groups properly nested inside the selected group are removed.
+ *
+ * RETURNS:
+ * The number of released non-group resources.
+ */
+int devres_release_group(struct device *dev, void *id)
+{
+ struct devres_group *grp;
+ unsigned long flags;
+ int cnt = 0;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+
+ grp = find_group(dev, id);
+ if (grp) {
+ struct list_head *first = &grp->node[0].entry;
+ struct list_head *end = &dev->devres_head;
+
+ if (!list_empty(&grp->node[1].entry))
+ end = grp->node[1].entry.next;
+
+ cnt = release_nodes(dev, first, end, flags);
+ } else {
+ WARN_ON(1);
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+ }
+
+ return cnt;
+}
+EXPORT_SYMBOL_GPL(devres_release_group);
+
+/*
+ * Managed kzalloc/kfree
+ */
+static void devm_kzalloc_release(struct device *dev, void *res)
+{
+ /* noop */
+}
+
+static int devm_kzalloc_match(struct device *dev, void *res, void *data)
+{
+ return res == data;
+}
+
+/**
+ * devm_kzalloc - Resource-managed kzalloc
+ * @dev: Device to allocate memory for
+ * @size: Allocation size
+ * @gfp: Allocation gfp flags
+ *
+ * Managed kzalloc. Memory allocated with this function is
+ * automatically freed on driver detach. Like all other devres
+ * resources, guaranteed alignment is unsigned long long.
+ *
+ * RETURNS:
+ * Pointer to allocated memory on success, NULL on failure.
+ */
+void * devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
+{
+ struct devres *dr;
+
+ /* use raw alloc_dr for kmalloc caller tracing */
+ dr = alloc_dr(devm_kzalloc_release, size, gfp);
+ if (unlikely(!dr))
+ return NULL;
+
+ set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
+ devres_add(dev, dr->data);
+ return dr->data;
+}
+EXPORT_SYMBOL_GPL(devm_kzalloc);
+
+/**
+ * devm_kfree - Resource-managed kfree
+ * @dev: Device this memory belongs to
+ * @p: Memory to free
+ *
+ * Free memory allocated with dev_kzalloc().
+ */
+void devm_kfree(struct device *dev, void *p)
+{
+ int rc;
+
+ rc = devres_destroy(dev, devm_kzalloc_release, devm_kzalloc_match, p);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_kfree);
diff --git a/libdde-linux26/contrib/drivers/base/driver.c b/libdde-linux26/contrib/drivers/base/driver.c
new file mode 100644
index 00000000..1e2bda78
--- /dev/null
+++ b/libdde-linux26/contrib/drivers/base/driver.c
@@ -0,0 +1,277 @@
+/*
+ * driver.c - centralized device driver management
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include "base.h"
+
+static struct device *next_device(struct klist_iter *i)
+{
+ struct klist_node *n = klist_next(i);
+ return n ? container_of(n, struct device, knode_driver) : NULL;
+}
+
+/**
+ * driver_for_each_device - Iterator for devices bound to a driver.
+ * @drv: Driver we're iterating.
+ * @start: Device to begin with
+ * @data: Data to pass to the callback.
+ * @fn: Function to call for each device.
+ *
+ * Iterate over the @drv's list of devices calling @fn for each one.
+ */
+int driver_for_each_device(struct device_driver *drv, struct device *start,
+ void *data, int (*fn)(struct device *, void *))
+{
+ struct klist_iter i;
+ struct device *dev;
+ int error = 0;
+
+ if (!drv)
+ return -EINVAL;
+
+ klist_iter_init_node(&drv->p->klist_devices, &i,
+ start ? &start->knode_driver : NULL);
+ while ((dev = next_device(&i)) && !error)
+ error = fn(dev, data);
+ klist_iter_exit(&i);
+ return error;
+}
+EXPORT_SYMBOL_GPL(driver_for_each_device);
+
+/**
+ * driver_find_device - device iterator for locating a particular device.
+ * @drv: The device's driver
+ * @start: Device to begin with
+ * @data: Data to pass to match function
+ * @match: Callback function to check device
+ *
+ * This is similar to the driver_for_each_device() function above, but
+ * it returns a reference to a device that is 'found' for later use, as
+ * determined by the @match callback.
+ *
+ * The callback should return 0 if the device doesn't match and non-zero
+ * if it does. If the callback returns non-zero, this function will
+ * return to the caller and not iterate over any more devices.
+ */
+struct device *driver_find_device(struct device_driver *drv,
+ struct device *start, void *data,
+ int (*match)(struct device *dev, void *data))
+{
+ struct klist_iter i;
+ struct device *dev;
+
+ if (!drv)
+ return NULL;
+
+ klist_iter_init_node(&drv->p->klist_devices, &i,
+ (start ? &start->knode_driver : NULL));
+ while ((dev = next_device(&i)))
+ if (match(dev, data) && get_device(dev))
+ break;
+ klist_iter_exit(&i);
+ return dev;
+}
+EXPORT_SYMBOL_GPL(driver_find_device);
+
+/**
+ * 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 (drv)
+ error = sysfs_create_file(&drv->p->kobj, &attr->attr);
+ else
+ error = -EINVAL;
+ return error;
+}
+EXPORT_SYMBOL_GPL(driver_create_file);
+
+/**
+ * driver_remove_file - remove sysfs file for driver.
+ * @drv: driver.
+ * @attr: driver attribute descriptor.
+ */
+void driver_remove_file(struct device_driver *drv,
+ struct driver_attribute *attr)
+{
+ if (drv)
+ sysfs_remove_file(&drv->p->kobj, &attr->attr);
+}
+EXPORT_SYMBOL_GPL(driver_remove_file);
+
+/**
+ * driver_add_kobj - add a kobject below the specified driver
+ * @drv: requesting device driver
+ * @kobj: kobject to add below this driver
+ * @fmt: format string that names the kobject
+ *
+ * You really don't want to do this, this is only here due to one looney
+ * iseries driver, go poke those developers if you are annoyed about
+ * this...
+ */
+int driver_add_kobj(struct device_driver *drv, struct kobject *kobj,
+ const char *fmt, ...)
+{
+ va_list args;
+ char *name;
+ int ret;
+
+ va_start(args, fmt);
+ name = kvasprintf(GFP_KERNEL, fmt, args);
+ va_end(args);
+
+ if (!name)
+ return -ENOMEM;
+
+ ret = kobject_add(kobj, &drv->p->kobj, "%s", name);
+ kfree(name);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(driver_add_kobj);
+
+/**
+ * get_driver - increment driver reference count.
+ * @drv: driver.
+ */
+struct device_driver *get_driver(struct device_driver *drv)
+{
+ if (drv) {
+ struct driver_private *priv;
+ struct kobject *kobj;
+
+ kobj = kobject_get(&drv->p->kobj);
+ priv = to_driver(kobj);
+ return priv->driver;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(get_driver);
+
+/**
+ * put_driver - decrement driver's refcount.
+ * @drv: driver.
+ */
+void put_driver(struct device_driver *drv)
+{
+ kobject_put(&drv->p->kobj);
+}
+EXPORT_SYMBOL_GPL(put_driver);
+
+static int driver_add_groups(struct device_driver *drv,
+ struct attribute_group **groups)
+{
+ int error = 0;
+ int i;
+
+ if (groups) {
+ for (i = 0; groups[i]; i++) {
+ error = sysfs_create_group(&drv->p->kobj, groups[i]);
+ if (error) {
+ while (--i >= 0)
+ sysfs_remove_group(&drv->p->kobj,
+ groups[i]);
+ break;
+ }
+ }
+ }
+ return error;
+}
+
+static void driver_remove_groups(struct device_driver *drv,
+ struct attribute_group **groups)
+{
+ int i;
+
+ if (groups)
+ for (i = 0; groups[i]; i++)
+ sysfs_remove_group(&drv->p->kobj, groups[i]);
+}
+
+/**
+ * driver_register - register driver with bus
+ * @drv: driver to register
+ *
+ * We pass off most of the work to the bus_add_driver() call,
+ * since most of the things we have to do deal with the bus
+ * structures.
+ */
+int driver_register(struct device_driver *drv)
+{
+ int ret;
+ struct device_driver *other;
+
+ if ((drv->bus->probe && drv->probe) ||
+ (drv->bus->remove && drv->remove) ||
+ (drv->bus->shutdown && drv->shutdown))
+ printk(KERN_WARNING "Driver '%s' needs updating - please use "
+ "bus_type methods\n", drv->name);
+
+ other = driver_find(drv->name, drv->bus);
+ if (other) {
+ put_driver(other);
+ printk(KERN_ERR "Error: Driver '%s' is already registered, "
+ "aborting...\n", drv->name);
+ return -EEXIST;
+ }
+
+ ret = bus_add_driver(drv);
+ if (ret)
+ return ret;
+ ret = driver_add_groups(drv, drv->groups);
+ if (ret)
+ bus_remove_driver(drv);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(driver_register);
+
+/**
+ * driver_unregister - remove driver from system.
+ * @drv: driver.
+ *
+ * Again, we pass off most of the work to the bus-level call.
+ */
+void driver_unregister(struct device_driver *drv)
+{
+ driver_remove_groups(drv, drv->groups);
+ bus_remove_driver(drv);
+}
+EXPORT_SYMBOL_GPL(driver_unregister);
+
+/**
+ * driver_find - locate driver on a bus by its name.
+ * @name: name of the driver.
+ * @bus: bus to scan for the driver.
+ *
+ * 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->p->drivers_kset, name);
+ struct driver_private *priv;
+
+ if (k) {
+ priv = to_driver(k);
+ return priv->driver;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(driver_find);
diff --git a/libdde-linux26/contrib/drivers/base/map.c b/libdde-linux26/contrib/drivers/base/map.c
new file mode 100644
index 00000000..e87017f3
--- /dev/null
+++ b/libdde-linux26/contrib/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/mutex.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 mutex *lock;
+};
+
+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;
+ }
+ mutex_lock(domain->lock);
+ 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;
+ }
+ mutex_unlock(domain->lock);
+ 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;
+
+ mutex_lock(domain->lock);
+ 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;
+ }
+ }
+ }
+ mutex_unlock(domain->lock);
+ 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:
+ mutex_lock(domain->lock);
+ 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;
+ }
+ mutex_unlock(domain->lock);
+ kobj = probe(dev, index, data);
+ /* Currently ->owner protects _only_ ->probe() itself. */
+ module_put(owner);
+ if (kobj)
+ return kobj;
+ goto retry;
+ }
+ mutex_unlock(domain->lock);
+ return NULL;
+}
+
+struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
+{
+ struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
+ struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
+ int i;
+
+ if ((p == NULL) || (base == NULL)) {
+ kfree(p);
+ kfree(base);
+ return NULL;
+ }
+
+ base->dev = 1;
+ base->range = ~0;
+ base->get = base_probe;
+ for (i = 0; i < 255; i++)
+ p->probes[i] = base;
+ p->lock = lock;
+ return p;
+}
diff --git a/libdde-linux26/contrib/drivers/base/platform.c b/libdde-linux26/contrib/drivers/base/platform.c
new file mode 100644
index 00000000..349a1013
--- /dev/null
+++ b/libdde-linux26/contrib/drivers/base/platform.c
@@ -0,0 +1,988 @@
+/*
+ * 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/platform_device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/bootmem.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "base.h"
+
+#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
+ driver))
+
+struct device platform_bus = {
+ .init_name = "platform",
+};
+EXPORT_SYMBOL_GPL(platform_bus);
+
+/**
+ * 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 (type == resource_type(r) && num-- == 0)
+ return r;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(platform_get_resource);
+
+/**
+ * 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 : -ENXIO;
+}
+EXPORT_SYMBOL_GPL(platform_get_irq);
+
+/**
+ * platform_get_resource_byname - get a resource for a device by name
+ * @dev: platform device
+ * @type: resource type
+ * @name: resource name
+ */
+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 (type == resource_type(r) && !strcmp(r->name, name))
+ return r;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(platform_get_resource_byname);
+
+/**
+ * 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 : -ENXIO;
+}
+EXPORT_SYMBOL_GPL(platform_get_irq_byname);
+
+/**
+ * platform_add_devices - add a numbers of platform devices
+ * @devs: array of platform devices to add
+ * @num: number of platform devices in array
+ */
+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;
+}
+EXPORT_SYMBOL_GPL(platform_add_devices);
+
+struct platform_object {
+ struct platform_device pdev;
+ char name[1];
+};
+
+/**
+ * platform_device_put
+ * @pdev: platform device to free
+ *
+ * Free all memory associated with a platform device. This function must
+ * _only_ be externally called in error cases. All other usage is a bug.
+ */
+void platform_device_put(struct platform_device *pdev)
+{
+ if (pdev)
+ put_device(&pdev->dev);
+}
+EXPORT_SYMBOL_GPL(platform_device_put);
+
+static void platform_device_release(struct device *dev)
+{
+ struct platform_object *pa = container_of(dev, struct platform_object,
+ pdev.dev);
+
+ kfree(pa->pdev.dev.platform_data);
+ kfree(pa->pdev.resource);
+ kfree(pa);
+}
+
+/**
+ * platform_device_alloc
+ * @name: base name of the device we're adding
+ * @id: instance id
+ *
+ * Create a platform device object which can have other objects attached
+ * to it, and which will have attached objects freed when it is released.
+ */
+struct platform_device *platform_device_alloc(const char *name, int id)
+{
+ struct platform_object *pa;
+
+ pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
+ if (pa) {
+ strcpy(pa->name, name);
+ pa->pdev.name = pa->name;
+ pa->pdev.id = id;
+ device_initialize(&pa->pdev.dev);
+ pa->pdev.dev.release = platform_device_release;
+ }
+
+ return pa ? &pa->pdev : NULL;
+}
+EXPORT_SYMBOL_GPL(platform_device_alloc);
+
+/**
+ * platform_device_add_resources
+ * @pdev: platform device allocated by platform_device_alloc to add resources to
+ * @res: set of resources that needs to be allocated for the device
+ * @num: number of resources
+ *
+ * Add a copy of the resources to the platform device. The memory
+ * associated with the resources will be freed when the platform device is
+ * released.
+ */
+int platform_device_add_resources(struct platform_device *pdev,
+ struct resource *res, unsigned int num)
+{
+ struct resource *r;
+
+ r = kmalloc(sizeof(struct resource) * num, GFP_KERNEL);
+ if (r) {
+ memcpy(r, res, sizeof(struct resource) * num);
+ pdev->resource = r;
+ pdev->num_resources = num;
+ }
+ return r ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(platform_device_add_resources);
+
+/**
+ * platform_device_add_data
+ * @pdev: platform device allocated by platform_device_alloc to add resources to
+ * @data: platform specific data for this platform device
+ * @size: size of platform specific data
+ *
+ * Add a copy of platform specific data to the platform device's
+ * platform_data pointer. The memory associated with the platform data
+ * will be freed when the platform device is released.
+ */
+int platform_device_add_data(struct platform_device *pdev, const void *data,
+ size_t size)
+{
+ void *d;
+
+ d = kmalloc(size, GFP_KERNEL);
+ if (d) {
+ memcpy(d, data, size);
+ pdev->dev.platform_data = d;
+ }
+ return d ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(platform_device_add_data);
+
+/**
+ * platform_device_add - add a platform device to device hierarchy
+ * @pdev: platform device we're adding
+ *
+ * This is part 2 of platform_device_register(), though may be called
+ * separately _iff_ pdev was allocated by platform_device_alloc().
+ */
+int platform_device_add(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)
+ dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
+ else
+ dev_set_name(&pdev->dev, pdev->name);
+
+ for (i = 0; i < pdev->num_resources; i++) {
+ struct resource *p, *r = &pdev->resource[i];
+
+ if (r->name == NULL)
+ r->name = dev_name(&pdev->dev);
+
+ p = r->parent;
+ if (!p) {
+ if (resource_type(r) == IORESOURCE_MEM)
+ p = &iomem_resource;
+ else if (resource_type(r) == IORESOURCE_IO)
+ p = &ioport_resource;
+ }
+
+ if (p && insert_resource(p, r)) {
+ printk(KERN_ERR
+ "%s: failed to claim resource %d\n",
+ dev_name(&pdev->dev), i);
+ ret = -EBUSY;
+ goto failed;
+ }
+ }
+
+ pr_debug("Registering platform device '%s'. Parent at %s\n",
+ dev_name(&pdev->dev), dev_name(pdev->dev.parent));
+
+ ret = device_add(&pdev->dev);
+ if (ret == 0)
+ return ret;
+
+ failed:
+ while (--i >= 0) {
+ struct resource *r = &pdev->resource[i];
+ unsigned long type = resource_type(r);
+
+ if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
+ release_resource(r);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(platform_device_add);
+
+/**
+ * platform_device_del - remove a platform-level device
+ * @pdev: platform device we're removing
+ *
+ * Note that this function will also release all memory- and port-based
+ * resources owned by the device (@dev->resource). This function must
+ * _only_ be externally called in error cases. All other usage is a bug.
+ */
+void platform_device_del(struct platform_device *pdev)
+{
+ int i;
+
+ if (pdev) {
+ device_del(&pdev->dev);
+
+ for (i = 0; i < pdev->num_resources; i++) {
+ struct resource *r = &pdev->resource[i];
+ unsigned long type = resource_type(r);
+
+ if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
+ release_resource(r);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(platform_device_del);
+
+/**
+ * platform_device_register - add a platform-level device
+ * @pdev: platform device we're adding
+ */
+int platform_device_register(struct platform_device *pdev)
+{
+ device_initialize(&pdev->dev);
+ return platform_device_add(pdev);
+}
+EXPORT_SYMBOL_GPL(platform_device_register);
+
+/**
+ * platform_device_unregister - unregister a platform-level device
+ * @pdev: platform device we're unregistering
+ *
+ * Unregistration is done in 2 steps. First we release all resources
+ * and remove it from the subsystem, then we drop reference count by
+ * calling platform_device_put().
+ */
+void platform_device_unregister(struct platform_device *pdev)
+{
+ platform_device_del(pdev);
+ platform_device_put(pdev);
+}
+EXPORT_SYMBOL_GPL(platform_device_unregister);
+
+/**
+ * platform_device_register_simple
+ * @name: base name of the device we're adding
+ * @id: instance id
+ * @res: set of resources that needs to be allocated for the device
+ * @num: number of resources
+ *
+ * This function creates a simple platform device that requires minimal
+ * resource and memory management. Canned release function freeing memory
+ * allocated for the device allows drivers using such devices to be
+ * unloaded without waiting for the last reference to the device to be
+ * dropped.
+ *
+ * This interface is primarily intended for use with legacy drivers which
+ * probe hardware directly. Because such drivers create sysfs device nodes
+ * themselves, rather than letting system infrastructure handle such device
+ * enumeration tasks, they don't fully conform to the Linux driver model.
+ * In particular, when such drivers are built as modules, they can't be
+ * "hotplugged".
+ */
+struct platform_device *platform_device_register_simple(const char *name,
+ int id,
+ struct resource *res,
+ unsigned int num)
+{
+ struct platform_device *pdev;
+ int retval;
+
+ pdev = platform_device_alloc(name, id);
+ if (!pdev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ if (num) {
+ retval = platform_device_add_resources(pdev, res, num);
+ if (retval)
+ goto error;
+ }
+
+ retval = platform_device_add(pdev);
+ if (retval)
+ goto error;
+
+ return pdev;
+
+error:
+ platform_device_put(pdev);
+ return ERR_PTR(retval);
+}
+EXPORT_SYMBOL_GPL(platform_device_register_simple);
+
+/**
+ * platform_device_register_data
+ * @parent: parent device for the device we're adding
+ * @name: base name of the device we're adding
+ * @id: instance id
+ * @data: platform specific data for this platform device
+ * @size: size of platform specific data
+ *
+ * This function creates a simple platform device that requires minimal
+ * resource and memory management. Canned release function freeing memory
+ * allocated for the device allows drivers using such devices to be
+ * unloaded without waiting for the last reference to the device to be
+ * dropped.
+ */
+struct platform_device *platform_device_register_data(
+ struct device *parent,
+ const char *name, int id,
+ const void *data, size_t size)
+{
+ struct platform_device *pdev;
+ int retval;
+
+ pdev = platform_device_alloc(name, id);
+ if (!pdev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ pdev->dev.parent = parent;
+
+ if (size) {
+ retval = platform_device_add_data(pdev, data, size);
+ if (retval)
+ goto error;
+ }
+
+ retval = platform_device_add(pdev);
+ if (retval)
+ goto error;
+
+ return pdev;
+
+error:
+ platform_device_put(pdev);
+ return ERR_PTR(retval);
+}
+
+static int platform_drv_probe(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->probe(dev);
+}
+
+static int platform_drv_probe_fail(struct device *_dev)
+{
+ return -ENXIO;
+}
+
+static int platform_drv_remove(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->remove(dev);
+}
+
+static void platform_drv_shutdown(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ drv->shutdown(dev);
+}
+
+static int platform_drv_suspend(struct device *_dev, pm_message_t state)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->suspend(dev, state);
+}
+
+static int platform_drv_resume(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->resume(dev);
+}
+
+/**
+ * platform_driver_register
+ * @drv: platform driver structure
+ */
+int platform_driver_register(struct platform_driver *drv)
+{
+ drv->driver.bus = &platform_bus_type;
+ if (drv->probe)
+ drv->driver.probe = platform_drv_probe;
+ if (drv->remove)
+ drv->driver.remove = platform_drv_remove;
+ if (drv->shutdown)
+ drv->driver.shutdown = platform_drv_shutdown;
+ if (drv->suspend)
+ drv->driver.suspend = platform_drv_suspend;
+ if (drv->resume)
+ drv->driver.resume = platform_drv_resume;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(platform_driver_register);
+
+/**
+ * platform_driver_unregister
+ * @drv: platform driver structure
+ */
+void platform_driver_unregister(struct platform_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(platform_driver_unregister);
+
+/**
+ * platform_driver_probe - register driver for non-hotpluggable device
+ * @drv: platform driver structure
+ * @probe: the driver probe routine, probably from an __init section
+ *
+ * Use this instead of platform_driver_register() when you know the device
+ * is not hotpluggable and has already been registered, and you want to
+ * remove its run-once probe() infrastructure from memory after the driver
+ * has bound to the device.
+ *
+ * One typical use for this would be with drivers for controllers integrated
+ * into system-on-chip processors, where the controller devices have been
+ * configured as part of board setup.
+ *
+ * Returns zero if the driver registered and bound to a device, else returns
+ * a negative error code and with the driver not registered.
+ */
+int __init_or_module platform_driver_probe(struct platform_driver *drv,
+ int (*probe)(struct platform_device *))
+{
+ int retval, code;
+
+ /* temporary section violation during probe() */
+ drv->probe = probe;
+ retval = code = platform_driver_register(drv);
+
+ /* Fixup that section violation, being paranoid about code scanning
+ * the list of drivers in order to probe new devices. Check to see
+ * if the probe was successful, and make sure any forced probes of
+ * new devices fail.
+ */
+ spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
+ drv->probe = NULL;
+ if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
+ retval = -ENODEV;
+ drv->driver.probe = platform_drv_probe_fail;
+ spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);
+
+ if (code != retval)
+ platform_driver_unregister(drv);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(platform_driver_probe);
+
+/* modalias support enables more hands-off userspace setup:
+ * (a) environment variable lets new-style hotplug events work once system is
+ * fully running: "modprobe $MODALIAS"
+ * (b) sysfs attribute lets new-style coldplug recover from hotplug events
+ * mishandled before system is fully running: "modprobe $(cat modalias)"
+ */
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int len = snprintf(buf, PAGE_SIZE, "platform:%s\n", pdev->name);
+
+ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute platform_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ add_uevent_var(env, "MODALIAS=platform:%s", pdev->name);
+ return 0;
+}
+
+/**
+ * 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;
+
+ pdev = container_of(dev, struct platform_device, dev);
+ return (strcmp(pdev->name, drv->name) == 0);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->suspend)
+ ret = dev->driver->suspend(dev, mesg);
+
+ return ret;
+}
+
+static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg)
+{
+ struct platform_driver *drv = to_platform_driver(dev->driver);
+ struct platform_device *pdev;
+ int ret = 0;
+
+ pdev = container_of(dev, struct platform_device, dev);
+ if (dev->driver && drv->suspend_late)
+ ret = drv->suspend_late(pdev, mesg);
+
+ return ret;
+}
+
+static int platform_legacy_resume_early(struct device *dev)
+{
+ struct platform_driver *drv = to_platform_driver(dev->driver);
+ struct platform_device *pdev;
+ int ret = 0;
+
+ pdev = container_of(dev, struct platform_device, dev);
+ if (dev->driver && drv->resume_early)
+ ret = drv->resume_early(pdev);
+
+ return ret;
+}
+
+static int platform_legacy_resume(struct device *dev)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->resume)
+ ret = dev->driver->resume(dev);
+
+ return ret;
+}
+
+static int platform_pm_prepare(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm && drv->pm->prepare)
+ ret = drv->pm->prepare(dev);
+
+ return ret;
+}
+
+static void platform_pm_complete(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+
+ if (drv && drv->pm && drv->pm->complete)
+ drv->pm->complete(dev);
+}
+
+#ifdef CONFIG_SUSPEND
+
+static int platform_pm_suspend(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->suspend)
+ ret = drv->pm->suspend(dev);
+ } else {
+ ret = platform_legacy_suspend(dev, PMSG_SUSPEND);
+ }
+
+ return ret;
+}
+
+static int platform_pm_suspend_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->suspend_noirq)
+ ret = drv->pm->suspend_noirq(dev);
+ } else {
+ ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND);
+ }
+
+ return ret;
+}
+
+static int platform_pm_resume(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->resume)
+ ret = drv->pm->resume(dev);
+ } else {
+ ret = platform_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_resume_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->resume_noirq)
+ ret = drv->pm->resume_noirq(dev);
+ } else {
+ ret = platform_legacy_resume_early(dev);
+ }
+
+ return ret;
+}
+
+#else /* !CONFIG_SUSPEND */
+
+#define platform_pm_suspend NULL
+#define platform_pm_resume NULL
+#define platform_pm_suspend_noirq NULL
+#define platform_pm_resume_noirq NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_HIBERNATION
+
+static int platform_pm_freeze(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->freeze)
+ ret = drv->pm->freeze(dev);
+ } else {
+ ret = platform_legacy_suspend(dev, PMSG_FREEZE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_freeze_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->freeze_noirq)
+ ret = drv->pm->freeze_noirq(dev);
+ } else {
+ ret = platform_legacy_suspend_late(dev, PMSG_FREEZE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_thaw(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->thaw)
+ ret = drv->pm->thaw(dev);
+ } else {
+ ret = platform_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_thaw_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->thaw_noirq)
+ ret = drv->pm->thaw_noirq(dev);
+ } else {
+ ret = platform_legacy_resume_early(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_poweroff(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->poweroff)
+ ret = drv->pm->poweroff(dev);
+ } else {
+ ret = platform_legacy_suspend(dev, PMSG_HIBERNATE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_poweroff_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->poweroff_noirq)
+ ret = drv->pm->poweroff_noirq(dev);
+ } else {
+ ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_restore(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->restore)
+ ret = drv->pm->restore(dev);
+ } else {
+ ret = platform_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_restore_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->restore_noirq)
+ ret = drv->pm->restore_noirq(dev);
+ } else {
+ ret = platform_legacy_resume_early(dev);
+ }
+
+ return ret;
+}
+
+#else /* !CONFIG_HIBERNATION */
+
+#define platform_pm_freeze NULL
+#define platform_pm_thaw NULL
+#define platform_pm_poweroff NULL
+#define platform_pm_restore NULL
+#define platform_pm_freeze_noirq NULL
+#define platform_pm_thaw_noirq NULL
+#define platform_pm_poweroff_noirq NULL
+#define platform_pm_restore_noirq NULL
+
+#endif /* !CONFIG_HIBERNATION */
+
+static struct dev_pm_ops platform_dev_pm_ops = {
+ .prepare = platform_pm_prepare,
+ .complete = platform_pm_complete,
+ .suspend = platform_pm_suspend,
+ .resume = platform_pm_resume,
+ .freeze = platform_pm_freeze,
+ .thaw = platform_pm_thaw,
+ .poweroff = platform_pm_poweroff,
+ .restore = platform_pm_restore,
+ .suspend_noirq = platform_pm_suspend_noirq,
+ .resume_noirq = platform_pm_resume_noirq,
+ .freeze_noirq = platform_pm_freeze_noirq,
+ .thaw_noirq = platform_pm_thaw_noirq,
+ .poweroff_noirq = platform_pm_poweroff_noirq,
+ .restore_noirq = platform_pm_restore_noirq,
+};
+
+#define PLATFORM_PM_OPS_PTR (&platform_dev_pm_ops)
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define PLATFORM_PM_OPS_PTR NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
+struct bus_type platform_bus_type = {
+ .name = "platform",
+ .dev_attrs = platform_dev_attrs,
+ .match = platform_match,
+ .uevent = platform_uevent,
+ .pm = PLATFORM_PM_OPS_PTR,
+};
+EXPORT_SYMBOL_GPL(platform_bus_type);
+
+int __init platform_bus_init(void)
+{
+ int error;
+
+ error = device_register(&platform_bus);
+ if (error)
+ return error;
+ error = bus_register(&platform_bus_type);
+ if (error)
+ device_unregister(&platform_bus);
+ return error;
+}
+
+#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;
+}
+EXPORT_SYMBOL_GPL(dma_get_required_mask);
+#endif
diff --git a/libdde-linux26/contrib/drivers/base/power/power.h b/libdde-linux26/contrib/drivers/base/power/power.h
new file mode 100644
index 00000000..41f51fae
--- /dev/null
+++ b/libdde-linux26/contrib/drivers/base/power/power.h
@@ -0,0 +1,49 @@
+static inline void device_pm_init(struct device *dev)
+{
+ dev->power.status = DPM_ON;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+/*
+ * main.c
+ */
+
+extern struct list_head dpm_list; /* The active device list */
+
+static inline struct device *to_device(struct list_head *entry)
+{
+ return container_of(entry, struct device, power.entry);
+}
+
+extern void device_pm_add(struct device *);
+extern void device_pm_remove(struct device *);
+
+#else /* CONFIG_PM_SLEEP */
+
+static inline void device_pm_add(struct device *dev) {}
+static inline void device_pm_remove(struct device *dev) {}
+
+#endif
+
+#ifdef CONFIG_PM
+
+/*
+ * sysfs.c
+ */
+
+extern int dpm_sysfs_add(struct device *);
+extern void dpm_sysfs_remove(struct device *);
+
+#else /* CONFIG_PM */
+
+static inline int dpm_sysfs_add(struct device *dev)
+{
+ return 0;
+}
+
+static inline void dpm_sysfs_remove(struct device *dev)
+{
+}
+
+#endif
diff --git a/libdde-linux26/contrib/drivers/base/sys.c b/libdde-linux26/contrib/drivers/base/sys.c
new file mode 100644
index 00000000..b428c8c4
--- /dev/null
+++ b/libdde-linux26/contrib/drivers/base/sys.c
@@ -0,0 +1,525 @@
+/*
+ * 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/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>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+#include "base.h"
+
+#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, sysdev_attr, buffer);
+ return -EIO;
+}
+
+
+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, sysdev_attr, buffer, count);
+ return -EIO;
+}
+
+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);
+
+#define to_sysdev_class(k) container_of(k, struct sysdev_class, kset.kobj)
+#define to_sysdev_class_attr(a) container_of(a, \
+ struct sysdev_class_attribute, attr)
+
+static ssize_t sysdev_class_show(struct kobject *kobj, struct attribute *attr,
+ char *buffer)
+{
+ struct sysdev_class * class = to_sysdev_class(kobj);
+ struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr);
+
+ if (class_attr->show)
+ return class_attr->show(class, buffer);
+ return -EIO;
+}
+
+static ssize_t sysdev_class_store(struct kobject *kobj, struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct sysdev_class * class = to_sysdev_class(kobj);
+ struct sysdev_class_attribute * class_attr = to_sysdev_class_attr(attr);
+
+ if (class_attr->store)
+ return class_attr->store(class, buffer, count);
+ return -EIO;
+}
+
+static struct sysfs_ops sysfs_class_ops = {
+ .show = sysdev_class_show,
+ .store = sysdev_class_store,
+};
+
+static struct kobj_type ktype_sysdev_class = {
+ .sysfs_ops = &sysfs_class_ops,
+};
+
+int sysdev_class_create_file(struct sysdev_class *c,
+ struct sysdev_class_attribute *a)
+{
+ return sysfs_create_file(&c->kset.kobj, &a->attr);
+}
+EXPORT_SYMBOL_GPL(sysdev_class_create_file);
+
+void sysdev_class_remove_file(struct sysdev_class *c,
+ struct sysdev_class_attribute *a)
+{
+ sysfs_remove_file(&c->kset.kobj, &a->attr);
+}
+EXPORT_SYMBOL_GPL(sysdev_class_remove_file);
+
+static struct kset *system_kset;
+
+int sysdev_class_register(struct sysdev_class * cls)
+{
+ pr_debug("Registering sysdev class '%s'\n", cls->name);
+
+ INIT_LIST_HEAD(&cls->drivers);
+ memset(&cls->kset.kobj, 0x00, sizeof(struct kobject));
+ cls->kset.kobj.parent = &system_kset->kobj;
+ cls->kset.kobj.ktype = &ktype_sysdev_class;
+ cls->kset.kobj.kset = system_kset;
+ kobject_set_name(&cls->kset.kobj, cls->name);
+ return kset_register(&cls->kset);
+}
+
+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 DEFINE_MUTEX(sysdev_drivers_lock);
+
+/**
+ * sysdev_driver_register - Register auxillary driver
+ * @cls: Device class driver belongs to.
+ * @drv: Driver.
+ *
+ * @drv is inserted into @cls->drivers to be
+ * called on each operation on devices of that class. The refcount
+ * of @cls is incremented.
+ */
+
+int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)
+{
+ int err = 0;
+
+ if (!cls) {
+ WARN(1, KERN_WARNING "sysdev: invalid class passed to "
+ "sysdev_driver_register!\n");
+ return -EINVAL;
+ }
+
+ /* Check whether this driver has already been added to a class. */
+ if (drv->entry.next && !list_empty(&drv->entry))
+ WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already"
+ " been registered to a class, something is wrong, but "
+ "will forge on!\n", cls->name, drv);
+
+ mutex_lock(&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 {
+ err = -EINVAL;
+ WARN(1, KERN_ERR "%s: invalid device class\n", __func__);
+ }
+ mutex_unlock(&sysdev_drivers_lock);
+ return err;
+}
+
+
+/**
+ * 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)
+{
+ mutex_lock(&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);
+ }
+ mutex_unlock(&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;
+
+ pr_debug("Registering sys device of class '%s'\n",
+ kobject_name(&cls->kset.kobj));
+
+ /* initialize the kobject to 0, in case it had previously been used */
+ memset(&sysdev->kobj, 0x00, sizeof(struct kobject));
+
+ /* Make sure the kset is set */
+ sysdev->kobj.kset = &cls->kset;
+
+ /* Register the object */
+ error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL,
+ "%s%d", kobject_name(&cls->kset.kobj),
+ sysdev->id);
+
+ if (!error) {
+ struct sysdev_driver * drv;
+
+ pr_debug("Registering sys device '%s'\n",
+ kobject_name(&sysdev->kobj));
+
+ mutex_lock(&sysdev_drivers_lock);
+ /* Generic notification is implicit, because it's that
+ * code that should have called us.
+ */
+
+ /* Notify class auxillary drivers */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->add)
+ drv->add(sysdev);
+ }
+ mutex_unlock(&sysdev_drivers_lock);
+ }
+
+ kobject_uevent(&sysdev->kobj, KOBJ_ADD);
+ return error;
+}
+
+void sysdev_unregister(struct sys_device * sysdev)
+{
+ struct sysdev_driver * drv;
+
+ mutex_lock(&sysdev_drivers_lock);
+ list_for_each_entry(drv, &sysdev->cls->drivers, entry) {
+ if (drv->remove)
+ drv->remove(sysdev);
+ }
+ mutex_unlock(&sysdev_drivers_lock);
+
+ kobject_put(&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 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");
+
+ mutex_lock(&sysdev_drivers_lock);
+ list_for_each_entry_reverse(cls, &system_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 auxillary drivers first */
+ 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);
+ }
+ }
+ mutex_unlock(&sysdev_drivers_lock);
+}
+
+static void __sysdev_resume(struct sys_device *dev)
+{
+ struct sysdev_class *cls = dev->cls;
+ struct sysdev_driver *drv;
+
+ /* First, call the class-specific one */
+ if (cls->resume)
+ cls->resume(dev);
+
+ /* Call auxillary drivers next. */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->resume)
+ drv->resume(dev);
+ }
+}
+
+/**
+ * sysdev_suspend - Suspend all system devices.
+ * @state: Power state to enter.
+ *
+ * We perform an almost identical operation as sysdev_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(pm_message_t state)
+{
+ struct sysdev_class * cls;
+ struct sys_device *sysdev, *err_dev;
+ struct sysdev_driver *drv, *err_drv;
+ int ret;
+
+ pr_debug("Suspending System Devices\n");
+
+ list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {
+ pr_debug("Suspending type '%s':\n",
+ kobject_name(&cls->kset.kobj));
+
+ list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
+ pr_debug(" %s\n", kobject_name(&sysdev->kobj));
+
+ /* Call auxillary drivers first */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->suspend) {
+ ret = drv->suspend(sysdev, state);
+ if (ret)
+ goto aux_driver;
+ }
+ }
+
+ /* Now call the generic one */
+ if (cls->suspend) {
+ ret = cls->suspend(sysdev, state);
+ if (ret)
+ goto cls_driver;
+ }
+ }
+ }
+ return 0;
+ /* resume current sysdev */
+cls_driver:
+ drv = NULL;
+ printk(KERN_ERR "Class suspend failed for %s\n",
+ kobject_name(&sysdev->kobj));
+
+aux_driver:
+ if (drv)
+ printk(KERN_ERR "Class driver suspend failed for %s\n",
+ kobject_name(&sysdev->kobj));
+ list_for_each_entry(err_drv, &cls->drivers, entry) {
+ if (err_drv == drv)
+ break;
+ if (err_drv->resume)
+ err_drv->resume(sysdev);
+ }
+
+ /* resume other sysdevs in current class */
+ list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
+ if (err_dev == sysdev)
+ break;
+ pr_debug(" %s\n", kobject_name(&err_dev->kobj));
+ __sysdev_resume(err_dev);
+ }
+
+ /* resume other classes */
+ list_for_each_entry_continue(cls, &system_kset->list, kset.kobj.entry) {
+ list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
+ pr_debug(" %s\n", kobject_name(&err_dev->kobj));
+ __sysdev_resume(err_dev);
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sysdev_suspend);
+
+/**
+ * sysdev_resume - Bring system devices back to life.
+ *
+ * Similar to sysdev_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_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) {
+ pr_debug(" %s\n", kobject_name(&sysdev->kobj));
+
+ __sysdev_resume(sysdev);
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sysdev_resume);
+
+int __init system_bus_init(void)
+{
+ system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
+ if (!system_kset)
+ return -ENOMEM;
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(sysdev_register);
+EXPORT_SYMBOL_GPL(sysdev_unregister);
+
+#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr)
+
+ssize_t sysdev_store_ulong(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ char *end;
+ unsigned long new = simple_strtoul(buf, &end, 0);
+ if (end == buf)
+ return -EINVAL;
+ *(unsigned long *)(ea->var) = new;
+ /* Always return full write size even if we didn't consume all */
+ return size;
+}
+EXPORT_SYMBOL_GPL(sysdev_store_ulong);
+
+ssize_t sysdev_show_ulong(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ char *buf)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ return snprintf(buf, PAGE_SIZE, "%lx\n", *(unsigned long *)(ea->var));
+}
+EXPORT_SYMBOL_GPL(sysdev_show_ulong);
+
+ssize_t sysdev_store_int(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ char *end;
+ long new = simple_strtol(buf, &end, 0);
+ if (end == buf || new > INT_MAX || new < INT_MIN)
+ return -EINVAL;
+ *(int *)(ea->var) = new;
+ /* Always return full write size even if we didn't consume all */
+ return size;
+}
+EXPORT_SYMBOL_GPL(sysdev_store_int);
+
+ssize_t sysdev_show_int(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ char *buf)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ return snprintf(buf, PAGE_SIZE, "%d\n", *(int *)(ea->var));
+}
+EXPORT_SYMBOL_GPL(sysdev_show_int);
+