diff options
| -rw-r--r-- | arch/arm/kernel/ecard.c | 2 | ||||
| -rw-r--r-- | drivers/base/base.h | 7 | ||||
| -rw-r--r-- | drivers/base/bus.c | 241 | ||||
| -rw-r--r-- | drivers/base/class.c | 132 | ||||
| -rw-r--r-- | drivers/base/core.c | 215 | ||||
| -rw-r--r-- | drivers/base/driver.c | 107 | ||||
| -rw-r--r-- | drivers/base/interface.c | 2 | ||||
| -rw-r--r-- | drivers/base/power.c | 68 | ||||
| -rw-r--r-- | drivers/base/sys.c | 6 | ||||
| -rw-r--r-- | drivers/input/serio/sa1111ps2.c | 2 | ||||
| -rw-r--r-- | drivers/pci/pci-driver.c | 2 | ||||
| -rw-r--r-- | drivers/pcmcia/sa1111_generic.c | 2 | ||||
| -rw-r--r-- | drivers/scsi/scsi_debug.c | 3 | ||||
| -rw-r--r-- | drivers/scsi/sd.c | 2 | ||||
| -rw-r--r-- | drivers/scsi/sg.c | 2 | ||||
| -rw-r--r-- | drivers/scsi/sr.c | 2 | ||||
| -rw-r--r-- | drivers/scsi/st.c | 2 | ||||
| -rw-r--r-- | drivers/usb/core/usb.c | 4 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-sa1111.c | 2 | ||||
| -rw-r--r-- | fs/driverfs/inode.c | 4 | ||||
| -rw-r--r-- | include/linux/device.h | 49 | ||||
| -rw-r--r-- | include/linux/driverfs_fs.h | 2 |
22 files changed, 466 insertions, 392 deletions
diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c index 4cb68c31b86e..2b291c31ff8b 100644 --- a/arch/arm/kernel/ecard.c +++ b/arch/arm/kernel/ecard.c @@ -1114,7 +1114,7 @@ int ecard_register_driver(struct ecard_driver *drv) void ecard_remove_driver(struct ecard_driver *drv) { - remove_driver(&drv->drv); + driver_unregister(&drv->drv); } static int ecard_match(struct device *_dev, struct device_driver *_drv) diff --git a/drivers/base/base.h b/drivers/base/base.h index 0c05ae058971..3f294f8cafff 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -8,6 +8,7 @@ extern struct list_head global_device_list; extern spinlock_t device_lock; +extern struct semaphore device_sem; extern struct device * get_device_locked(struct device *); @@ -20,6 +21,9 @@ extern void device_remove_dir(struct device * dev); extern int bus_make_dir(struct bus_type * bus); extern void bus_remove_dir(struct bus_type * bus); +extern int bus_add_driver(struct device_driver *); +extern void bus_remove_driver(struct device_driver *); + extern int driver_make_dir(struct device_driver * drv); extern void driver_remove_dir(struct device_driver * drv); @@ -48,9 +52,6 @@ extern int interface_add(struct device_class *, struct device *); extern void interface_remove(struct device_class *, struct device *); -extern int driver_attach(struct device_driver * drv); -extern void driver_detach(struct device_driver * drv); - #ifdef CONFIG_HOTPLUG extern int dev_hotplug(struct device *dev, const char *action); #else diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 7daffbfd9913..49683a1d3898 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -43,28 +43,23 @@ int bus_for_each_dev(struct bus_type * bus, void * data, int (*callback)(struct device * dev, void * data)) { struct list_head * node; - struct device * prev = NULL; int error = 0; - get_bus(bus); - spin_lock(&device_lock); - list_for_each(node,&bus->devices) { - struct device * dev = get_device_locked(to_dev(node)); - if (dev) { - spin_unlock(&device_lock); - error = callback(dev,data); - if (prev) - put_device(prev); - prev = dev; - spin_lock(&device_lock); - if (error) - break; + bus = get_bus(bus); + if (bus) { + down_read(&bus->rwsem); + list_for_each(node,&bus->devices) { + struct device * dev = get_device(to_dev(node)); + if (dev) { + error = callback(dev,data); + put_device(dev); + if (error) + break; + } } + up_read(&bus->rwsem); + put_bus(bus); } - spin_unlock(&device_lock); - if (prev) - put_device(prev); - put_bus(bus); return error; } @@ -72,33 +67,126 @@ int bus_for_each_drv(struct bus_type * bus, void * data, int (*callback)(struct device_driver * drv, void * data)) { struct list_head * node; - struct device_driver * prev = NULL; int error = 0; - /* pin bus in memory */ - get_bus(bus); + bus = get_bus(bus); + if (bus) { + down_read(&bus->rwsem); + list_for_each(node,&bus->drivers) { + struct device_driver * drv = get_driver(to_drv(node)); + if (drv) { + error = callback(drv,data); + put_driver(drv); + if (error) + break; + } + } + up_read(&bus->rwsem); + put_bus(bus); + } + return error; +} - spin_lock(&device_lock); - list_for_each(node,&bus->drivers) { - struct device_driver * drv = get_driver(to_drv(node)); - if (drv) { - spin_unlock(&device_lock); - error = callback(drv,data); - if (prev) - put_driver(prev); - prev = drv; - spin_lock(&device_lock); - if (error) - break; +static void attach(struct device * dev) +{ + pr_debug("bound device '%s' to driver '%s'\n", + dev->bus_id,dev->driver->name); + list_add_tail(&dev->driver_list,&dev->driver->devices); +} + +static int bus_match(struct device * dev, struct device_driver * drv) +{ + int error = 0; + if (dev->bus->match(dev,drv)) { + dev->driver = drv; + if (drv->probe) { + if (!(error = drv->probe(dev))) + attach(dev); + else + dev->driver = NULL; } } - spin_unlock(&device_lock); - if (prev) - put_driver(prev); - put_bus(bus); return error; } +static int device_attach(struct device * dev) +{ + struct bus_type * bus = dev->bus; + struct list_head * entry; + int error = 0; + + if (dev->driver) { + attach(dev); + return 0; + } + + if (!bus->match) + return 0; + + list_for_each(entry,&bus->drivers) { + struct device_driver * drv = + get_driver(container_of(entry,struct device_driver,bus_list)); + if (!drv) + continue; + error = bus_match(dev,drv); + put_driver(drv); + if (!error) + break; + } + return error; +} + +static int driver_attach(struct device_driver * drv) +{ + struct bus_type * bus = drv->bus; + struct list_head * entry; + int error = 0; + + if (!bus->match) + return 0; + + list_for_each(entry,&bus->devices) { + struct device * dev = container_of(entry,struct device,bus_list); + if (get_device(dev)) { + if (!dev->driver) { + if (!bus_match(dev,drv) && dev->driver) + devclass_add_device(dev); + } + put_device(dev); + } + } + return error; +} + +static void detach(struct device * dev, struct device_driver * drv) +{ + if (drv) { + list_del_init(&dev->driver_list); + devclass_remove_device(dev); + if (drv->remove) + drv->remove(dev); + dev->driver = NULL; + } +} + +static void device_detach(struct device * dev) +{ + detach(dev,dev->driver); +} + +static void driver_detach(struct device_driver * drv) +{ + struct list_head * entry, * next; + list_for_each_safe(entry,next,&drv->devices) { + struct device * dev = container_of(entry,struct device,driver_list); + if (get_device(dev)) { + detach(dev,drv); + put_device(dev); + } + } + +} + /** * bus_add_device - add device to bus * @dev: device being added @@ -110,12 +198,13 @@ int bus_for_each_drv(struct bus_type * bus, void * data, */ int bus_add_device(struct device * dev) { - if (dev->bus) { - pr_debug("registering %s with bus '%s'\n",dev->bus_id,dev->bus->name); - get_bus(dev->bus); - spin_lock(&device_lock); + struct bus_type * bus = get_bus(dev->bus); + if (bus) { + down_write(&dev->bus->rwsem); + pr_debug("bus %s: add device %s\n",bus->name,dev->bus_id); list_add_tail(&dev->bus_list,&dev->bus->devices); - spin_unlock(&device_lock); + device_attach(dev); + up_write(&dev->bus->rwsem); device_bus_link(dev); } return 0; @@ -131,17 +220,70 @@ int bus_add_device(struct device * dev) void bus_remove_device(struct device * dev) { if (dev->bus) { + down_write(&dev->bus->rwsem); + pr_debug("bus %s: remove device %s\n",dev->bus->name,dev->bus_id); device_remove_symlink(&dev->bus->device_dir,dev->bus_id); + device_detach(dev); + list_del_init(&dev->bus_list); + up_write(&dev->bus->rwsem); put_bus(dev->bus); } } +int bus_add_driver(struct device_driver * drv) +{ + struct bus_type * bus = get_bus(drv->bus); + if (bus) { + down_write(&bus->rwsem); + pr_debug("bus %s: add driver %s\n",bus->name,drv->name); + list_add_tail(&drv->bus_list,&bus->drivers); + driver_attach(drv); + up_write(&bus->rwsem); + driver_make_dir(drv); + } + return 0; +} + +void bus_remove_driver(struct device_driver * drv) +{ + if (drv->bus) { + down_write(&drv->bus->rwsem); + pr_debug("bus %s: remove driver %s\n",drv->bus->name,drv->name); + driver_detach(drv); + list_del_init(&drv->bus_list); + up_write(&drv->bus->rwsem); + } +} + +struct bus_type * get_bus(struct bus_type * bus) +{ + struct bus_type * ret = bus; + spin_lock(&device_lock); + if (bus && bus->present && atomic_read(&bus->refcount)) + atomic_inc(&bus->refcount); + else + ret = NULL; + spin_unlock(&device_lock); + return ret; +} + +void put_bus(struct bus_type * bus) +{ + if (!atomic_dec_and_lock(&bus->refcount,&device_lock)) + return; + list_del_init(&bus->node); + spin_unlock(&device_lock); + BUG_ON(bus->present); + bus_remove_dir(bus); +} + int bus_register(struct bus_type * bus) { - rwlock_init(&bus->lock); + init_rwsem(&bus->rwsem); INIT_LIST_HEAD(&bus->devices); INIT_LIST_HEAD(&bus->drivers); atomic_set(&bus->refcount,2); + bus->present = 1; spin_lock(&device_lock); list_add_tail(&bus->node,&bus_driver_list); @@ -156,13 +298,14 @@ int bus_register(struct bus_type * bus) return 0; } -void put_bus(struct bus_type * bus) +void bus_unregister(struct bus_type * bus) { - if (!atomic_dec_and_lock(&bus->refcount,&device_lock)) - return; - list_del_init(&bus->node); + spin_lock(&device_lock); + bus->present = 0; spin_unlock(&device_lock); - bus_remove_dir(bus); + + pr_debug("bus %s: unregistering\n",bus->name); + put_bus(bus); } EXPORT_SYMBOL(bus_for_each_dev); @@ -170,4 +313,6 @@ EXPORT_SYMBOL(bus_for_each_drv); EXPORT_SYMBOL(bus_add_device); EXPORT_SYMBOL(bus_remove_device); EXPORT_SYMBOL(bus_register); +EXPORT_SYMBOL(bus_unregister); +EXPORT_SYMBOL(get_bus); EXPORT_SYMBOL(put_bus); diff --git a/drivers/base/class.c b/drivers/base/class.c index dfef9793871a..7328f3231a65 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -10,27 +10,29 @@ static LIST_HEAD(class_list); int devclass_add_driver(struct device_driver * drv) { - if (drv->devclass) { - pr_debug("Registering driver %s:%s with class %s\n", - drv->bus->name,drv->name,drv->devclass->name); - - spin_lock(&device_lock); - list_add_tail(&drv->class_list,&drv->devclass->drivers); - spin_unlock(&device_lock); + struct device_class * cls = get_devclass(drv->devclass); + if (cls) { + down_write(&cls->rwsem); + pr_debug("device class %s: adding driver %s:%s\n", + cls->name,drv->bus->name,drv->name); + list_add_tail(&drv->class_list,&cls->drivers); devclass_drv_link(drv); + up_write(&cls->rwsem); } return 0; } void devclass_remove_driver(struct device_driver * drv) { - if (drv->devclass) { - pr_debug("Removing driver %s:%s:%s\n", - drv->devclass->name,drv->bus->name,drv->name); - spin_lock(&device_lock); + struct device_class * cls = drv->devclass; + if (cls) { + down_write(&cls->rwsem); + pr_debug("device class %s: removing driver %s:%s\n", + cls->name,drv->bus->name,drv->name); list_del_init(&drv->class_list); - spin_unlock(&device_lock); devclass_drv_unlink(drv); + up_write(&cls->rwsem); + put_devclass(cls); } } @@ -38,9 +40,7 @@ void devclass_remove_driver(struct device_driver * drv) static void enum_device(struct device_class * cls, struct device * dev) { u32 val; - spin_lock(&device_lock); val = cls->devnum++; - spin_unlock(&device_lock); dev->class_num = val; devclass_dev_link(cls,dev); } @@ -51,18 +51,44 @@ static void unenum_device(struct device_class * cls, struct device * dev) dev->class_num = 0; } +/** + * devclass_add_device - register device with device class + * @dev: device to be registered + * + * This is called when a device is either registered with the + * core, or after the a driver module is loaded and bound to + * the device. + * The class is determined by looking at @dev's driver, so one + * way or another, it must be bound to something. Once the + * class is determined, it's set to prevent against concurrent + * calls for the same device stomping on each other. + * + * /sbin/hotplug should be called once the device is added to + * class and all the interfaces. + */ int devclass_add_device(struct device * dev) { - struct device_class * cls = dev->driver->devclass; + struct device_class * cls; int error = 0; - if (cls) { - pr_debug("adding device '%s' to class '%s'\n", - dev->name,cls->name); - if (cls->add_device) - error = cls->add_device(dev); - if (!error) { - enum_device(cls,dev); - interface_add(cls,dev); + + if (dev->driver) { + cls = get_devclass(dev->driver->devclass); + if (cls) { + down_write(&cls->rwsem); + pr_debug("device class %s: adding device %s\n", + cls->name,dev->name); + if (cls->add_device) + error = cls->add_device(dev); + if (!error) { + enum_device(cls,dev); + interface_add(cls,dev); + } + + /* notify userspace (call /sbin/hotplug) here */ + + up_write(&cls->rwsem); + if (error) + put_devclass(cls); } } return error; @@ -70,40 +96,74 @@ int devclass_add_device(struct device * dev) void devclass_remove_device(struct device * dev) { - struct device_class * cls = dev->driver->devclass; - if (cls) { - pr_debug("removing device '%s' from class '%s'\n", - dev->name,cls->name); - interface_remove(cls,dev); - unenum_device(cls,dev); - if (cls->remove_device) - cls->remove_device(dev); + struct device_class * cls; + + if (dev->driver) { + cls = dev->driver->devclass; + if (cls) { + down_write(&cls->rwsem); + pr_debug("device class %s: removing device %s\n", + cls->name,dev->name); + interface_remove(cls,dev); + unenum_device(cls,dev); + if (cls->remove_device) + cls->remove_device(dev); + up_write(&cls->rwsem); + put_devclass(cls); + } } } +struct device_class * get_devclass(struct device_class * cls) +{ + struct device_class * ret = cls; + spin_lock(&device_lock); + if (cls && cls->present && atomic_read(&cls->refcount) > 0) + atomic_inc(&cls->refcount); + else + ret = NULL; + spin_unlock(&device_lock); + return ret; +} + +void put_devclass(struct device_class * cls) +{ + if (atomic_dec_and_lock(&cls->refcount,&device_lock)) { + list_del_init(&cls->node); + spin_unlock(&device_lock); + devclass_remove_dir(cls); + } +} + + int devclass_register(struct device_class * cls) { INIT_LIST_HEAD(&cls->drivers); INIT_LIST_HEAD(&cls->intf_list); - - pr_debug("registering device class '%s'\n",cls->name); + init_rwsem(&cls->rwsem); + atomic_set(&cls->refcount,2); + cls->present = 1; + pr_debug("device class '%s': registering\n",cls->name); spin_lock(&device_lock); list_add_tail(&cls->node,&class_list); spin_unlock(&device_lock); devclass_make_dir(cls); + put_devclass(cls); return 0; } void devclass_unregister(struct device_class * cls) { - pr_debug("unregistering device class '%s'\n",cls->name); - devclass_remove_dir(cls); spin_lock(&device_lock); - list_del_init(&class_list); + cls->present = 0; spin_unlock(&device_lock); + pr_debug("device class '%s': unregistering\n",cls->name); + put_devclass(cls); } EXPORT_SYMBOL(devclass_register); EXPORT_SYMBOL(devclass_unregister); +EXPORT_SYMBOL(get_devclass); +EXPORT_SYMBOL(put_devclass); diff --git a/drivers/base/core.c b/drivers/base/core.c index 83c31723d844..bd82a792d877 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -19,136 +19,12 @@ 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(node) container_of(node,struct device,driver_list) -static int probe(struct device * dev, struct device_driver * drv) -{ - dev->driver = drv; - return drv->probe ? drv->probe(dev) : 0; -} - -static void attach(struct device * dev) -{ - spin_lock(&device_lock); - list_add_tail(&dev->driver_list,&dev->driver->devices); - spin_unlock(&device_lock); - devclass_add_device(dev); -} - -/** - * found_match - do actual binding of device to driver - * @dev: device - * @drv: driver - * - * We're here because the bus's match callback returned success for this - * pair. We call the driver's probe callback to verify they're really a - * match made in heaven. - * - * In the future, we may want to notify userspace of the binding. (But, - * we might not want to do it here). - * - * We may also want to create a symlink in the driver's directory to the - * device's physical directory. - */ -static int found_match(struct device * dev, struct device_driver * drv) -{ - int error = 0; - - if (!(error = probe(dev,get_driver(drv)))) { - pr_debug("bound device '%s' to driver '%s'\n", - dev->bus_id,drv->name); - attach(dev); - } else { - put_driver(drv); - dev->driver = NULL; - } - return error; -} - -/** - * device_attach - try to associated device with a driver - * @drv: current driver to try - * @data: device in disguise - * - * This function is used as a callback to bus_for_each_drv. - * It calls the bus's match callback to check if the driver supports - * the device. If so, it calls the found_match() function above to - * take care of all the details. - */ -static int do_device_attach(struct device_driver * drv, void * data) -{ - struct device * dev = (struct device *)data; - int error = 0; - - if (drv->bus->match && drv->bus->match(dev,drv)) - error = found_match(dev,drv); - return error; -} - -static int device_attach(struct device * dev) -{ - int error = 0; - if (!dev->driver) { - if (dev->bus) - error = bus_for_each_drv(dev->bus,dev,do_device_attach); - } else - attach(dev); - return error; -} - -static void device_detach(struct device * dev) -{ - struct device_driver * drv = dev->driver; - - if (drv) { - devclass_remove_device(dev); - if (drv && drv->remove) - drv->remove(dev); - dev->driver = NULL; - } -} - -static int do_driver_attach(struct device * dev, void * data) -{ - struct device_driver * drv = (struct device_driver *)data; - int error = 0; - - if (!dev->driver) { - if (dev->bus->match && dev->bus->match(dev,drv)) - error = found_match(dev,drv); - } - return error; -} - -int driver_attach(struct device_driver * drv) -{ - return bus_for_each_dev(drv->bus,drv,do_driver_attach); -} - -void driver_detach(struct device_driver * drv) -{ - struct list_head * node; - struct device * prev = NULL; - - spin_lock(&device_lock); - list_for_each(node,&drv->devices) { - struct device * dev = get_device_locked(to_dev(node)); - if (dev) { - if (prev) - list_del_init(&prev->driver_list); - spin_unlock(&device_lock); - device_detach(dev); - if (prev) - put_device(prev); - prev = dev; - spin_lock(&device_lock); - } - } - spin_unlock(&device_lock); -} - int device_add(struct device *dev) { int error; @@ -156,14 +32,14 @@ int device_add(struct device *dev) if (!dev || !strlen(dev->bus_id)) return -EINVAL; - spin_lock(&device_lock); - dev->present = 1; + down(&device_sem); + dev->state = DEVICE_REGISTERED; if (dev->parent) { list_add_tail(&dev->g_list,&dev->parent->g_list); list_add_tail(&dev->node,&dev->parent->children); } else list_add_tail(&dev->g_list,&global_device_list); - spin_unlock(&device_lock); + up(&device_sem); pr_debug("DEV: registering device: ID = '%s', name = %s\n", dev->bus_id, dev->name); @@ -173,9 +49,6 @@ int device_add(struct device *dev) bus_add_device(dev); - /* bind to driver */ - device_attach(dev); - /* notify platform of device entry */ if (platform_notify) platform_notify(dev); @@ -183,12 +56,13 @@ int device_add(struct device *dev) /* notify userspace of device entry */ dev_hotplug(dev, "add"); + devclass_add_device(dev); register_done: if (error) { - spin_lock(&device_lock); + up(&device_sem); list_del_init(&dev->g_list); list_del_init(&dev->node); - spin_unlock(&device_lock); + up(&device_sem); } return error; } @@ -203,6 +77,7 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->intf_list); spin_lock_init(&dev->lock); atomic_set(&dev->refcount,1); + dev->state = DEVICE_INITIALIZED; if (dev->parent) get_device(dev->parent); } @@ -234,22 +109,15 @@ int device_register(struct device *dev) return error; } -struct device * get_device_locked(struct device * dev) +struct device * get_device(struct device * dev) { struct device * ret = dev; - if (dev && dev->present && atomic_read(&dev->refcount) > 0) + down(&device_sem); + if (device_present(dev) && atomic_read(&dev->refcount) > 0) atomic_inc(&dev->refcount); else ret = NULL; - return ret; -} - -struct device * get_device(struct device * dev) -{ - struct device * ret; - spin_lock(&device_lock); - ret = get_device_locked(dev); - spin_unlock(&device_lock); + up(&device_sem); return ret; } @@ -259,34 +127,23 @@ struct device * get_device(struct device * dev) */ void put_device(struct device * dev) { - struct device * parent; - if (!atomic_dec_and_lock(&dev->refcount,&device_lock)) + down(&device_sem); + if (!atomic_dec_and_test(&dev->refcount)) { + up(&device_sem); return; - parent = dev->parent; - dev->parent = NULL; - spin_unlock(&device_lock); + } + list_del_init(&dev->node); + list_del_init(&dev->g_list); + up(&device_sem); - BUG_ON(dev->present); + BUG_ON((dev->state != DEVICE_GONE)); - if (dev->release) - dev->release(dev); - - if (parent) - put_device(parent); + device_del(dev); } void device_del(struct device * dev) { - spin_lock(&device_lock); - dev->present = 0; - list_del_init(&dev->node); - list_del_init(&dev->g_list); - list_del_init(&dev->bus_list); - list_del_init(&dev->driver_list); - spin_unlock(&device_lock); - - pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n", - dev->bus_id,dev->name); + struct device * parent = dev->parent; /* Notify the platform of the removal, in case they * need to do anything... @@ -297,11 +154,16 @@ void device_del(struct device * dev) /* notify userspace that this device is about to disappear */ dev_hotplug (dev, "remove"); - device_detach(dev); bus_remove_device(dev); /* remove the driverfs directory */ device_remove_dir(dev); + + if (dev->release) + dev->release(dev); + + if (parent) + put_device(parent); } /** @@ -315,22 +177,15 @@ void device_del(struct device * dev) */ void device_unregister(struct device * dev) { - device_del(dev); - put_device(dev); -} - -static int __init device_init(void) -{ - int error; + down(&device_sem); + dev->state = DEVICE_GONE; + up(&device_sem); - error = init_driverfs_fs(); - if (error) - panic("DEV: could not initialize driverfs"); - return 0; + pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n", + dev->bus_id,dev->name); + put_device(dev); } -core_initcall(device_init); - EXPORT_SYMBOL(device_register); EXPORT_SYMBOL(device_unregister); EXPORT_SYMBOL(get_device); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 221e525736bd..4bf4a005b918 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -16,29 +16,61 @@ int driver_for_each_dev(struct device_driver * drv, void * data, int (*callback)(struct device *, void * )) { struct list_head * node; - struct device * prev = NULL; int error = 0; - get_driver(drv); - spin_lock(&device_lock); - list_for_each(node,&drv->devices) { - struct device * dev = get_device_locked(to_dev(node)); - if (dev) { - spin_unlock(&device_lock); - error = callback(dev,data); - if (prev) - put_device(prev); - prev = dev; - spin_lock(&device_lock); - if (error) - break; + drv = get_driver(drv); + if (drv) { + down_read(&drv->bus->rwsem); + list_for_each(node,&drv->devices) { + struct device * dev = get_device(to_dev(node)); + if (dev) { + error = callback(dev,data); + put_device(dev); + if (error) + break; + } } + up_read(&drv->bus->rwsem); + put_driver(drv); } - spin_unlock(&device_lock); - put_driver(drv); return error; } +struct device_driver * get_driver(struct device_driver * drv) +{ + struct device_driver * ret = drv; + spin_lock(&device_lock); + if (drv && drv->present && atomic_read(&drv->refcount) > 0) + atomic_inc(&drv->refcount); + else + ret = NULL; + spin_unlock(&device_lock); + return ret; +} + + +void remove_driver(struct device_driver * drv) +{ + BUG(); +} + +/** + * put_driver - decrement driver's refcount and clean up if necessary + * @drv: driver in question + */ +void put_driver(struct device_driver * drv) +{ + struct bus_type * bus = drv->bus; + if (!atomic_dec_and_lock(&drv->refcount,&device_lock)) + return; + spin_unlock(&device_lock); + BUG_ON(drv->present); + bus_remove_driver(drv); + if (drv->release) + drv->release(drv); + put_bus(bus); +} + /** * driver_register - register driver with bus * @drv: driver to register @@ -50,54 +82,29 @@ 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); + pr_debug("driver %s:%s: registering\n",drv->bus->name,drv->name); get_bus(drv->bus); atomic_set(&drv->refcount,2); rwlock_init(&drv->lock); INIT_LIST_HEAD(&drv->devices); - spin_lock(&device_lock); - list_add(&drv->bus_list,&drv->bus->drivers); - spin_unlock(&device_lock); - driver_make_dir(drv); - driver_attach(drv); + drv->present = 1; + bus_add_driver(drv); put_driver(drv); return 0; } -static void __remove_driver(struct device_driver * drv) -{ - pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name); - driver_detach(drv); - driver_remove_dir(drv); - if (drv->release) - drv->release(drv); - put_bus(drv->bus); -} - -void remove_driver(struct device_driver * drv) +void driver_unregister(struct device_driver * drv) { spin_lock(&device_lock); - atomic_set(&drv->refcount,0); - list_del_init(&drv->bus_list); + drv->present = 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; - list_del_init(&drv->bus_list); - spin_unlock(&device_lock); - __remove_driver(drv); + pr_debug("driver %s:%s: unregistering\n",drv->bus->name,drv->name); + put_driver(drv); } EXPORT_SYMBOL(driver_for_each_dev); EXPORT_SYMBOL(driver_register); +EXPORT_SYMBOL(driver_unregister); +EXPORT_SYMBOL(get_driver); EXPORT_SYMBOL(put_driver); -EXPORT_SYMBOL(remove_driver); diff --git a/drivers/base/interface.c b/drivers/base/interface.c index a585b729c5c4..8a5c6e6bc226 100644 --- a/drivers/base/interface.c +++ b/drivers/base/interface.c @@ -19,7 +19,7 @@ static DEVICE_ATTR(name,S_IRUGO,device_read_name,NULL); static ssize_t device_read_power(struct device * dev, char * page, size_t count, loff_t off) { - return off ? 0 : sprintf(page,"%d\n",dev->current_state); + return off ? 0 : sprintf(page,"%d\n",dev->power_state); } static ssize_t diff --git a/drivers/base/power.c b/drivers/base/power.c index c98984dfd8ab..176be19b72d1 100644 --- a/drivers/base/power.c +++ b/drivers/base/power.c @@ -8,6 +8,8 @@ * */ +#define DEBUG 0 + #include <linux/device.h> #include <linux/module.h> #include "base.h" @@ -28,34 +30,21 @@ int device_suspend(u32 state, u32 level) { struct list_head * node; - struct device * prev = NULL; int error = 0; - if(level == SUSPEND_POWER_DOWN) - printk(KERN_EMERG "Shutting down devices\n"); - else - printk(KERN_EMERG "Suspending devices\n"); + printk(KERN_EMERG "Suspending devices\n"); - spin_lock(&device_lock); + down(&device_sem); list_for_each(node,&global_device_list) { - struct device * dev = get_device_locked(to_dev(node)); - if (dev) { - spin_unlock(&device_lock); - if(dev->driver) { - if(level == SUSPEND_POWER_DOWN) { - if(dev->driver->remove) - dev->driver->remove(dev); - } else if(dev->driver->suspend) - error = dev->driver->suspend(dev,state,level); - } - if (prev) - put_device(prev); - prev = dev; - spin_lock(&device_lock); + struct device * dev = to_dev(node); + if (device_present(dev) && dev->driver && dev->driver->suspend) { + pr_debug("suspending device %s\n",dev->name); + error = dev->driver->suspend(dev,state,level); + if (error) + printk(KERN_ERR "%s: suspend returned %d\n",dev->name,error); } } - spin_unlock(&device_lock); - + up(&device_sem); return error; } @@ -70,33 +59,38 @@ int device_suspend(u32 state, u32 level) void device_resume(u32 level) { struct list_head * node; - struct device * prev = NULL; - spin_lock(&device_lock); + down(&device_sem); list_for_each_prev(node,&global_device_list) { - struct device * dev = get_device_locked(to_dev(node)); - if (dev) { - spin_unlock(&device_lock); - if (dev->driver && dev->driver->resume) - dev->driver->resume(dev,level); - if (prev) - put_device(prev); - prev = dev; - spin_lock(&device_lock); + struct device * dev = to_dev(node); + if (device_present(dev) && dev->driver && dev->driver->resume) { + pr_debug("resuming device %s\n",dev->name); + dev->driver->resume(dev,level); } } - spin_unlock(&device_lock); + up(&device_sem); printk(KERN_EMERG "Devices Resumed\n"); } /** - * device_shutdown - call device_suspend with status set to shutdown, to - * cause all devices to remove themselves cleanly + * device_shutdown - call ->remove() on each device to shutdown. */ void device_shutdown(void) { - device_suspend(4, SUSPEND_POWER_DOWN); + struct list_head * entry; + + printk(KERN_EMERG "Shutting down devices\n"); + + down(&device_sem); + list_for_each(entry,&global_device_list) { + struct device * dev = to_dev(entry); + if (device_present(dev) && dev->driver && dev->driver->shutdown) { + pr_debug("shutting down %s\n",dev->name); + dev->driver->shutdown(dev); + } + } + up(&device_sem); } EXPORT_SYMBOL(device_suspend); diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 2f8e10cf1ae8..cd681d1190bd 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -10,6 +10,8 @@ * add themselves as children of the system bus. */ +#define DEBUG 1 + #include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> @@ -76,8 +78,8 @@ int sys_register_root(struct sys_root * root) */ void sys_unegister_root(struct sys_root * root) { - put_device(&root->sysdev); - put_device(&root->dev); + device_unregister(&root->sysdev); + device_unregister(&root->dev); } /** diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c index 502d3c90b864..01dc3c0681f2 100644 --- a/drivers/input/serio/sa1111ps2.c +++ b/drivers/input/serio/sa1111ps2.c @@ -354,7 +354,7 @@ static int __init ps2_init(void) static void __exit ps2_exit(void) { - remove_driver(&ps2_driver.drv); + driver_unregister(&ps2_driver.drv); } module_init(ps2_init); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 040cc588981b..3ef7d9eb6e84 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -141,7 +141,7 @@ pci_register_driver(struct pci_driver *drv) void pci_unregister_driver(struct pci_driver *drv) { - remove_driver(&drv->driver); + driver_unregister(&drv->driver); } static struct pci_driver pci_compat_driver = { diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c index 9bf1a6c392d4..7afea0c7b3aa 100644 --- a/drivers/pcmcia/sa1111_generic.c +++ b/drivers/pcmcia/sa1111_generic.c @@ -297,7 +297,7 @@ static int __init sa1111_drv_pcmcia_init(void) static void __exit sa1111_drv_pcmcia_exit(void) { - remove_driver(&pcmcia_driver.drv); + driver_unregister(&pcmcia_driver.drv); } module_init(sa1111_drv_pcmcia_init); diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index c8ad7724307c..d32b1a65d6ee 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -861,8 +861,7 @@ static int scsi_debug_release(struct Scsi_Host * hpnt) if (++num_releases == num_present) { #ifdef DRIVERFS_SUPPORT do_remove_driverfs_files(); - remove_driver(&sdebug_driverfs_driver); - // driver_unregister(&sdebug_driverfs_driver); + driver_unregister(&sdebug_driverfs_driver); #endif vfree(fake_storep); vfree(devInfop); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 32300bedc779..02121a6fd6d5 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1492,7 +1492,7 @@ static void __exit exit_sd(void) vfree(sd_dsk_arr); } sd_template.dev_max = 0; - remove_driver(&sd_template.scsi_driverfs_driver); + driver_unregister(&sd_template.scsi_driverfs_driver); unregister_reboot_notifier(&sd_notifier_block); } diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 53acfd9194d5..4e7682ec0565 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1655,7 +1655,7 @@ exit_sg(void) sg_dev_arr = NULL; } sg_template.dev_max = 0; - remove_driver(&sg_template.scsi_driverfs_driver); + driver_unregister(&sg_template.scsi_driverfs_driver); } static int diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 5c0a4bd0a9df..14c54d55c764 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -831,7 +831,7 @@ static void __exit exit_sr(void) kfree(scsi_CDs); sr_template.dev_max = 0; - remove_driver(&sr_template.scsi_driverfs_driver); + driver_unregister(&sr_template.scsi_driverfs_driver); } module_init(init_sr); diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 2a8161b18c04..43c05cb65e7b 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3992,7 +3992,7 @@ static void __exit exit_st(void) kfree(scsi_tapes); } st_template.dev_max = 0; - remove_driver(&st_template.scsi_driverfs_driver); + driver_unregister(&st_template.scsi_driverfs_driver); printk(KERN_INFO "st: Unloaded.\n"); } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 556dbacb7c23..e31a270ce931 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -208,7 +208,7 @@ void usb_deregister(struct usb_driver *driver) { info("deregistering driver %s", driver->name); - remove_driver (&driver->driver); + driver_unregister (&driver->driver); usbfs_update_special(); } @@ -1394,7 +1394,7 @@ static void __exit usb_exit(void) if (nousb) return; - remove_driver(&usb_generic_driver); + driver_unregister(&usb_generic_driver); usb_major_cleanup(); usbfs_cleanup(); usb_hub_cleanup(); diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index 98996cd62d6e..e5b729e5ad39 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -413,7 +413,7 @@ static int __init ohci_hcd_sa1111_init (void) static void __exit ohci_hcd_sa1111_cleanup (void) { - remove_driver(&ohci_hcd_sa1111_driver.drv); + driver_unregister(&ohci_hcd_sa1111_driver.drv); } module_init (ohci_hcd_sa1111_init); diff --git a/fs/driverfs/inode.c b/fs/driverfs/inode.c index 0f35e27e669c..b4b5aa9f6a55 100644 --- a/fs/driverfs/inode.c +++ b/fs/driverfs/inode.c @@ -509,11 +509,13 @@ static void put_mount(void) DBG("driverfs: mount_count = %d\n",mount_count); } -int __init init_driverfs_fs(void) +static int __init driverfs_init(void) { return register_filesystem(&driverfs_fs_type); } +core_initcall(driverfs_init); + static struct dentry * get_dentry(struct dentry * parent, const char * name) { struct qstr qstr; diff --git a/include/linux/device.h b/include/linux/device.h index 80a63939f924..3dd9a4081298 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -48,14 +48,22 @@ enum { RESUME_ENABLE, }; +enum device_state { + DEVICE_UNINITIALIZED = 0, + DEVICE_INITIALIZED = 1, + DEVICE_REGISTERED = 2, + DEVICE_GONE = 3, +}; + struct device; struct device_driver; struct device_class; struct bus_type { char * name; - rwlock_t lock; + struct rw_semaphore rwsem; atomic_t refcount; + u32 present; struct list_head node; struct list_head devices; @@ -73,14 +81,9 @@ struct bus_type { extern int bus_register(struct bus_type * bus); +extern void bus_unregister(struct bus_type * bus); -static inline struct bus_type * get_bus(struct bus_type * bus) -{ - BUG_ON(!atomic_read(&bus->refcount)); - atomic_inc(&bus->refcount); - return bus; -} - +extern struct bus_type * get_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus); extern int bus_for_each_dev(struct bus_type * bus, void * data, @@ -114,6 +117,7 @@ struct device_driver { rwlock_t lock; atomic_t refcount; + u32 present; struct list_head bus_list; struct list_head class_list; @@ -123,7 +127,7 @@ struct device_driver { int (*probe) (struct device * dev); int (*remove) (struct device * dev); - + void (*shutdown) (struct device * dev); int (*suspend) (struct device * dev, u32 state, u32 level); int (*resume) (struct device * dev, u32 level); @@ -131,16 +135,10 @@ struct device_driver { }; - extern int driver_register(struct device_driver * drv); +extern void driver_unregister(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 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); @@ -172,6 +170,11 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute * */ struct device_class { char * name; + struct rw_semaphore rwsem; + + atomic_t refcount; + u32 present; + u32 devnum; struct list_head node; @@ -189,6 +192,9 @@ struct device_class { extern int devclass_register(struct device_class *); extern void devclass_unregister(struct device_class *); +extern struct device_class * get_devclass(struct device_class *); +extern void put_devclass(struct device_class *); + struct devclass_attribute { struct attribute attr; @@ -289,8 +295,8 @@ struct device { void *platform_data; /* Platform specific data (e.g. ACPI, BIOS data relevant to device) */ - u32 present; - u32 current_state; /* Current operating state. In + enum device_state state; + u32 power_state; /* Current operating state. In ACPI-speak, this is D0-D3, D0 being fully functional, and D3 being off. */ @@ -362,6 +368,11 @@ extern int (*platform_notify)(struct device * dev); extern int (*platform_notify_remove)(struct device * dev); +static inline int device_present(struct device * dev) +{ + return (dev && (dev->state == DEVICE_INITIALIZED || dev->state == DEVICE_REGISTERED)); +} + /* device and bus locking helpers. * * FIXME: Is there anything else we need to do? diff --git a/include/linux/driverfs_fs.h b/include/linux/driverfs_fs.h index d859f8c2e041..b4270e947a1e 100644 --- a/include/linux/driverfs_fs.h +++ b/include/linux/driverfs_fs.h @@ -65,6 +65,4 @@ driverfs_create_symlink(struct driver_dir_entry * parent, extern void driverfs_remove_file(struct driver_dir_entry *, const char * name); -extern int init_driverfs_fs(void); - #endif /* _DDFS_H_ */ |
