From 473c71058e2bfdbe091652b9a82572aa621bfffa Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 15 Oct 2002 20:32:25 -0700 Subject: driver model: make driverfs an implicit initcall. --- include/linux/driverfs_fs.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') 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_ */ -- cgit v1.2.3 From 4278bccc2d9707c9c4ee1769b464bffe4dfd820a Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 15 Oct 2002 20:54:20 -0700 Subject: driver model: change bus refcounting scheme to match devices'. Based on recent changes to device refcounting/unregistration. struct bus_type gets a ->present field, which is set when the bus is registered. bus_unregister() is added, which only clears ->present and calls put_bus(). get_bus() checks the ->present and returns NULL if clear. This allows all current references to buses to keep valid structures, while preventing any new references to the bus structure. --- drivers/base/bus.c | 36 +++++++++++++++++++++++++++++++----- include/linux/device.h | 10 +++------- 2 files changed, 34 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 7daffbfd9913..1343c64c5a38 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -136,12 +136,35 @@ void bus_remove_device(struct device * dev) } } +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_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 +179,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 +194,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/include/linux/device.h b/include/linux/device.h index 80a63939f924..03c7d995e96c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -56,6 +56,7 @@ struct bus_type { char * name; rwlock_t lock; atomic_t refcount; + u32 present; struct list_head node; struct list_head devices; @@ -73,14 +74,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, -- cgit v1.2.3 From 2884fae053fb6a58f4f7c5985d574532b7ddd3d2 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 15 Oct 2002 21:11:26 -0700 Subject: driver model: make driver refcounting similar to devices'. In the spirit of devices and buses, change driver refcounting model to match the way that devices and buses are done. struct device_driver gets a ->present field, which is set on registration and cleared in driver_unregister(). get_device() checks the state of this flag and returns NULL if cleared. Note that the horribly wrong remove_driver() is deprecated and simply BUG()s when called. Please convert callers to use driver_unregister(). Updates to callers will be coming soon. Note also that this still doesn't fix the race in which a driver module can be removed while the refcount on a driver > 1. Near future work should help to remedy it, but no solutions are guaranteed.. --- drivers/base/driver.c | 74 ++++++++++++++++++++++++++++++-------------------- include/linux/device.h | 11 ++------ 2 files changed, 48 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 221e525736bd..b19b06201dd5 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -39,6 +39,43 @@ int driver_for_each_dev(struct device_driver * drv, void * data, 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; + list_del_init(&drv->bus_list); + spin_unlock(&device_lock); + BUG_ON(drv->present); + driver_detach(drv); + driver_remove_dir(drv); + if (drv->release) + drv->release(drv); + put_bus(bus); +} + /** * driver_register - register driver with bus * @drv: driver to register @@ -50,12 +87,13 @@ 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); + drv->present = 1; spin_lock(&device_lock); list_add(&drv->bus_list,&drv->bus->drivers); spin_unlock(&device_lock); @@ -65,39 +103,17 @@ int driver_register(struct device_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); - 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); + drv->present = 0; 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/include/linux/device.h b/include/linux/device.h index 03c7d995e96c..4b6b0cbe77ed 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -110,6 +110,7 @@ struct device_driver { rwlock_t lock; atomic_t refcount; + u32 present; struct list_head bus_list; struct list_head class_list; @@ -127,16 +128,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); -- cgit v1.2.3 From 1067efac8c64c2608ececdca87e1329ba3ffa073 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 15 Oct 2002 21:26:48 -0700 Subject: driver model: change class reference counting to be like devices'. device classes join the club of devices, buses, and drivers. They get a ->present flag, which is set on registration and cleared on unregistration. They also get get_devclass() and put_devclass(), which, you guessed it, bump the reference count. get_...() of course checks the present flag and returns NULL if clear. --- drivers/base/class.c | 36 +++++++++++++++++++++++++++++++----- include/linux/device.h | 6 ++++++ 2 files changed, 37 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index dfef9793871a..3c7024cc3efb 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -81,29 +81,55 @@ void devclass_remove_device(struct device * dev) } } +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); + 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/include/linux/device.h b/include/linux/device.h index 4b6b0cbe77ed..112fca2dd2d8 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -163,6 +163,9 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute * */ struct device_class { char * name; + atomic_t refcount; + u32 present; + u32 devnum; struct list_head node; @@ -180,6 +183,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; -- cgit v1.2.3 From ba809e8ac59030f8b8c1822f0ce6b3161cabc60b Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Wed, 16 Oct 2002 01:28:28 -0700 Subject: driver model: replace rwlock in struct bus_type with a rwsem. Synchronize all walks of the device and driver lists of a bus with an rwsem wrapped around the entire iterator, instead of using device_lock and dropping it after we grabbed each node. Note this also prevents deadlock when walking the list of drivers and calling get_driver(), since get_driver() tries to take device_lock while we already have it held. --- drivers/base/bus.c | 24 +++++++++++------------- drivers/base/core.c | 1 - include/linux/device.h | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 1343c64c5a38..559956c29763 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -47,23 +47,21 @@ int bus_for_each_dev(struct bus_type * bus, void * data, int error = 0; get_bus(bus); - spin_lock(&device_lock); + down_write(&bus->rwsem); list_for_each(node,&bus->devices) { - struct device * dev = get_device_locked(to_dev(node)); + struct device * dev = get_device(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; } } - spin_unlock(&device_lock); if (prev) put_device(prev); + up_write(&bus->rwsem); put_bus(bus); return error; } @@ -77,24 +75,21 @@ int bus_for_each_drv(struct bus_type * bus, void * data, /* pin bus in memory */ get_bus(bus); - - spin_lock(&device_lock); + down_write(&bus->rwsem); 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; } } - spin_unlock(&device_lock); if (prev) put_driver(prev); + up_write(&bus->rwsem); put_bus(bus); return error; } @@ -111,11 +106,11 @@ int bus_for_each_drv(struct bus_type * bus, void * data, int bus_add_device(struct device * dev) { if (dev->bus) { + down_write(&dev->bus->rwsem); pr_debug("registering %s with bus '%s'\n",dev->bus_id,dev->bus->name); get_bus(dev->bus); - spin_lock(&device_lock); list_add_tail(&dev->bus_list,&dev->bus->devices); - spin_unlock(&device_lock); + up_write(&dev->bus->rwsem); device_bus_link(dev); } return 0; @@ -131,7 +126,10 @@ int bus_add_device(struct device * dev) void bus_remove_device(struct device * dev) { if (dev->bus) { + down_write(&dev->bus->rwsem); + list_del_init(&dev->bus_list); device_remove_symlink(&dev->bus->device_dir,dev->bus_id); + up_write(&dev->bus->rwsem); put_bus(dev->bus); } } @@ -160,7 +158,7 @@ void put_bus(struct bus_type * 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); diff --git a/drivers/base/core.c b/drivers/base/core.c index b6f2104ae038..a1a41f4b0a07 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -263,7 +263,6 @@ void put_device(struct device * dev) return; 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); diff --git a/include/linux/device.h b/include/linux/device.h index 112fca2dd2d8..c0f8d6c5e4bf 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -54,7 +54,7 @@ struct device_class; struct bus_type { char * name; - rwlock_t lock; + struct rw_semaphore rwsem; atomic_t refcount; u32 present; -- cgit v1.2.3 From d9b24a116f72056c7ac8bb2cdcd9dfd84ce85513 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Wed, 16 Oct 2002 03:12:42 -0700 Subject: driver model: add per-class rwsem and protect list accesses with it. This is similar to struct bus_type's rwsem, though classes aren't doing anything nearly as fancy with it. We just make sure to take it when ever we add or remove any devices or drivers. --- drivers/base/class.c | 80 ++++++++++++++++++++++++++++++++++---------------- include/linux/device.h | 2 ++ 2 files changed, 57 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index 21860a1380f7..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,22 +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; int error = 0; if (dev->driver) { - cls = dev->driver->devclass; + cls = get_devclass(dev->driver->devclass); if (cls) { - pr_debug("adding device '%s' to class '%s'\n", - dev->name,cls->name); + 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; @@ -74,14 +96,21 @@ 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); + } } } @@ -111,6 +140,7 @@ int devclass_register(struct device_class * cls) { INIT_LIST_HEAD(&cls->drivers); INIT_LIST_HEAD(&cls->intf_list); + init_rwsem(&cls->rwsem); atomic_set(&cls->refcount,2); cls->present = 1; pr_debug("device class '%s': registering\n",cls->name); diff --git a/include/linux/device.h b/include/linux/device.h index c0f8d6c5e4bf..fdcdf0077027 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -163,6 +163,8 @@ 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; -- cgit v1.2.3 From de9d4fa4fc14669c97a8d0911bb0b5624f94b992 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Wed, 16 Oct 2002 21:03:13 -0700 Subject: driver model: add struct device_driver::shutdown() method and device_present() helper. ->shutdown() is added to be explicitly called during reboot transitions by device_shutdown(). Drivers implementing this method should use it solely to quiesce the device. Drivers do not have to free memory allocated by the driver, as it is implied that the system is rebooting; and, they must not fuss with the reference counts. Devices are guaranteed not to disappear during the call, though their validity may be checked using device_present(). --- include/linux/device.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/device.h b/include/linux/device.h index fdcdf0077027..d34142925760 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -120,7 +120,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); @@ -361,6 +361,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->present == 1); +} + /* device and bus locking helpers. * * FIXME: Is there anything else we need to do? -- cgit v1.2.3 From be0b9c71e35ebee75fc694daa9f2a268b504ca50 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Wed, 16 Oct 2002 22:07:11 -0700 Subject: driver model: change struct device::present to enumerated value with multiple states. Adapted from Greg KH: - add multiple possible enumerated states a device can be in: UNINITIALIZED, INITIALIZED, REGISTERED, and GONE. - Check whether device is INITIALIZED or REGISTERED in device_present(). - Change struct device::current_state to ::power_state to better reflect what it is. --- drivers/base/core.c | 9 +++++---- drivers/base/interface.c | 2 +- include/linux/device.h | 13 ++++++++++--- 3 files changed, 16 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index c7f2fcfd480c..bd82a792d877 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -33,7 +33,7 @@ int device_add(struct device *dev) return -EINVAL; down(&device_sem); - dev->present = 1; + 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); @@ -77,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); } @@ -112,7 +113,7 @@ struct device * get_device(struct device * dev) { struct device * ret = dev; down(&device_sem); - if (dev && dev->present && atomic_read(&dev->refcount) > 0) + if (device_present(dev) && atomic_read(&dev->refcount) > 0) atomic_inc(&dev->refcount); else ret = NULL; @@ -135,7 +136,7 @@ void put_device(struct device * dev) list_del_init(&dev->g_list); up(&device_sem); - BUG_ON(dev->present); + BUG_ON((dev->state != DEVICE_GONE)); device_del(dev); } @@ -177,7 +178,7 @@ void device_del(struct device * dev) void device_unregister(struct device * dev) { down(&device_sem); - dev->present = 0; + dev->state = DEVICE_GONE; up(&device_sem); pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n", 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/include/linux/device.h b/include/linux/device.h index d34142925760..3dd9a4081298 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -48,6 +48,13 @@ 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; @@ -288,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. */ @@ -363,7 +370,7 @@ extern int (*platform_notify_remove)(struct device * dev); static inline int device_present(struct device * dev) { - return (dev && dev->present == 1); + return (dev && (dev->state == DEVICE_INITIALIZED || dev->state == DEVICE_REGISTERED)); } /* device and bus locking helpers. -- cgit v1.2.3