diff options
Diffstat (limited to 'drivers/base/sys.c')
-rw-r--r-- | drivers/base/sys.c | 82 |
1 files changed, 55 insertions, 27 deletions
diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 1667aaf4fde6..acde9b5ee131 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c | |||
@@ -166,8 +166,38 @@ EXPORT_SYMBOL_GPL(sysdev_class_unregister); | |||
166 | 166 | ||
167 | static DEFINE_MUTEX(sysdev_drivers_lock); | 167 | static DEFINE_MUTEX(sysdev_drivers_lock); |
168 | 168 | ||
169 | /* | ||
170 | * @dev != NULL means that we're unwinding because some drv->add() | ||
171 | * failed for some reason. You need to grab sysdev_drivers_lock before | ||
172 | * calling this. | ||
173 | */ | ||
174 | static void __sysdev_driver_remove(struct sysdev_class *cls, | ||
175 | struct sysdev_driver *drv, | ||
176 | struct sys_device *from_dev) | ||
177 | { | ||
178 | struct sys_device *dev = from_dev; | ||
179 | |||
180 | list_del_init(&drv->entry); | ||
181 | if (!cls) | ||
182 | return; | ||
183 | |||
184 | if (!drv->remove) | ||
185 | goto kset_put; | ||
186 | |||
187 | if (dev) | ||
188 | list_for_each_entry_continue_reverse(dev, &cls->kset.list, | ||
189 | kobj.entry) | ||
190 | drv->remove(dev); | ||
191 | else | ||
192 | list_for_each_entry(dev, &cls->kset.list, kobj.entry) | ||
193 | drv->remove(dev); | ||
194 | |||
195 | kset_put: | ||
196 | kset_put(&cls->kset); | ||
197 | } | ||
198 | |||
169 | /** | 199 | /** |
170 | * sysdev_driver_register - Register auxillary driver | 200 | * sysdev_driver_register - Register auxiliary driver |
171 | * @cls: Device class driver belongs to. | 201 | * @cls: Device class driver belongs to. |
172 | * @drv: Driver. | 202 | * @drv: Driver. |
173 | * | 203 | * |
@@ -175,14 +205,14 @@ static DEFINE_MUTEX(sysdev_drivers_lock); | |||
175 | * called on each operation on devices of that class. The refcount | 205 | * called on each operation on devices of that class. The refcount |
176 | * of @cls is incremented. | 206 | * of @cls is incremented. |
177 | */ | 207 | */ |
178 | |||
179 | int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) | 208 | int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) |
180 | { | 209 | { |
210 | struct sys_device *dev = NULL; | ||
181 | int err = 0; | 211 | int err = 0; |
182 | 212 | ||
183 | if (!cls) { | 213 | if (!cls) { |
184 | WARN(1, KERN_WARNING "sysdev: invalid class passed to " | 214 | WARN(1, KERN_WARNING "sysdev: invalid class passed to %s!\n", |
185 | "sysdev_driver_register!\n"); | 215 | __func__); |
186 | return -EINVAL; | 216 | return -EINVAL; |
187 | } | 217 | } |
188 | 218 | ||
@@ -198,21 +228,29 @@ int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) | |||
198 | 228 | ||
199 | /* If devices of this class already exist, tell the driver */ | 229 | /* If devices of this class already exist, tell the driver */ |
200 | if (drv->add) { | 230 | if (drv->add) { |
201 | struct sys_device *dev; | 231 | list_for_each_entry(dev, &cls->kset.list, kobj.entry) { |
202 | list_for_each_entry(dev, &cls->kset.list, kobj.entry) | 232 | err = drv->add(dev); |
203 | drv->add(dev); | 233 | if (err) |
234 | goto unwind; | ||
235 | } | ||
204 | } | 236 | } |
205 | } else { | 237 | } else { |
206 | err = -EINVAL; | 238 | err = -EINVAL; |
207 | WARN(1, KERN_ERR "%s: invalid device class\n", __func__); | 239 | WARN(1, KERN_ERR "%s: invalid device class\n", __func__); |
208 | } | 240 | } |
241 | |||
242 | goto unlock; | ||
243 | |||
244 | unwind: | ||
245 | __sysdev_driver_remove(cls, drv, dev); | ||
246 | |||
247 | unlock: | ||
209 | mutex_unlock(&sysdev_drivers_lock); | 248 | mutex_unlock(&sysdev_drivers_lock); |
210 | return err; | 249 | return err; |
211 | } | 250 | } |
212 | 251 | ||
213 | |||
214 | /** | 252 | /** |
215 | * sysdev_driver_unregister - Remove an auxillary driver. | 253 | * sysdev_driver_unregister - Remove an auxiliary driver. |
216 | * @cls: Class driver belongs to. | 254 | * @cls: Class driver belongs to. |
217 | * @drv: Driver. | 255 | * @drv: Driver. |
218 | */ | 256 | */ |
@@ -220,23 +258,12 @@ void sysdev_driver_unregister(struct sysdev_class *cls, | |||
220 | struct sysdev_driver *drv) | 258 | struct sysdev_driver *drv) |
221 | { | 259 | { |
222 | mutex_lock(&sysdev_drivers_lock); | 260 | mutex_lock(&sysdev_drivers_lock); |
223 | list_del_init(&drv->entry); | 261 | __sysdev_driver_remove(cls, drv, NULL); |
224 | if (cls) { | ||
225 | if (drv->remove) { | ||
226 | struct sys_device *dev; | ||
227 | list_for_each_entry(dev, &cls->kset.list, kobj.entry) | ||
228 | drv->remove(dev); | ||
229 | } | ||
230 | kset_put(&cls->kset); | ||
231 | } | ||
232 | mutex_unlock(&sysdev_drivers_lock); | 262 | mutex_unlock(&sysdev_drivers_lock); |
233 | } | 263 | } |
234 | |||
235 | EXPORT_SYMBOL_GPL(sysdev_driver_register); | 264 | EXPORT_SYMBOL_GPL(sysdev_driver_register); |
236 | EXPORT_SYMBOL_GPL(sysdev_driver_unregister); | 265 | EXPORT_SYMBOL_GPL(sysdev_driver_unregister); |
237 | 266 | ||
238 | |||
239 | |||
240 | /** | 267 | /** |
241 | * sysdev_register - add a system device to the tree | 268 | * sysdev_register - add a system device to the tree |
242 | * @sysdev: device in question | 269 | * @sysdev: device in question |
@@ -275,7 +302,7 @@ int sysdev_register(struct sys_device *sysdev) | |||
275 | * code that should have called us. | 302 | * code that should have called us. |
276 | */ | 303 | */ |
277 | 304 | ||
278 | /* Notify class auxillary drivers */ | 305 | /* Notify class auxiliary drivers */ |
279 | list_for_each_entry(drv, &cls->drivers, entry) { | 306 | list_for_each_entry(drv, &cls->drivers, entry) { |
280 | if (drv->add) | 307 | if (drv->add) |
281 | drv->add(sysdev); | 308 | drv->add(sysdev); |
@@ -302,13 +329,13 @@ void sysdev_unregister(struct sys_device *sysdev) | |||
302 | } | 329 | } |
303 | 330 | ||
304 | 331 | ||
305 | 332 | #ifndef CONFIG_ARCH_NO_SYSDEV_OPS | |
306 | /** | 333 | /** |
307 | * sysdev_shutdown - Shut down all system devices. | 334 | * sysdev_shutdown - Shut down all system devices. |
308 | * | 335 | * |
309 | * Loop over each class of system devices, and the devices in each | 336 | * Loop over each class of system devices, and the devices in each |
310 | * of those classes. For each device, we call the shutdown method for | 337 | * of those classes. For each device, we call the shutdown method for |
311 | * each driver registered for the device - the auxillaries, | 338 | * each driver registered for the device - the auxiliaries, |
312 | * and the class driver. | 339 | * and the class driver. |
313 | * | 340 | * |
314 | * Note: The list is iterated in reverse order, so that we shut down | 341 | * Note: The list is iterated in reverse order, so that we shut down |
@@ -333,7 +360,7 @@ void sysdev_shutdown(void) | |||
333 | struct sysdev_driver *drv; | 360 | struct sysdev_driver *drv; |
334 | pr_debug(" %s\n", kobject_name(&sysdev->kobj)); | 361 | pr_debug(" %s\n", kobject_name(&sysdev->kobj)); |
335 | 362 | ||
336 | /* Call auxillary drivers first */ | 363 | /* Call auxiliary drivers first */ |
337 | list_for_each_entry(drv, &cls->drivers, entry) { | 364 | list_for_each_entry(drv, &cls->drivers, entry) { |
338 | if (drv->shutdown) | 365 | if (drv->shutdown) |
339 | drv->shutdown(sysdev); | 366 | drv->shutdown(sysdev); |
@@ -358,7 +385,7 @@ static void __sysdev_resume(struct sys_device *dev) | |||
358 | WARN_ONCE(!irqs_disabled(), | 385 | WARN_ONCE(!irqs_disabled(), |
359 | "Interrupts enabled after %pF\n", cls->resume); | 386 | "Interrupts enabled after %pF\n", cls->resume); |
360 | 387 | ||
361 | /* Call auxillary drivers next. */ | 388 | /* Call auxiliary drivers next. */ |
362 | list_for_each_entry(drv, &cls->drivers, entry) { | 389 | list_for_each_entry(drv, &cls->drivers, entry) { |
363 | if (drv->resume) | 390 | if (drv->resume) |
364 | drv->resume(dev); | 391 | drv->resume(dev); |
@@ -405,7 +432,7 @@ int sysdev_suspend(pm_message_t state) | |||
405 | list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { | 432 | list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { |
406 | pr_debug(" %s\n", kobject_name(&sysdev->kobj)); | 433 | pr_debug(" %s\n", kobject_name(&sysdev->kobj)); |
407 | 434 | ||
408 | /* Call auxillary drivers first */ | 435 | /* Call auxiliary drivers first */ |
409 | list_for_each_entry(drv, &cls->drivers, entry) { | 436 | list_for_each_entry(drv, &cls->drivers, entry) { |
410 | if (drv->suspend) { | 437 | if (drv->suspend) { |
411 | ret = drv->suspend(sysdev, state); | 438 | ret = drv->suspend(sysdev, state); |
@@ -497,6 +524,7 @@ int sysdev_resume(void) | |||
497 | return 0; | 524 | return 0; |
498 | } | 525 | } |
499 | EXPORT_SYMBOL_GPL(sysdev_resume); | 526 | EXPORT_SYMBOL_GPL(sysdev_resume); |
527 | #endif /* CONFIG_ARCH_NO_SYSDEV_OPS */ | ||
500 | 528 | ||
501 | int __init system_bus_init(void) | 529 | int __init system_bus_init(void) |
502 | { | 530 | { |