summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@osdl.org>2002-05-28 18:54:23 -0700
committerPatrick Mochel <mochel@osdl.org>2002-05-28 18:54:23 -0700
commit21a16f65b95259e28e99eb81977c790e77133ffb (patch)
tree8c7bf46d4cf7f13a5dd32aeb899f957df6ad4adc
parent83ca4c7e89ffc8fb30dd05754895dc381e0afbc6 (diff)
parent4b4a837f2b57467de03001bb9003382d1c9a7f18 (diff)
Merge master.kernel.org:/home/mochel/BK/linux-2.5
into osdl.org:/home/mochel/src/kernel/devel/linux-2.5-sync
-rw-r--r--drivers/base/Makefile5
-rw-r--r--drivers/base/driver.c70
-rw-r--r--drivers/pci/hotplug.c3
-rw-r--r--drivers/pci/pci-driver.c124
-rw-r--r--drivers/pci/probe.c3
-rw-r--r--include/linux/device.h31
-rw-r--r--include/linux/pci.h2
7 files changed, 186 insertions, 52 deletions
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index eae7bc965fcb..a20768fa9630 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -1,7 +1,8 @@
O_TARGET := base.o
-obj-y := core.o sys.o interface.o fs.o power.o bus.o
+obj-y := core.o sys.o interface.o fs.o power.o bus.o \
+ driver.o
-export-objs := core.o fs.o power.o sys.o bus.o
+export-objs := core.o fs.o power.o sys.o bus.o driver.o
include $(TOPDIR)/Rules.make
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
new file mode 100644
index 000000000000..13a7038c4826
--- /dev/null
+++ b/drivers/base/driver.c
@@ -0,0 +1,70 @@
+/*
+ * driver.c - centralized device driver management
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include "base.h"
+
+/**
+ * driver_make_dir - create a driverfs directory for a driver
+ * @drv: driver in question
+ */
+static int driver_make_dir(struct device_driver * drv)
+{
+ drv->dir.name = drv->name;
+ return device_create_dir(&drv->dir,&drv->bus->driver_dir);
+}
+
+/**
+ * driver_register - register driver with bus
+ * @drv: driver to register
+ *
+ * Add to bus's list of devices
+ */
+int driver_register(struct device_driver * drv)
+{
+ if (!drv->bus)
+ return -EINVAL;
+
+ pr_debug("Registering driver '%s' with bus '%s'\n",drv->name,drv->bus->name);
+
+ get_bus(drv->bus);
+ atomic_set(&drv->refcount,2);
+ rwlock_init(&drv->lock);
+ write_lock(&drv->bus->lock);
+ list_add(&drv->bus_list,&drv->bus->drivers);
+ write_unlock(&drv->bus->lock);
+ driver_make_dir(drv);
+ put_driver(drv);
+ return 0;
+}
+
+/**
+ * put_driver - decrement driver's refcount and clean up if necessary
+ * @drv: driver in question
+ */
+void put_driver(struct device_driver * drv)
+{
+ if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
+ return;
+ spin_unlock(&device_lock);
+
+ if (drv->bus) {
+ pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name);
+
+ write_lock(&drv->bus->lock);
+ list_del_init(&drv->bus_list);
+ write_unlock(&drv->bus->lock);
+
+ driverfs_remove_dir(&drv->dir);
+ put_bus(drv->bus);
+ }
+ if (drv->release)
+ drv->release(drv);
+}
+
+EXPORT_SYMBOL(driver_register);
+EXPORT_SYMBOL(put_driver);
diff --git a/drivers/pci/hotplug.c b/drivers/pci/hotplug.c
index 90eb3d8d426e..96b02d5be4be 100644
--- a/drivers/pci/hotplug.c
+++ b/drivers/pci/hotplug.c
@@ -2,7 +2,6 @@
#include <linux/module.h>
#include <linux/kmod.h> /* for hotplug_path */
-extern struct list_head pci_drivers;
extern int pci_announce_device(struct pci_driver *drv, struct pci_dev *dev);
#ifndef FALSE
@@ -61,7 +60,7 @@ pci_announce_device_to_drivers(struct pci_dev *dev)
{
struct list_head *ln;
- for(ln=pci_drivers.next; ln != &pci_drivers; ln=ln->next) {
+ for(ln=pci_bus_type.drivers.next; ln != &pci_bus_type.drivers; ln=ln->next) {
struct pci_driver *drv = list_entry(ln, struct pci_driver, node);
if (drv->remove && pci_announce_device(drv, dev))
break;
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 8ff363949d68..228f10eba8b1 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -10,8 +10,6 @@
* Registration of PCI drivers and handling of hot-pluggable devices.
*/
-LIST_HEAD(pci_drivers);
-
/**
* pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
* @ids: array of PCI device id structures to search in
@@ -61,6 +59,57 @@ out:
return ret;
}
+
+static int pci_device_probe(struct device * dev)
+{
+ int error = 0;
+
+ struct pci_driver * drv = list_entry(dev->driver,struct pci_driver,driver);
+ struct pci_dev * pci_dev = list_entry(dev,struct pci_dev,dev);
+
+ if (drv->probe)
+ error = drv->probe(pci_dev,NULL);
+ printk("%s: returning %d\n",__FUNCTION__,error);
+ return error > 0 ? 0 : -ENODEV;
+}
+
+static int pci_device_remove(struct device * dev, u32 flags)
+{
+ struct pci_dev * pci_dev = list_entry(dev,struct pci_dev,dev);
+
+ if (dev->driver) {
+ struct pci_driver * drv = list_entry(dev->driver,struct pci_driver,driver);
+ if (drv->remove)
+ drv->remove(pci_dev);
+ }
+ return 0;
+}
+
+static int pci_device_suspend(struct device * dev, u32 state, u32 level)
+{
+ struct pci_dev * pci_dev = (struct pci_dev *)list_entry(dev,struct pci_dev,dev);
+ int error = 0;
+
+ if (pci_dev->driver) {
+ if (level == SUSPEND_SAVE_STATE && pci_dev->driver->save_state)
+ error = pci_dev->driver->save_state(pci_dev,state);
+ else if (level == SUSPEND_POWER_DOWN && pci_dev->driver->suspend)
+ error = pci_dev->driver->suspend(pci_dev,state);
+ }
+ return error;
+}
+
+static int pci_device_resume(struct device * dev, u32 level)
+{
+ struct pci_dev * pci_dev = (struct pci_dev *)list_entry(dev,struct pci_dev,dev);
+
+ if (pci_dev->driver) {
+ if (level == RESUME_POWER_ON && pci_dev->driver->resume)
+ pci_dev->driver->resume(pci_dev);
+ }
+ return 0;
+}
+
/**
* pci_register_driver - register a new pci driver
* @drv: the driver structure to register
@@ -73,15 +122,25 @@ out:
int
pci_register_driver(struct pci_driver *drv)
{
- struct pci_dev *dev;
int count = 0;
+ struct pci_dev * dev;
+
+ /* initialize common driver fields */
+ drv->driver.name = drv->name;
+ drv->driver.bus = &pci_bus_type;
+ drv->driver.probe = pci_device_probe;
+ drv->driver.resume = pci_device_resume;
+ drv->driver.suspend = pci_device_suspend;
+ drv->driver.remove = pci_device_remove;
+
+ /* register with core */
+ count = driver_register(&drv->driver);
- list_add_tail(&drv->node, &pci_drivers);
pci_for_each_dev(dev) {
if (!pci_dev_driver(dev))
- count += pci_announce_device(drv, dev);
+ pci_announce_device(drv, dev);
}
- return count;
+ return count ? count : 1;
}
/**
@@ -97,16 +156,21 @@ pci_register_driver(struct pci_driver *drv)
void
pci_unregister_driver(struct pci_driver *drv)
{
- struct pci_dev *dev;
-
- list_del(&drv->node);
- pci_for_each_dev(dev) {
- if (dev->driver == drv) {
- if (drv->remove)
- drv->remove(dev);
- dev->driver = NULL;
- }
+ list_t * node;
+
+ node = drv->driver.devices.next;
+
+ while (node != &drv->driver.devices) {
+ struct device * dev = list_entry(node,struct device,driver_list);
+ struct pci_dev * pci_dev = list_entry(dev,struct pci_dev,dev);
+
+ if (drv->remove)
+ drv->remove(pci_dev);
+ pci_dev->driver = NULL;
+ dev->driver = NULL;
+ list_del_init(&dev->driver_list);
}
+ put_driver(&drv->driver);
}
static struct pci_driver pci_compat_driver = {
@@ -134,36 +198,6 @@ pci_dev_driver(const struct pci_dev *dev)
return NULL;
}
-static int pci_device_suspend(struct device * dev, u32 state, u32 level)
-{
- struct pci_dev * pci_dev = (struct pci_dev *)list_entry(dev,struct pci_dev,dev);
- int error = 0;
-
- if (pci_dev->driver) {
- if (level == SUSPEND_SAVE_STATE && pci_dev->driver->save_state)
- error = pci_dev->driver->save_state(pci_dev,state);
- else if (level == SUSPEND_POWER_DOWN && pci_dev->driver->suspend)
- error = pci_dev->driver->suspend(pci_dev,state);
- }
- return error;
-}
-
-static int pci_device_resume(struct device * dev, u32 level)
-{
- struct pci_dev * pci_dev = (struct pci_dev *)list_entry(dev,struct pci_dev,dev);
-
- if (pci_dev->driver) {
- if (level == RESUME_POWER_ON && pci_dev->driver->resume)
- pci_dev->driver->resume(pci_dev);
- }
- return 0;
-}
-
-struct device_driver pci_device_driver = {
- suspend: pci_device_suspend,
- resume: pci_device_resume,
-};
-
struct bus_type pci_bus_type = {
name: "pci",
};
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 77e0cb72abfe..75b84219d66b 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -18,8 +18,6 @@
LIST_HEAD(pci_root_buses);
LIST_HEAD(pci_devices);
-extern struct device_driver pci_device_driver;
-
/*
* Translate the low bits of the PCI base
* to the resource type
@@ -511,7 +509,6 @@ unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus)
dev0.bus = bus;
dev0.sysdata = bus->sysdata;
dev0.dev.parent = bus->dev;
- dev0.dev.driver = &pci_device_driver;
dev0.dev.bus = &pci_bus_type;
/* Go find them, Rover! */
diff --git a/include/linux/device.h b/include/linux/device.h
index 078c65e02370..2ad1e9ff4f75 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -84,17 +84,48 @@ extern void put_bus(struct bus_type * bus);
struct device_driver {
+ char * name;
+ struct bus_type * bus;
+
+ rwlock_t lock;
+ atomic_t refcount;
+
+ list_t bus_list;
+ list_t devices;
+
+ struct driver_dir_entry dir;
+
int (*probe) (struct device * dev);
int (*remove) (struct device * dev, u32 flags);
int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level);
+
+ void (*release) (struct device_driver * drv);
};
+
+
+extern int driver_register(struct device_driver * drv);
+
+static inline struct device_driver * get_driver(struct device_driver * drv)
+{
+ BUG_ON(!atomic_read(&drv->refcount));
+ atomic_inc(&drv->refcount);
+ return drv;
+}
+
+extern void put_driver(struct device_driver * drv);
+
+extern int driver_for_each_dev(struct device_driver * drv, void * data,
+ int (*callback)(struct device * dev, void * data));
+
+
struct device {
struct list_head g_list; /* node in depth-first order list */
struct list_head node; /* node in sibling list */
struct list_head bus_list; /* node in bus's list */
+ struct list_head driver_list;
struct list_head children;
struct device * parent;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index f6e3a68a2a0d..7cbc68fdb743 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -487,6 +487,8 @@ struct pci_driver {
int (*suspend) (struct pci_dev *dev, u32 state); /* Device suspended */
int (*resume) (struct pci_dev *dev); /* Device woken up */
int (*enable_wake) (struct pci_dev *dev, u32 state, int enable); /* Enable wake event */
+
+ struct device_driver driver;
};