summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/base.h12
-rw-r--r--drivers/base/bus.c90
-rw-r--r--drivers/base/class.c12
-rw-r--r--drivers/base/core.c24
-rw-r--r--drivers/base/intf.c275
-rw-r--r--drivers/base/node.c4
-rw-r--r--drivers/base/power.c22
7 files changed, 344 insertions, 95 deletions
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 6194d88242ec..ab31ff74207c 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -1,13 +1,5 @@
#undef DEBUG
-#ifdef DEBUG
-# define DBG(x...) printk(x)
-#else
-# define DBG(x...)
-#endif
-
-extern struct list_head global_device_list;
-extern spinlock_t device_lock;
extern struct semaphore device_sem;
extern int bus_add_device(struct device * dev);
@@ -22,8 +14,8 @@ extern void devclass_remove_device(struct device *);
extern int devclass_add_driver(struct device_driver *);
extern void devclass_remove_driver(struct device_driver *);
-extern int interface_add(struct device_class *, struct device *);
-extern void interface_remove(struct device_class *, struct device *);
+extern int interface_add_dev(struct device *);
+extern void interface_remove_dev(struct device *);
#ifdef CONFIG_HOTPLUG
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index d18effa7f981..ca5ef19aa4a9 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -133,6 +133,91 @@ struct subsystem bus_subsys = {
/**
+ * 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 list_head * head, * entry;
+ int error = 0;
+
+ if (!(bus = get_bus(bus)))
+ return -EINVAL;
+
+ head = start ? &start->bus_list : &bus->devices;
+
+ down_read(&bus->subsys.rwsem);
+ list_for_each(entry,head) {
+ struct device * dev = get_device(to_dev(entry));
+ error = fn(dev,data);
+ put_device(dev);
+ if (error)
+ break;
+ }
+ up_read(&bus->subsys.rwsem);
+ return error;
+}
+
+/**
+ * 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 list_head * head, * entry;
+ int error = 0;
+
+ if(!(bus = get_bus(bus)))
+ return -EINVAL;
+
+ head = start ? &start->bus_list : &bus->drivers;
+
+ down_read(&bus->subsys.rwsem);
+ list_for_each(entry,head) {
+ struct device_driver * drv = get_driver(to_drv(entry));
+ error = fn(drv,data);
+ put_driver(drv);
+ if(error)
+ break;
+ }
+ up_read(&bus->subsys.rwsem);
+ return error;
+}
+
+/**
* attach - add device to driver.
* @dev: device.
*
@@ -190,7 +275,7 @@ static int bus_match(struct device * dev, struct device_driver * drv)
*/
static int device_attach(struct device * dev)
{
- struct bus_type * bus = dev->bus;
+ struct bus_type * bus = dev->bus;
struct list_head * entry;
int error = 0;
@@ -455,6 +540,9 @@ static int __init bus_subsys_init(void)
core_initcall(bus_subsys_init);
+EXPORT_SYMBOL(bus_for_each_dev);
+EXPORT_SYMBOL(bus_for_each_drv);
+
EXPORT_SYMBOL(bus_add_device);
EXPORT_SYMBOL(bus_remove_device);
EXPORT_SYMBOL(bus_register);
diff --git a/drivers/base/class.c b/drivers/base/class.c
index fbc6326312d8..86c7775c42bd 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -2,7 +2,7 @@
* class.c - basic device class management
*/
-#undef DEBUG
+#define DEBUG
#include <linux/device.h>
#include <linux/module.h>
@@ -98,7 +98,6 @@ void devclass_remove_file(struct device_class * cls, struct devclass_attribute *
}
-
int devclass_add_driver(struct device_driver * drv)
{
struct device_class * cls = get_devclass(drv->devclass);
@@ -172,9 +171,11 @@ int devclass_add_device(struct device * dev)
error = cls->add_device(dev);
if (!error) {
enum_device(cls,dev);
- interface_add(cls,dev);
+ interface_add_dev(dev);
}
+ list_add_tail(&dev->class_list,&cls->devices);
+
/* notify userspace (call /sbin/hotplug) */
class_hotplug (dev, "add");
@@ -196,9 +197,11 @@ void devclass_remove_device(struct device * dev)
down_write(&cls->subsys.rwsem);
pr_debug("device class %s: removing device %s\n",
cls->name,dev->name);
- interface_remove(cls,dev);
+ interface_remove_dev(dev);
unenum_device(cls,dev);
+ list_del(&dev->class_list);
+
/* notify userspace (call /sbin/hotplug) */
class_hotplug (dev, "remove");
@@ -224,6 +227,7 @@ void put_devclass(struct device_class * cls)
int devclass_register(struct device_class * cls)
{
INIT_LIST_HEAD(&cls->drivers);
+ INIT_LIST_HEAD(&cls->devices);
pr_debug("device class '%s': registering\n",cls->name);
strncpy(cls->subsys.kobj.name,cls->name,KOBJ_NAME_LEN);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 6425fc8e0634..a5ab807d9f49 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -18,15 +18,11 @@
#include "base.h"
-LIST_HEAD(global_device_list);
-
int (*platform_notify)(struct device * dev) = NULL;
int (*platform_notify_remove)(struct device * dev) = NULL;
DECLARE_MUTEX(device_sem);
-spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
-
#define to_dev(obj) container_of(obj,struct device,kobj)
@@ -146,11 +142,10 @@ void device_initialize(struct device *dev)
kobject_init(&dev->kobj);
INIT_LIST_HEAD(&dev->node);
INIT_LIST_HEAD(&dev->children);
- INIT_LIST_HEAD(&dev->g_list);
INIT_LIST_HEAD(&dev->driver_list);
INIT_LIST_HEAD(&dev->bus_list);
+ INIT_LIST_HEAD(&dev->class_list);
INIT_LIST_HEAD(&dev->intf_list);
-// spin_lock_init(&dev->lock);
}
/**
@@ -188,13 +183,11 @@ int device_add(struct device *dev)
goto register_done;
/* now take care of our own registration */
- down(&device_sem);
if (parent) {
- list_add_tail(&dev->g_list,&dev->parent->g_list);
+ down(&device_sem);
list_add_tail(&dev->node,&parent->children);
- } else
- list_add_tail(&dev->g_list,&global_device_list);
- up(&device_sem);
+ up(&device_sem);
+ }
bus_add_device(dev);
@@ -276,10 +269,11 @@ void device_del(struct device * dev)
{
struct device * parent = dev->parent;
- down(&device_sem);
- list_del_init(&dev->node);
- list_del_init(&dev->g_list);
- up(&device_sem);
+ if (parent) {
+ down(&device_sem);
+ list_del_init(&dev->node);
+ up(&device_sem);
+ }
/* Notify the platform of the removal, in case they
* need to do anything...
diff --git a/drivers/base/intf.c b/drivers/base/intf.c
index d31ad38bf3d2..63c5a4d12cd4 100644
--- a/drivers/base/intf.c
+++ b/drivers/base/intf.c
@@ -2,7 +2,7 @@
* intf.c - class-specific interface management
*/
-#undef DEBUG
+#define DEBUG
#include <linux/device.h>
#include <linux/module.h>
@@ -10,98 +10,267 @@
#include "base.h"
-#define to_intf(node) container_of(node,struct device_interface,kobj.entry)
+#define to_intf(node) container_of(node,struct device_interface,subsys.kobj.entry)
+
+#define to_data(e) container_of(e,struct intf_data,kobj.entry)
+
+#define intf_from_data(d) container_of(d->kobj.subsys,struct device_interface, subsys);
+
/**
- * intf_dev_link - symlink from interface's directory to device's directory
+ * intf_dev_link - create sysfs symlink for interface.
+ * @data: interface data descriptor.
*
+ * Create a symlink 'phys' in the interface's directory to
*/
+
static int intf_dev_link(struct intf_data * data)
{
- char linkname[16];
-
- snprintf(linkname,16,"%u",data->intf_num);
- return sysfs_create_link(&data->intf->kobj,&data->dev->kobj,linkname);
+ char name[16];
+ snprintf(name,16,"%d",data->intf_num);
+ return sysfs_create_link(&data->intf->subsys.kobj,&data->dev->kobj,name);
}
+/**
+ * intf_dev_unlink - remove symlink for interface.
+ * @intf: interface data descriptor.
+ *
+ */
+
static void intf_dev_unlink(struct intf_data * data)
{
- char linkname[16];
- snprintf(linkname,16,"%u",data->intf_num);
- sysfs_remove_link(&data->intf->kobj,linkname);
+ char name[16];
+ snprintf(name,16,"%d",data->intf_num);
+ sysfs_remove_link(&data->intf->subsys.kobj,name);
+}
+
+
+/**
+ * interface_add_data - attach data descriptor
+ * @data: interface data descriptor.
+ *
+ * This attaches the per-instance interface object to the
+ * interface (by registering its kobject) and the device
+ * itself (by inserting it into the device's list).
+ *
+ * Note that there is no explicit protection done in this
+ * function. This should be called from the interface's
+ * add_device() method, which is called under the protection
+ * of the class's rwsem.
+ */
+
+int interface_add_data(struct intf_data * data)
+{
+ struct device_interface * intf = intf_from_data(data);
+
+ data->intf_num = data->intf->devnum++;
+ data->kobj.subsys = &intf->subsys;
+ kobject_register(&data->kobj);
+
+ list_add_tail(&data->dev_entry,&data->dev->intf_list);
+ intf_dev_link(data);
+ return 0;
+}
+
+
+/**
+ * interface_remove_data - detach data descriptor.
+ * @data: interface data descriptor.
+ *
+ * This detaches the per-instance data descriptor by removing
+ * it from the device's list and unregistering the kobject from
+ * the subsystem.
+ */
+
+void interface_remove_data(struct intf_data * data)
+{
+ intf_dev_unlink(data);
+ list_del_init(&data->dev_entry);
+ kobject_unregister(&data->kobj);
}
+/**
+ * add - attach device to interface
+ * @intf: interface.
+ * @dev: device.
+ *
+ * This is just a simple helper. Check the interface's interface
+ * helper and call it. This is called when adding an interface
+ * the class's devices, or a device to the class's interfaces.
+ */
+
+static int add(struct device_interface * intf, struct device * dev)
+{
+ int error = 0;
+
+ if (intf->add_device)
+ error = intf->add_device(dev);
+ pr_debug(" -> %s (%d)\n",dev->bus_id,error);
+ return error;
+}
+
+/**
+ * del - detach device from interface.
+ * @data: interface data descriptor.
+ *
+ * Another simple helper. Remove the data descriptor from
+ * the device and the interface, then call the interface's
+ * remove_device() method.
+ */
+
+static void del(struct intf_data * data)
+{
+ struct device_interface * intf = intf_from_data(data);
+
+ pr_debug(" -> %s ",data->intf->name);
+ interface_remove_data(data);
+ if (intf->remove_device)
+ intf->remove_device(data);
+}
+
+#define to_dev(entry) container_of(entry,struct device,class_list)
+
+
+/**
+ * add_intf - add class's devices to interface.
+ * @intf: interface.
+ *
+ * Loop over the devices registered with the class, and call
+ * the interface's add_device() method for each.
+ *
+ * On an error, we won't break, but we will print debugging info.
+ */
+static void add_intf(struct device_interface * intf)
+{
+ struct device_class * cls = intf->devclass;
+ struct list_head * entry;
+
+ down_write(&cls->subsys.rwsem);
+ list_for_each(entry,&cls->devices)
+ add(intf,to_dev(entry));
+ up_write(&cls->subsys.rwsem);
+}
+
+/**
+ * interface_register - register an interface with a device class.
+ * @intf: interface.
+ *
+ * An interface may be loaded after drivers and devices have been
+ * added to the class. So, we must add each device already known to
+ * the class to the interface as its registered.
+ */
+
int interface_register(struct device_interface * intf)
{
struct device_class * cls = get_devclass(intf->devclass);
- int error = 0;
if (cls) {
pr_debug("register interface '%s' with class '%s'\n",
intf->name,cls->name);
- strncpy(intf->kobj.name,intf->name,KOBJ_NAME_LEN);
- intf->kobj.subsys = &cls->subsys;
- kobject_register(&intf->kobj);
- } else
- error = -EINVAL;
- return error;
+
+ strncpy(intf->subsys.kobj.name,intf->name,KOBJ_NAME_LEN);
+ intf->subsys.kobj.subsys = &cls->subsys;
+ subsystem_register(&intf->subsys);
+ add_intf(intf);
+ }
+ return 0;
}
+
+/**
+ * del_intf - remove devices from interface.
+ * @intf: interface being unloaded.
+ *
+ * This loops over the devices registered with a class and
+ * calls the interface's remove_device() method for each.
+ * This is called when an interface is being unregistered.
+ */
+
+static void del_intf(struct device_interface * intf)
+{
+ struct list_head * entry;
+
+ down_write(&intf->devclass->subsys.rwsem);
+ list_for_each(entry,&intf->subsys.list) {
+ struct intf_data * data = to_data(entry);
+ del(data);
+ }
+ up_write(&intf->devclass->subsys.rwsem);
+}
+
+/**
+ * interface_unregister - remove interface from class.
+ * @intf: interface.
+ *
+ * This is called when an interface in unloaded, giving it a
+ * chance to remove itself from devicse that have been added to
+ * it.
+ */
+
void interface_unregister(struct device_interface * intf)
{
- pr_debug("unregistering interface '%s' from class '%s'\n",
- intf->name,intf->devclass->name);
- kobject_unregister(&intf->kobj);
+ struct device_class * cls = intf->devclass;
+ if (cls) {
+ pr_debug("unregistering interface '%s' from class '%s'\n",
+ intf->name,cls->name);
+ del_intf(intf);
+ subsystem_unregister(&intf->subsys);
+ put_devclass(cls);
+ }
}
-int interface_add(struct device_class * cls, struct device * dev)
+
+/**
+ * interface_add_dev - add device to interfaces.
+ * @dev: device.
+ *
+ * This is a helper for the class driver core. When a
+ * device is being added to a class, this is called to add
+ * the device to all the interfaces in the class.
+ *
+ * The operation is simple enough: loop over the interfaces
+ * and call add() [above] for each. The class rwsem is assumed
+ * to be held.
+ */
+
+int interface_add_dev(struct device * dev)
{
+ struct device_class * cls = dev->driver->devclass;
struct list_head * node;
- int error = 0;
- pr_debug("adding '%s' to %s class interfaces\n",dev->name,cls->name);
+ pr_debug("interfaces: adding device %s\n",dev->name);
list_for_each(node,&cls->subsys.list) {
struct device_interface * intf = to_intf(node);
- if (intf->add_device) {
- error = intf->add_device(dev);
- if (error)
- pr_debug("%s:%s: adding '%s' failed: %d\n",
- cls->name,intf->name,dev->name,error);
- }
-
+ add(intf,dev);
}
return 0;
}
-void interface_remove(struct device_class * cls, struct device * dev)
-{
- struct list_head * node;
- struct list_head * next;
- pr_debug("remove '%s' from %s class interfaces: ",dev->name,cls->name);
+/**
+ * interface_remove_dev - remove device from interfaces.
+ * @dev: device.
+ *
+ * This is another helper for the class driver core, and called
+ * when the device is being removed from the class.
+ *
+ * We iterate over the list of interface data descriptors attached
+ * to the device, and call del() [above] for each. Again, the
+ * class's rwsem is assumed to be held during this.
+ */
- list_for_each_safe(node,next,&dev->intf_list) {
- struct intf_data * intf_data = container_of(node,struct intf_data,node);
- list_del_init(&intf_data->node);
+void interface_remove_dev(struct device * dev)
+{
+ struct list_head * entry, * next;
- intf_dev_unlink(intf_data);
- pr_debug("%s ",intf_data->intf->name);
- if (intf_data->intf->remove_device)
- intf_data->intf->remove_device(intf_data);
- }
- pr_debug("\n");
-}
+ pr_debug("interfaces: removing device %s\n",dev->name);
-int interface_add_data(struct intf_data * data)
-{
- down_write(&data->intf->devclass->subsys.rwsem);
- list_add_tail(&data->node,&data->dev->intf_list);
- data->intf_num = data->intf->devnum++;
- intf_dev_link(data);
- up_write(&data->intf->devclass->subsys.rwsem);
- return 0;
+ list_for_each_safe(entry,next,&dev->intf_list) {
+ struct intf_data * intf_data = to_data(entry);
+ del(intf_data);
+ }
}
EXPORT_SYMBOL(interface_register);
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 631cdc395e05..815567bd72fe 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -93,7 +93,7 @@ int __init register_node(struct node *node, int num, struct node *parent)
static int __init register_node_type(void)
{
- driver_register(&node_driver);
- return devclass_register(&node_devclass);
+ devclass_register(&node_devclass);
+ return driver_register(&node_driver);
}
postcore_initcall(register_node_type);
diff --git a/drivers/base/power.c b/drivers/base/power.c
index 594a793e24f9..9e38aa766ef1 100644
--- a/drivers/base/power.c
+++ b/drivers/base/power.c
@@ -15,7 +15,9 @@
#include <asm/semaphore.h>
#include "base.h"
-#define to_dev(node) container_of(node,struct device,g_list)
+#define to_dev(node) container_of(node,struct device,kobj.entry)
+
+extern struct subsystem device_subsys;
/**
* device_suspend - suspend/remove all devices on the device ree
@@ -35,8 +37,8 @@ int device_suspend(u32 state, u32 level)
printk(KERN_EMERG "Suspending devices\n");
- down(&device_sem);
- list_for_each(node,&global_device_list) {
+ down_write(&device_subsys.rwsem);
+ list_for_each(node,&device_subsys.list) {
struct device * dev = to_dev(node);
if (dev->driver && dev->driver->suspend) {
pr_debug("suspending device %s\n",dev->name);
@@ -45,7 +47,7 @@ int device_suspend(u32 state, u32 level)
printk(KERN_ERR "%s: suspend returned %d\n",dev->name,error);
}
}
- up(&device_sem);
+ up_write(&device_subsys.rwsem);
return error;
}
@@ -61,15 +63,15 @@ void device_resume(u32 level)
{
struct list_head * node;
- down(&device_sem);
- list_for_each_prev(node,&global_device_list) {
+ down_write(&device_subsys.rwsem);
+ list_for_each_prev(node,&device_subsys.list) {
struct device * dev = to_dev(node);
if (dev->driver && dev->driver->resume) {
pr_debug("resuming device %s\n",dev->name);
dev->driver->resume(dev,level);
}
}
- up(&device_sem);
+ up_write(&device_subsys.rwsem);
printk(KERN_EMERG "Devices Resumed\n");
}
@@ -83,15 +85,15 @@ void device_shutdown(void)
printk(KERN_EMERG "Shutting down devices\n");
- down(&device_sem);
- list_for_each(entry,&global_device_list) {
+ down_write(&device_subsys.rwsem);
+ list_for_each(entry,&device_subsys.list) {
struct device * dev = to_dev(entry);
if (dev->driver && dev->driver->shutdown) {
pr_debug("shutting down %s\n",dev->name);
dev->driver->shutdown(dev);
}
}
- up(&device_sem);
+ up_write(&device_subsys.rwsem);
}
EXPORT_SYMBOL(device_suspend);