* device_delete_child() now works differently from FreeBSD: in FreeBSD you can't just

call this function if you don't know if a device has a direct reference to one of
  its children. Since we want to be able to delete the root without having any knowledge
  about its children, we now detach everything first, and then delete the entries.
  This fixes a possible crashing bug when a preloaded driver (used for network boot)
  tried to acquire its resources again at a later point.
* device_detach() and device_attach() now check if the device has an attach or detach
  method at all.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23036 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2007-12-01 11:09:58 +00:00
parent 3bd7e77cfa
commit 1f89a3766a

View File

@ -314,27 +314,40 @@ device_add_child(device_t parent, const char *name, int unit)
int int
device_delete_child(device_t parent, device_t child) device_delete_child(device_t parent, device_t child)
{ {
int status;
if (child == NULL) if (child == NULL)
return 0; return 0;
if (parent != NULL) if (parent != NULL)
list_remove_item(&parent->children, child); list_remove_item(&parent->children, child);
// We differentiate from the FreeBSD logic here - it will first delete
// the children, and will then detach the device.
// This has the problem that you cannot safely call device_delete_child()
// as you don't know if one of the children deletes its own children this
// way when it is detached.
// Therefore, we'll detach first, and then delete whatever is left.
parent = child; parent = child;
child = NULL;
while ((child = list_remove_head_item(&parent->children)) != NULL) { // detach children
device_delete_child(NULL, child); while ((child = list_get_next_item(&parent->children, child)) != NULL) {
device_detach(child);
} }
if ((atomic_and(&parent->flags, ~DEVICE_ATTACHED) & DEVICE_ATTACHED) != 0 // detach device
&& parent->methods.detach != NULL) { status = device_detach(parent);
int status = parent->methods.detach(parent); if (status != 0)
if (status != 0) { return status;
atomic_or(&parent->flags, DEVICE_ATTACHED);
return status; // delete children
} while ((child = list_get_first_item(&parent->children)) != NULL) {
device_delete_child(parent, child);
} }
// delete device
if (parent->flags & DEVICE_DESC_ALLOCED) if (parent->flags & DEVICE_DESC_ALLOCED)
free((char *)parent->description); free((char *)parent->description);
@ -356,7 +369,8 @@ device_attach(device_t device)
{ {
int result; int result;
if (device->driver == NULL) if (device->driver == NULL
|| device->methods.attach == NULL)
return B_ERROR; return B_ERROR;
result = device->methods.attach(device); result = device->methods.attach(device);
@ -373,7 +387,8 @@ device_detach(device_t device)
if (device->driver == NULL) if (device->driver == NULL)
return B_ERROR; return B_ERROR;
if ((atomic_and(&device->flags, ~DEVICE_ATTACHED) & DEVICE_ATTACHED) != 0) { if ((atomic_and(&device->flags, ~DEVICE_ATTACHED) & DEVICE_ATTACHED) != 0
&& device->methods.detach != NULL) {
int result = device->methods.detach(device); int result = device->methods.detach(device);
if (result != 0) { if (result != 0) {
atomic_or(&device->flags, DEVICE_ATTACHED); atomic_or(&device->flags, DEVICE_ATTACHED);