diff options
| -rw-r--r-- | drivers/base/core.c | 39 | ||||
| -rw-r--r-- | drivers/base/driver.c | 35 | ||||
| -rw-r--r-- | drivers/pci/pci-driver.c | 32 | ||||
| -rw-r--r-- | include/linux/device.h | 1 |
4 files changed, 81 insertions, 26 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 573719ba5e55..5b29a4943f7f 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -5,6 +5,8 @@ * 2002 Open Source Development Lab */ +#define DEBUG 0 + #include <linux/device.h> #include <linux/module.h> #include <linux/slab.h> @@ -118,9 +120,8 @@ int driver_bind(struct device_driver * drv) return bus_for_each_dev(drv->bus,drv,do_driver_bind); } -static int do_driver_unbind(struct device * dev, void * data) +static int do_driver_unbind(struct device * dev, struct device_driver * drv) { - struct device_driver * drv = (struct device_driver *)data; lock_device(dev); if (dev->driver == drv) { dev->driver = NULL; @@ -134,7 +135,31 @@ static int do_driver_unbind(struct device * dev, void * data) void driver_unbind(struct device_driver * drv) { - driver_for_each_dev(drv,drv,do_driver_unbind); + struct device * next; + struct device * dev = NULL; + struct list_head * node; + int error = 0; + + read_lock(&drv->lock); + node = drv->devices.next; + while (node != &drv->devices) { + next = list_entry(node,struct device,driver_list); + get_device(next); + read_unlock(&drv->lock); + + if (dev) + put_device(dev); + dev = next; + if ((error = do_driver_unbind(dev,drv))) { + put_device(dev); + break; + } + read_lock(&drv->lock); + node = dev->driver_list.next; + } + read_unlock(&drv->lock); + if (dev) + put_device(dev); } /** @@ -178,8 +203,8 @@ int device_register(struct device *dev) } spin_unlock(&device_lock); - DBG("DEV: registering device: ID = '%s', name = %s\n", - dev->bus_id, dev->name); + pr_debug("DEV: registering device: ID = '%s', name = %s\n", + dev->bus_id, dev->name); if ((error = device_make_dir(dev))) goto register_done; @@ -212,8 +237,8 @@ void put_device(struct device * dev) list_del_init(&dev->g_list); spin_unlock(&device_lock); - DBG("DEV: Unregistering device. ID = '%s', name = '%s'\n", - dev->bus_id,dev->name); + pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n", + dev->bus_id,dev->name); /* Notify the platform of the removal, in case they * need to do anything... diff --git a/drivers/base/driver.c b/drivers/base/driver.c index b640347eeeda..820d4bac5651 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -3,6 +3,8 @@ * */ +#define DEBUG 0 + #include <linux/device.h> #include <linux/module.h> #include <linux/errno.h> @@ -67,6 +69,7 @@ int driver_register(struct device_driver * drv) get_bus(drv->bus); atomic_set(&drv->refcount,2); rwlock_init(&drv->lock); + INIT_LIST_HEAD(&drv->devices); write_lock(&drv->bus->lock); list_add(&drv->bus_list,&drv->bus->drivers); write_unlock(&drv->bus->lock); @@ -76,16 +79,8 @@ int driver_register(struct device_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) +static void __remove_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); @@ -101,6 +96,28 @@ void put_driver(struct device_driver * drv) drv->release(drv); } +void remove_driver(struct device_driver * drv) +{ + spin_lock(&device_lock); + atomic_set(&drv->refcount,0); + spin_unlock(&device_lock); + __remove_driver(drv); +} + +/** + * 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); + + __remove_driver(drv); +} + EXPORT_SYMBOL(driver_for_each_dev); EXPORT_SYMBOL(driver_register); EXPORT_SYMBOL(put_driver); +EXPORT_SYMBOL(remove_driver); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 0109b2fad2c6..e5bad37436f9 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -38,23 +38,35 @@ pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) 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,drv->id_table); - return error > 0 ? 0 : -ENODEV; + struct pci_driver *drv; + struct pci_dev *pci_dev; + + drv = list_entry(dev->driver, struct pci_driver, driver); + pci_dev = list_entry(dev, struct pci_dev, dev); + + if (drv->probe) { + const struct pci_device_id *id; + + id = pci_match_device(drv->id_table, pci_dev); + if (id) + error = drv->probe(pci_dev, id); + if (error >= 0) { + pci_dev->driver = drv; + error = 0; + } + } + return error; } static int pci_device_remove(struct device * dev) { struct pci_dev * pci_dev = list_entry(dev,struct pci_dev,dev); + struct pci_driver * drv = pci_dev->driver; - if (dev->driver) { - struct pci_driver * drv = list_entry(dev->driver,struct pci_driver,driver); + if (drv) { if (drv->remove) drv->remove(pci_dev); + pci_dev->driver = NULL; } return 0; } @@ -124,7 +136,7 @@ pci_register_driver(struct pci_driver *drv) void pci_unregister_driver(struct pci_driver *drv) { - put_driver(&drv->driver); + remove_driver(&drv->driver); } static struct pci_driver pci_compat_driver = { diff --git a/include/linux/device.h b/include/linux/device.h index 2186f85a8beb..1c35f87abc9e 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -118,6 +118,7 @@ static inline struct device_driver * get_driver(struct device_driver * drv) } extern void put_driver(struct device_driver * drv); +extern void remove_driver(struct device_driver * drv); extern int driver_for_each_dev(struct device_driver * drv, void * data, int (*callback)(struct device * dev, void * data)); |
