⛏️ index : haiku.git

/*
 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
 * Distributed under the terms of the MIT License.
 */


#include "device_manager.h"

#include <new>
#include <set>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <KernelExport.h>
#include <Locker.h>
#include <module.h>
#include <PCI.h>

#include <fs/KPath.h>
#include <util/AutoLock.h>
#include <util/DoublyLinkedList.h>
#include <util/Stack.h>

#include "bus.h"

#define TRACE(a) dprintf a

#define DEVICE_MANAGER_ROOT_NAME "system/devices_root/driver_v1"

extern struct device_module_info gDeviceModuleInfo;
extern struct driver_module_info gDriverModuleInfo;
extern struct device_module_info gGenericVideoDeviceModuleInfo;
extern struct driver_module_info gGenericVideoDriverModuleInfo;
extern struct device_module_info gSpecificVideoDeviceModuleInfo;
extern struct driver_module_info gSpecificVideoDriverModuleInfo;
extern struct driver_module_info gBusModuleInfo;
extern struct driver_module_info gBusDriverModuleInfo;

extern "C" status_t _add_builtin_module(module_info *info);
extern "C" status_t _get_builtin_dependencies(void);
extern bool gDebugOutputEnabled;
	// from libkernelland_emu.so

struct device_attr_private : device_attr,
		DoublyLinkedListLinkImpl<device_attr_private> {
						device_attr_private();
						device_attr_private(const device_attr& attr);
						~device_attr_private();

			status_t	InitCheck();
			status_t	CopyFrom(const device_attr& attr);

	static	int			Compare(const device_attr* attrA,
							const device_attr *attrB);

private:
			void		_Unset();
};

typedef DoublyLinkedList<device_attr_private> AttributeList;

// I/O resource
typedef struct io_resource_info {
	struct io_resource_info *prev, *next;
	device_node*		owner;			// associated node; NULL for temporary allocation
	io_resource			resource;		// info about actual resource
} io_resource_info;

class Device : public DoublyLinkedListLinkImpl<Device> {
public:
							Device(device_node* node, const char* path,
								const char* moduleName);
							~Device();

			status_t		InitCheck() const;

			device_node*	Node() const { return fNode; }
			const char*		Path() const { return fPath; }
			const char*		ModuleName() const { return fModuleName; }

			status_t		InitDevice();
			void			UninitDevice();

			device_module_info* DeviceModule() const { return fDeviceModule; }
			void*			DeviceData() const { return fDeviceData; }

private:
	device_node*			fNode;
	const char*				fPath;
	const char*				fModuleName;

	int32					fInitialized;
	device_module_info*		fDeviceModule;
	void*					fDeviceData;
};

typedef DoublyLinkedList<Device> DeviceList;
typedef DoublyLinkedList<device_node> NodeList;

struct device_node : DoublyLinkedListLinkImpl<device_node> {
							device_node(const char* moduleName,
								const device_attr* attrs,
								const io_resource* resources);
							~device_node();

			status_t		InitCheck() const;

			const char*		ModuleName() const { return fModuleName; }
			device_node*	Parent() const { return fParent; }
			AttributeList&	Attributes() { return fAttributes; }
			const AttributeList& Attributes() const { return fAttributes; }

			status_t		InitDriver();
			bool			UninitDriver();
			void			UninitUnusedDriver();

			// The following two are only valid, if the node's driver is
			// initialized
			driver_module_info* DriverModule() const { return fDriver; }
			void*			DriverData() const { return fDriverData; }

			void			AddChild(device_node *node);
			void			RemoveChild(device_node *node);
			const NodeList&	Children() const { return fChildren; }
			void			DeviceRemoved();

			status_t		Register(device_node* parent);
			status_t		Probe(const char* devicePath, uint32 updateCycle);
			bool			IsRegistered() const { return fRegistered; }
			bool			IsInitialized() const { return fInitialized > 0; }
			uint32			Flags() const { return fFlags; }

			void			Acquire();
			bool			Release();

			const DeviceList& Devices() const { return fDevices; }
			void			AddDevice(Device* device);
			void			RemoveDevice(Device* device);

			int				CompareTo(const device_attr* attributes) const;
			device_node*	FindChild(const device_attr* attributes) const;
			device_node*	FindChild(const char* moduleName) const;

			void			Dump(int32 level = 0);

private:
			status_t		_RegisterFixed(uint32& registered);
			bool			_AlwaysRegisterDynamic();
			status_t		_AddPath(Stack<KPath*>& stack, const char* path,
								const char* subPath = NULL);
			status_t		_GetNextDriverPath(void*& cookie, KPath& _path);
			status_t		_GetNextDriver(void* list,
								driver_module_info*& driver);
			status_t		_FindBestDriver(const char* path,
								driver_module_info*& bestDriver,
								float& bestSupport,
								device_node* previous = NULL);
			status_t		_RegisterPath(const char* path);
			status_t		_RegisterDynamic(device_node* previous = NULL);
			status_t		_RemoveChildren();
			device_node*	_FindCurrentChild();
			void			_ReleaseWaiting();

	device_node*			fParent;
	NodeList				fChildren;
	int32					fRefCount;
	int32					fInitialized;
	bool					fRegistered;
	uint32					fFlags;
	float					fSupportsParent;
	uint32					fLastUpdateCycle;

	const char*				fModuleName;

	driver_module_info*		fDriver;
	void*					fDriverData;

	DeviceList				fDevices;
	AttributeList			fAttributes;
};

// flags in addition to those specified by B_DEVICE_FLAGS
enum node_flags {
	NODE_FLAG_REGISTER_INITIALIZED	= 0x00010000,
	NODE_FLAG_DEVICE_REMOVED		= 0x00020000,
	NODE_FLAG_OBSOLETE_DRIVER		= 0x00040000,
	NODE_FLAG_WAITING_FOR_DRIVER	= 0x00080000,

	NODE_FLAG_PUBLIC_MASK			= 0x0000ffff
};

device_manager_info *gDeviceManager;

static device_node *sRootNode;
static recursive_lock sLock;

static uint32 sDriverUpdateCycle = 1;
	// this is a *very* basic devfs emulation


//	#pragma mark -


static device_attr_private*
find_attr(const device_node* node, const char* name, bool recursive,
	type_code type)
{
	do {
		AttributeList::ConstIterator iterator
			= node->Attributes().GetIterator();

		while (iterator.HasNext()) {
			device_attr_private* attr = iterator.Next();

			if (type != B_ANY_TYPE && attr->type != type)
				continue;

			if (!strcmp(attr->name, name))
				return attr;
		}

		node = node->Parent();
	} while (node != NULL && recursive);

	return NULL;
}


static void
put_level(int32 level)
{
	while (level-- > 0)
		dprintf("   ");
}


static void
dump_attribute(device_attr* attr, int32 level)
{
	if (attr == NULL)
		return;

	put_level(level + 2);
	dprintf("\"%s\" : ", attr->name);
	switch (attr->type) {
		case B_STRING_TYPE:
			dprintf("string : \"%s\"", attr->value.string);
			break;
		case B_INT8_TYPE:
		case B_UINT8_TYPE:
			dprintf("uint8 : %u (%#x)", attr->value.ui8, attr->value.ui8);
			break;
		case B_INT16_TYPE:
		case B_UINT16_TYPE:
			dprintf("uint16 : %u (%#x)", attr->value.ui16, attr->value.ui16);
			break;
		case B_INT32_TYPE:
		case B_UINT32_TYPE:
			dprintf("uint32 : %lu (%#lx)", attr->value.ui32, attr->value.ui32);
			break;
		case B_INT64_TYPE:
		case B_UINT64_TYPE:
			dprintf("uint64 : %Lu (%#Lx)", attr->value.ui64, attr->value.ui64);
			break;
		default:
			dprintf("raw data");
	}
	dprintf("\n");
}


static void
uninit_unused()
{
	puts("uninit unused");
	RecursiveLocker _(sLock);
	sRootNode->UninitUnusedDriver();
}


static status_t
probe_path(const char* path)
{
	printf("probe path \"%s\"\n", path);
	RecursiveLocker _(sLock);
	return sRootNode->Probe(path, sDriverUpdateCycle);
}


static void
close_path(void* cookie)
{
	Device* device = (Device*)cookie;
	if (device == NULL)
		return;

	printf("close path \"%s\" (node %p)\n", device->Path(), device->Node());
	device->UninitDevice();
}


static Device*
get_device(device_node* node, const char* path)
{
	DeviceList::ConstIterator iterator = node->Devices().GetIterator();
	while (iterator.HasNext()) {
		Device* device = iterator.Next();
		if (!strcmp(device->Path(), path)) {
			status_t status = device->InitDevice();
			if (status != B_OK) {
				printf("opening path \"%s\" failed: %s\n", path,
					strerror(status));
				return NULL;
			}

			printf("open path \"%s\" (node %p)\n", device->Path(),
				device->Node());
			return device;
		}
	}

	// search in children

	NodeList::ConstIterator nodeIterator = node->Children().GetIterator();
	while (nodeIterator.HasNext()) {
		device_node* child = nodeIterator.Next();

		Device* device = get_device(child, path);
		if (device != NULL)
			return device;
	}

	return NULL;
}


static void*
open_path(const char* path)
{
	return get_device(sRootNode, path);
}


//	#pragma mark - Device Manager module API


static status_t
rescan_node(device_node* node)
{
	return B_ERROR;
}


static status_t
register_node(device_node* parent, const char* moduleName,
	const device_attr* attrs, const io_resource* ioResources,
	device_node** _node)
{
	if ((parent == NULL && sRootNode != NULL) || moduleName == NULL)
		return B_BAD_VALUE;

	if (parent != NULL && parent->FindChild(attrs) != NULL) {
		// A node like this one already exists for this parent
		return B_NAME_IN_USE;
	}

	// TODO: handle I/O resources!

	device_node *newNode = new(std::nothrow) device_node(moduleName, attrs,
		ioResources);
	if (newNode == NULL)
		return B_NO_MEMORY;

	TRACE(("%p: register node \"%s\", parent %p\n", newNode, moduleName,
		parent));

	RecursiveLocker _(sLock);

	status_t status = newNode->InitCheck();
	if (status != B_OK)
		goto err1;

#if 0
	// The following is done to reduce the stack usage of deeply nested
	// child device nodes.
	// There is no other need to delay the complete registration process
	// the way done here. This approach is also slightly different as
	// the registration might fail later than it used in case of errors.

	if (!parent->IsRegistered()) {
		// The parent has not been registered completely yet - child
		// registration is deferred to the parent registration
		return B_OK;
	}
#endif

	status = newNode->Register(parent);
	if (status < B_OK) {
		parent->RemoveChild(newNode);
		goto err1;
	}

	if (_node)
		*_node = newNode;

	return B_OK;

err1:
	newNode->Release();
	return status;
}


/*!	Unregisters the device \a node.

	If the node is currently in use, this function will return B_BUSY to
	indicate that the node hasn't been removed yet - it will still remove
	the node as soon as possible.
*/
static status_t
unregister_node(device_node* node)
{
	TRACE(("unregister_node(node %p)\n", node));
	RecursiveLocker _(sLock);

	bool initialized = node->IsInitialized();

	node->DeviceRemoved();

	return initialized ? B_BUSY : B_OK;
}


static status_t
get_driver(device_node* node, driver_module_info** _module, void** _data)
{
	if (node->DriverModule() == NULL)
		return B_NO_INIT;

	if (_module != NULL)
		*_module = node->DriverModule();
	if (_data != NULL)
		*_data = node->DriverData();

	return B_OK;
}


static device_node*
get_root_node(void)
{
	if (sRootNode != NULL)
		sRootNode->Acquire();

	return sRootNode;
}


static status_t
get_next_child_node(device_node* parent, const device_attr* attributes,
	device_node** _node)
{
	RecursiveLocker _(sLock);

	NodeList::ConstIterator iterator = parent->Children().GetIterator();
	device_node* last = *_node;

	// skip those we already traversed
	while (iterator.HasNext() && last != NULL) {
		device_node* node = iterator.Next();

		if (node != last)
			continue;
	}

	// find the next one that fits
	while (iterator.HasNext()) {
		device_node* node = iterator.Next();

		if (!node->IsRegistered())
			continue;

		if (!node->CompareTo(attributes)) {
			if (last != NULL)
				last->Release();

			node->Acquire();
			*_node = node;
			return B_OK;
		}
	}

	if (last != NULL)
		last->Release();

	return B_ENTRY_NOT_FOUND;
}


static device_node*
get_parent_node(device_node* node)
{
	if (node == NULL)
		return NULL;

	RecursiveLocker _(sLock);

	device_node* parent = node->Parent();
	parent->Acquire();

	return parent;
}


static void
put_node(device_node* node)
{
	RecursiveLocker _(sLock);
	node->Release();
}


static status_t
publish_device(device_node *node, const char *path, const char *moduleName)
{
	if (path == NULL || !path[0] || moduleName == NULL || !moduleName[0])
		return B_BAD_VALUE;

	RecursiveLocker _(sLock);
	dprintf("publish device: node %p, path %s, module %s\n", node, path,
		moduleName);

	Device* device = new(std::nothrow) Device(node, path, moduleName);
	if (device == NULL)
		return B_NO_MEMORY;

	if (device->InitCheck() != B_OK) {
		delete device;
		return B_NO_MEMORY;
	}

	node->AddDevice(device);
	return B_OK;
}


static status_t
unpublish_device(device_node *node, const char *path)
{
	if (path == NULL)
		return B_BAD_VALUE;

	RecursiveLocker _(sLock);

	DeviceList::ConstIterator iterator = node->Devices().GetIterator();
	while (iterator.HasNext()) {
		Device* device = iterator.Next();
		if (!strcmp(device->Path(), path)) {
			node->RemoveDevice(device);
			delete device;
			return B_OK;
		}
	}

	return B_ENTRY_NOT_FOUND;
}


static status_t
get_attr_uint8(const device_node* node, const char* name, uint8* _value,
	bool recursive)
{
	if (node == NULL || name == NULL || _value == NULL)
		return B_BAD_VALUE;

	device_attr_private* attr = find_attr(node, name, recursive, B_UINT8_TYPE);
	if (attr == NULL)
		return B_NAME_NOT_FOUND;

	*_value = attr->value.ui8;
	return B_OK;
}


static status_t
get_attr_uint16(const device_node* node, const char* name, uint16* _value,
	bool recursive)
{
	if (node == NULL || name == NULL || _value == NULL)
		return B_BAD_VALUE;

	device_attr_private* attr = find_attr(node, name, recursive, B_UINT16_TYPE);
	if (attr == NULL)
		return B_NAME_NOT_FOUND;

	*_value = attr->value.ui16;
	return B_OK;
}


static status_t
get_attr_uint32(const device_node* node, const char* name, uint32* _value,
	bool recursive)
{
	if (node == NULL || name == NULL || _value == NULL)
		return B_BAD_VALUE;

	device_attr_private* attr = find_attr(node, name, recursive, B_UINT32_TYPE);
	if (attr == NULL)
		return B_NAME_NOT_FOUND;

	*_value = attr->value.ui32;
	return B_OK;
}


static status_t
get_attr_uint64(const device_node* node, const char* name,
	uint64* _value, bool recursive)
{
	if (node == NULL || name == NULL || _value == NULL)
		return B_BAD_VALUE;

	device_attr_private* attr = find_attr(node, name, recursive, B_UINT64_TYPE);
	if (attr == NULL)
		return B_NAME_NOT_FOUND;

	*_value = attr->value.ui64;
	return B_OK;
}


static status_t
get_attr_string(const device_node* node, const char* name,
	const char** _value, bool recursive)
{
	if (node == NULL || name == NULL || _value == NULL)
		return B_BAD_VALUE;

	device_attr_private* attr = find_attr(node, name, recursive, B_STRING_TYPE);
	if (attr == NULL)
		return B_NAME_NOT_FOUND;

	*_value = attr->value.string;
	return B_OK;
}


static status_t
get_attr_raw(const device_node* node, const char* name, const void** _data,
	size_t* _length, bool recursive)
{
	if (node == NULL || name == NULL || (_data == NULL && _length == NULL))
		return B_BAD_VALUE;

	device_attr_private* attr = find_attr(node, name, recursive, B_RAW_TYPE);
	if (attr == NULL)
		return B_NAME_NOT_FOUND;

	if (_data != NULL)
		*_data = attr->value.raw.data;
	if (_length != NULL)
		*_length = attr->value.raw.length;
	return B_OK;
}


static status_t
get_next_attr(device_node* node, device_attr** _attr)
{
	if (node == NULL)
		return B_BAD_VALUE;

	device_attr_private* next;
	device_attr_private* attr = *(device_attr_private**)_attr;

	if (attr != NULL) {
		// next attribute
		next = attr->GetDoublyLinkedListLink()->next;
	} else {
		// first attribute
		next = node->Attributes().First();
	}

	*_attr = next;

	return next ? B_OK : B_ENTRY_NOT_FOUND;
}


static struct device_manager_info sDeviceManagerModule = {
	{
		B_DEVICE_MANAGER_MODULE_NAME,
		0,
		NULL
	},

	// device nodes
	rescan_node,
	register_node,
	unregister_node,
	get_driver,
	get_root_node,
	get_next_child_node,
	get_parent_node,
	put_node,

	// devices
	publish_device,
	unpublish_device,

	// attributes
	get_attr_uint8,
	get_attr_uint16,
	get_attr_uint32,
	get_attr_uint64,
	get_attr_string,
	get_attr_raw,
	get_next_attr,
};


//	#pragma mark - device_attr


device_attr_private::device_attr_private()
{
	name = NULL;
	type = 0;
	value.raw.data = NULL;
	value.raw.length = 0;
}


device_attr_private::device_attr_private(const device_attr& attr)
{
	CopyFrom(attr);
}


device_attr_private::~device_attr_private()
{
	_Unset();
}


status_t
device_attr_private::InitCheck()
{
	return name != NULL ? B_OK : B_NO_INIT;
}


status_t
device_attr_private::CopyFrom(const device_attr& attr)
{
	name = strdup(attr.name);
	if (name == NULL)
		return B_NO_MEMORY;

	type = attr.type;

	switch (type) {
		case B_UINT8_TYPE:
		case B_UINT16_TYPE:
		case B_UINT32_TYPE:
		case B_UINT64_TYPE:
			value.ui64 = attr.value.ui64;
			break;

		case B_STRING_TYPE:
			if (attr.value.string != NULL) {
				value.string = strdup(attr.value.string);
				if (value.string == NULL) {
					_Unset();
					return B_NO_MEMORY;
				}
			} else
				value.string = NULL;
			break;

		case B_RAW_TYPE:
			value.raw.data = malloc(attr.value.raw.length);
			if (value.raw.data == NULL) {
				_Unset();
				return B_NO_MEMORY;
			}

			value.raw.length = attr.value.raw.length;
			memcpy((void*)value.raw.data, attr.value.raw.data,
				attr.value.raw.length);
			break;

		default:
			return B_BAD_VALUE;
	}

	return B_OK;
}


void
device_attr_private::_Unset()
{
	if (type == B_STRING_TYPE)
		free((char*)value.string);
	else if (type == B_RAW_TYPE)
		free((void*)value.raw.data);

	free((char*)name);

	name = NULL;
	value.raw.data = NULL;
	value.raw.length = 0;
}


/*static*/ int
device_attr_private::Compare(const device_attr* attrA, const device_attr *attrB)
{
	if (attrA->type != attrB->type)
		return -1;

	switch (attrA->type) {
		case B_UINT8_TYPE:
			return (int)attrA->value.ui8 - (int)attrB->value.ui8;

		case B_UINT16_TYPE:
			return (int)attrA->value.ui16 - (int)attrB->value.ui16;

		case B_UINT32_TYPE:
			if (attrA->value.ui32 > attrB->value.ui32)
				return 1;
			if (attrA->value.ui32 < attrB->value.ui32)
				return -1;
			return 0;

		case B_UINT64_TYPE:
			if (attrA->value.ui64 > attrB->value.ui64)
				return 1;
			if (attrA->value.ui64 < attrB->value.ui64)
				return -1;
			return 0;

		case B_STRING_TYPE:
			return strcmp(attrA->value.string, attrB->value.string);

		case B_RAW_TYPE:
			if (attrA->value.raw.length != attrB->value.raw.length)
				return -1;

			return memcmp(attrA->value.raw.data, attrB->value.raw.data,
				attrA->value.raw.length);
	}

	return -1;
}


//	#pragma mark - Device


Device::Device(device_node* node, const char* path, const char* moduleName)
	:
	fNode(node),
	fInitialized(0),
	fDeviceModule(NULL),
	fDeviceData(NULL)
{
	fPath = strdup(path);
	fModuleName = strdup(moduleName);
}


Device::~Device()
{
	free((char*)fPath);
	free((char*)fModuleName);
}


status_t
Device::InitCheck() const
{
	return fPath != NULL && fModuleName != NULL ? B_OK : B_NO_MEMORY;
}


status_t
Device::InitDevice()
{
	if ((fNode->Flags() & NODE_FLAG_DEVICE_REMOVED) != 0) {
		// TODO: maybe the device should be unlinked in devfs, too
		return ENODEV;
	}
	if ((fNode->Flags() & NODE_FLAG_WAITING_FOR_DRIVER) != 0)
		return B_BUSY;

	if (fInitialized++ > 0) {
		fNode->InitDriver();
			// acquire another reference to our parent as well
		return B_OK;
	}

	status_t status = get_module(ModuleName(), (module_info**)&fDeviceModule);
	if (status == B_OK) {
		// our parent always has to be initialized
		status = fNode->InitDriver();
	}
	if (status < B_OK) {
		fInitialized--;
		return status;
	}

	if (fDeviceModule->init_device != NULL)
		status = fDeviceModule->init_device(fNode->DriverData(), &fDeviceData);

	if (status < B_OK) {
		fNode->UninitDriver();
		fInitialized--;

		put_module(ModuleName());
		fDeviceModule = NULL;
		fDeviceData = NULL;
	}

	return status;
}


void
Device::UninitDevice()
{
	if (fInitialized-- > 1) {
		fNode->UninitDriver();
		return;
	}

	TRACE(("uninit driver for node %p\n", this));

	if (fDeviceModule->uninit_device != NULL)
		fDeviceModule->uninit_device(fDeviceData);

	fDeviceModule = NULL;
	fDeviceData = NULL;

	put_module(ModuleName());

	fNode->UninitDriver();
}


//	#pragma mark - device_node


/*!	Allocate device node info structure;
	initially, ref_count is one to make sure node won't get destroyed by mistake
*/
device_node::device_node(const char* moduleName, const device_attr* attrs,
	const io_resource* resources)
{
	fModuleName = strdup(moduleName);
	if (fModuleName == NULL)
		return;

	fParent = NULL;
	fRefCount = 1;
	fInitialized = 0;
	fRegistered = false;
	fFlags = 0;
	fSupportsParent = 0.0;
	fLastUpdateCycle = 0;
	fDriver = NULL;
	fDriverData = NULL;

	// copy attributes

	while (attrs != NULL && attrs->name != NULL) {
		device_attr_private* attr
			= new(std::nothrow) device_attr_private(*attrs);
		if (attr == NULL)
			break;

		fAttributes.Add(attr);
		attrs++;
	}

	get_attr_uint32(this, B_DEVICE_FLAGS, &fFlags, false);
	fFlags &= NODE_FLAG_PUBLIC_MASK;
}


device_node::~device_node()
{
	TRACE(("delete node %p\n", this));
	ASSERT(DriverModule() == NULL);

	if (Parent() != NULL) {
		if ((fFlags & NODE_FLAG_OBSOLETE_DRIVER) != 0) {
			// This driver has been obsoleted; another driver has been waiting
			// for us - make it available
			Parent()->_ReleaseWaiting();
		}
		Parent()->RemoveChild(this);
	}

	// Delete children
	NodeList::Iterator nodeIterator = fChildren.GetIterator();
	while (nodeIterator.HasNext()) {
		device_node* child = nodeIterator.Next();
		nodeIterator.Remove();
		delete child;
	}

	// Delete devices
	DeviceList::Iterator deviceIterator = fDevices.GetIterator();
	while (deviceIterator.HasNext()) {
		Device* device = deviceIterator.Next();
		deviceIterator.Remove();
		// TODO: unpublish!
		delete device;
	}

	// Delete attributes
	AttributeList::Iterator attrIterator = fAttributes.GetIterator();
	while (attrIterator.HasNext()) {
		device_attr_private* attr = attrIterator.Next();
		attrIterator.Remove();
		delete attr;
	}

	free((char*)fModuleName);
}


status_t
device_node::InitCheck() const
{
	return fModuleName != NULL ? B_OK : B_NO_MEMORY;
}


status_t
device_node::InitDriver()
{
	if (fInitialized++ > 0) {
		if (Parent() != NULL) {
			Parent()->InitDriver();
				// acquire another reference to our parent as well
		}
		Acquire();
		return B_OK;
	}

	status_t status = get_module(ModuleName(), (module_info**)&fDriver);
	if (status == B_OK && Parent() != NULL) {
		// our parent always has to be initialized
		status = Parent()->InitDriver();
	}
	if (status < B_OK) {
		fInitialized--;
		return status;
	}

	if (fDriver->init_driver != NULL)
		status = fDriver->init_driver(this, &fDriverData);

	if (status < B_OK) {
		if (Parent() != NULL)
			Parent()->UninitDriver();
		fInitialized--;

		put_module(ModuleName());
		fDriver = NULL;
		fDriverData = NULL;
		return status;
	}

	Acquire();
	return B_OK;
}


bool
device_node::UninitDriver()
{
	if (fInitialized-- > 1) {
		if (Parent() != NULL)
			Parent()->UninitDriver();
		Release();
		return false;
	}

	TRACE(("uninit driver for node %p\n", this));

	if (fDriver->uninit_driver != NULL)
		fDriver->uninit_driver(fDriverData);

	fDriver = NULL;
	fDriverData = NULL;

	put_module(ModuleName());

	if (Parent() != NULL)
		Parent()->UninitDriver();
	Release();

	return true;
}


void
device_node::AddChild(device_node* node)
{
	// we must not be destroyed	as long as we have children
	Acquire();
	node->fParent = this;
	fChildren.Add(node);
}


void
device_node::RemoveChild(device_node* node)
{
	node->fParent = NULL;
	fChildren.Remove(node);
	Release();
}


/*!	Registers this node, and all of its children that have to be registered.
	Also initializes the driver and keeps it that way on return in case
	it returns successfully.
*/
status_t
device_node::Register(device_node* parent)
{
	// make it public
	if (parent != NULL)
		parent->AddChild(this);
	else
		sRootNode = this;

	status_t status = InitDriver();
	if (status != B_OK)
		return status;

	if ((fFlags & B_KEEP_DRIVER_LOADED) != 0) {
		// We keep this driver loaded by having it always initialized
		InitDriver();
	}

	fFlags |= NODE_FLAG_REGISTER_INITIALIZED;
		// We don't uninitialize the driver - this is done by the caller
		// in order to save reinitializing during driver loading.

	uint32 registered;
	status = _RegisterFixed(registered);
	if (status != B_OK) {
		UninitUnusedDriver();
		return status;
	}
	if (registered > 0) {
		fRegistered = true;
		return B_OK;
	}

	// Register the children the driver wants

	if (DriverModule()->register_child_devices != NULL) {
		status = DriverModule()->register_child_devices(this);
		if (status != B_OK) {
			UninitUnusedDriver();
			return status;
		}

		if (!fChildren.IsEmpty()) {
			fRegistered = true;
			return B_OK;
		}
	}

	// Register all possible child device nodes

	status = _RegisterDynamic();
	if (status == B_OK)
		fRegistered = true;
	else
		UninitUnusedDriver();

	return status;
}


/*!	Registers any children that are identified via the B_DRIVER_FIXED_CHILD
	attribute.
	If any of these children cannot be registered, this call will fail (we
	don't remove children we already registered up to this point in this case).
*/
status_t
device_node::_RegisterFixed(uint32& registered)
{
	AttributeList::Iterator iterator = fAttributes.GetIterator();
	registered = 0;

	while (iterator.HasNext()) {
		device_attr_private* attr = iterator.Next();
		if (strcmp(attr->name, B_DEVICE_FIXED_CHILD))
			continue;

		driver_module_info* driver;
		status_t status = get_module(attr->value.string,
			(module_info**)&driver);
		if (status != B_OK)
			return status;

		if (driver->register_device != NULL) {
			status = driver->register_device(this);
			if (status == B_OK)
				registered++;
		}

		put_module(attr->value.string);

		if (status != B_OK)
			return status;
	}

	return B_OK;
}


status_t
device_node::_AddPath(Stack<KPath*>& stack, const char* basePath,
	const char* subPath)
{
	KPath* path = new(std::nothrow) KPath;
	if (path == NULL)
		return B_NO_MEMORY;

	status_t status = path->SetTo(basePath);
	if (status == B_OK && subPath != NULL && subPath[0])
		status = path->Append(subPath);
	if (status == B_OK)
		status = stack.Push(path);

	if (status != B_OK)
		delete path;

	return status;
}


status_t
device_node::_GetNextDriverPath(void*& cookie, KPath& _path)
{
	Stack<KPath*>* stack = NULL;

	if (cookie == NULL) {
		// find all paths and add them
		stack = new(std::nothrow) Stack<KPath*>();
		if (stack == NULL)
			return B_NO_MEMORY;

		StackDeleter<KPath*> stackDeleter(stack);
		uint16 type = 0;
		uint16 subType = 0;
		uint16 interface = 0;
		get_attr_uint16(this, B_DEVICE_TYPE, &type, false);
		get_attr_uint16(this, B_DEVICE_SUB_TYPE, &subType, false);
		get_attr_uint16(this, B_DEVICE_INTERFACE, &interface, false);

		// TODO: maybe make this extendible via settings file?
		switch (type) {
			case PCI_mass_storage:
				switch (subType) {
					case PCI_scsi:
						_AddPath(*stack, "busses", "scsi");
						break;
					case PCI_ide:
						_AddPath(*stack, "busses", "ide");
						break;
					case PCI_sata:
						_AddPath(*stack, "busses", "sata");
						break;
					default:
						_AddPath(*stack, "busses", "disk");
						break;
				}
				break;
			case PCI_serial_bus:
				switch (subType) {
					case PCI_firewire:
						_AddPath(*stack, "busses", "firewire");
						break;
					case PCI_usb:
						_AddPath(*stack, "busses", "usb");
						break;
					default:
						_AddPath(*stack, "busses");
						break;
				}
				break;
			case PCI_network:
				_AddPath(*stack, "drivers", "net");
				break;
			case PCI_display:
				_AddPath(*stack, "drivers", "graphics");
				break;
			case PCI_multimedia:
				switch (subType) {
					case PCI_audio:
					case PCI_hd_audio:
						_AddPath(*stack, "drivers", "audio");
						break;
					case PCI_video:
						_AddPath(*stack, "drivers", "video");
						break;
					default:
						_AddPath(*stack, "drivers");
						break;
				}
				break;
			default:
				if (sRootNode == this) {
					_AddPath(*stack, "busses/pci");
					_AddPath(*stack, "bus_managers");
				} else
					_AddPath(*stack, "drivers");
				break;
		}

		stackDeleter.Detach();

		cookie = (void*)stack;
	} else
		stack = static_cast<Stack<KPath*>*>(cookie);

	KPath* path;
	if (stack->Pop(&path)) {
		_path.Adopt(*path);
		delete path;
		return B_OK;
	}

	delete stack;
	return B_ENTRY_NOT_FOUND;
}


status_t
device_node::_GetNextDriver(void* list, driver_module_info*& driver)
{
	while (true) {
		char name[B_FILE_NAME_LENGTH];
		size_t nameLength = sizeof(name);

		status_t status = read_next_module_name(list, name, &nameLength);
		if (status != B_OK)
			return status;

		if (!strcmp(fModuleName, name))
			continue;

		if (get_module(name, (module_info**)&driver) != B_OK)
			continue;

		if (driver->supports_device == NULL
			|| driver->register_device == NULL) {
			put_module(name);
			continue;
		}

		return B_OK;
	}
}


status_t
device_node::_FindBestDriver(const char* path, driver_module_info*& bestDriver,
	float& bestSupport, device_node* previous)
{
	if (bestDriver == NULL)
		bestSupport = previous != NULL ? previous->fSupportsParent : 0.0f;

	void* list = open_module_list_etc(path, "driver_v1");
	driver_module_info* driver;
	while (_GetNextDriver(list, driver) == B_OK) {
		if (previous != NULL && driver == previous->DriverModule()) {
			put_module(driver->info.name);
			continue;
		}

		float support = driver->supports_device(this);
		if (support > bestSupport) {
			if (bestDriver != NULL)
				put_module(bestDriver->info.name);

			bestDriver = driver;
			bestSupport = support;
			continue;
				// keep reference to best module around
		}

		put_module(driver->info.name);
	}
	close_module_list(list);

	return bestDriver != NULL ? B_OK : B_ENTRY_NOT_FOUND;
}


status_t
device_node::_RegisterPath(const char* path)
{
	void* list = open_module_list_etc(path, "driver_v1");
	driver_module_info* driver;
	uint32 count = 0;

	while (_GetNextDriver(list, driver) == B_OK) {
		float support = driver->supports_device(this);
		if (support > 0.0) {
			TRACE(("  register module \"%s\", support %f\n", driver->info.name,
				support));
			if (driver->register_device(this) == B_OK)
				count++;
		}

		put_module(driver->info.name);
	}
	close_module_list(list);

	return count > 0 ? B_OK : B_ENTRY_NOT_FOUND;
}


bool
device_node::_AlwaysRegisterDynamic()
{
	uint16 type = 0;
	uint16 subType = 0;
	get_attr_uint16(this, B_DEVICE_TYPE, &type, false);
	get_attr_uint16(this, B_DEVICE_SUB_TYPE, &subType, false);

	return type == PCI_serial_bus || type == PCI_bridge;
		// TODO: we may want to be a bit more specific in the future
}


status_t
device_node::_RegisterDynamic(device_node* previous)
{
	// If this is our initial registration, we honour the B_FIND_CHILD_ON_DEMAND
	// requirements
	if (!fRegistered && (fFlags & B_FIND_CHILD_ON_DEMAND) != 0
		&& !_AlwaysRegisterDynamic())
		return B_OK;

	KPath path;

	if ((fFlags & B_FIND_MULTIPLE_CHILDREN) == 0) {
		// find the one driver
		driver_module_info* bestDriver = NULL;
		float bestSupport = 0.0;
		void* cookie = NULL;

		while (_GetNextDriverPath(cookie, path) == B_OK) {
			_FindBestDriver(path.Path(), bestDriver, bestSupport, previous);
		}

		if (bestDriver != NULL) {
			TRACE(("  register best module \"%s\", support %f\n",
				bestDriver->info.name, bestSupport));
			if (bestDriver->register_device(this) == B_OK) {
				// There can only be one node of this driver
				// (usually only one at all, but there might be a new driver
				// "waiting" for its turn)
				device_node* child = FindChild(bestDriver->info.name);
				if (child != NULL) {
					child->fSupportsParent = bestSupport;
					if (previous != NULL) {
						previous->fFlags |= NODE_FLAG_OBSOLETE_DRIVER;
						previous->Release();
						child->fFlags |= NODE_FLAG_WAITING_FOR_DRIVER;
					}
				}
				// TODO: if this fails, we could try the second best driver,
				// and so on...
			}
			put_module(bestDriver->info.name);
		}
	} else {
		// register all drivers that match
		void* cookie = NULL;
		while (_GetNextDriverPath(cookie, path) == B_OK) {
			_RegisterPath(path.Path());
		}
	}

	return B_OK;
}


void
device_node::_ReleaseWaiting()
{
	NodeList::Iterator iterator = fChildren.GetIterator();
	while (iterator.HasNext()) {
		device_node* child = iterator.Next();

		child->fFlags &= ~NODE_FLAG_WAITING_FOR_DRIVER;
	}
}


status_t
device_node::_RemoveChildren()
{
	NodeList::Iterator iterator = fChildren.GetIterator();
	while (iterator.HasNext()) {
		device_node* child = iterator.Next();
		child->Release();
	}

	return fChildren.IsEmpty() ? B_OK : B_BUSY;
}


device_node*
device_node::_FindCurrentChild()
{
	NodeList::Iterator iterator = fChildren.GetIterator();
	while (iterator.HasNext()) {
		device_node* child = iterator.Next();

		if ((child->Flags() & NODE_FLAG_WAITING_FOR_DRIVER) == 0)
			return child;
	}

	return NULL;
}


status_t
device_node::Probe(const char* devicePath, uint32 updateCycle)
{
	if ((fFlags & NODE_FLAG_DEVICE_REMOVED) != 0
		|| updateCycle == fLastUpdateCycle)
		return B_OK;

	status_t status = InitDriver();
	if (status < B_OK)
		return status;

	MethodDeleter<device_node, bool> uninit(this,
		&device_node::UninitDriver);

	uint16 type = 0;
	uint16 subType = 0;
	if (get_attr_uint16(this, B_DEVICE_TYPE, &type, false) == B_OK
		&& get_attr_uint16(this, B_DEVICE_SUB_TYPE, &subType, false)
			== B_OK) {
		// Check if this node matches the device path
		// TODO: maybe make this extendible via settings file?
		bool matches = false;
		if (!strcmp(devicePath, "disk")) {
			matches = type == PCI_mass_storage;
		} else if (!strcmp(devicePath, "audio")) {
			matches = type == PCI_multimedia
				&& (subType == PCI_audio || subType == PCI_hd_audio);
		} else if (!strcmp(devicePath, "net")) {
			matches = type == PCI_network;
		} else if (!strcmp(devicePath, "graphics")) {
			matches = type == PCI_display;
		} else if (!strcmp(devicePath, "video")) {
			matches = type == PCI_multimedia && subType == PCI_video;
		}

		if (matches) {
			device_node* previous = NULL;

			fLastUpdateCycle = updateCycle;
				// This node will be probed in this update cycle

			if (!fChildren.IsEmpty()
				&& (fFlags & B_FIND_MULTIPLE_CHILDREN) == 0) {
				// We already have a driver that claims this node; remove all
				// (unused) nodes, and evaluate it again
				_RemoveChildren();

				previous = _FindCurrentChild();
				if (previous != NULL) {
					// This driver is still active - give it back the reference
					// that was stolen by _RemoveChildren() - _RegisterDynamic()
					// will release it, if it really isn't needed anymore
					previous->Acquire();
				}
			}
			return _RegisterDynamic(previous);
		}

		return B_OK;
	}

	NodeList::Iterator iterator = fChildren.GetIterator();
	while (iterator.HasNext()) {
		device_node* child = iterator.Next();

		status = child->Probe(devicePath, updateCycle);
		if (status != B_OK)
			return status;
	}

	return B_OK;
}


/*!	Uninitializes all temporary references to the driver. The registration
	process keeps the driver initialized to optimize the startup procedure;
	this function gives this reference away again.
*/
void
device_node::UninitUnusedDriver()
{
	// First, we need to go to the leaf, and go back from there

	NodeList::Iterator iterator = fChildren.GetIterator();
	while (iterator.HasNext()) {
		device_node* child = iterator.Next();

		child->UninitUnusedDriver();
	}

	if (!IsInitialized()
		|| (fFlags & NODE_FLAG_REGISTER_INITIALIZED) == 0)
		return;

	fFlags &= ~NODE_FLAG_REGISTER_INITIALIZED;

	UninitDriver();
}


/*!	Calls device_removed() on this node and all of its children - starting
	with the deepest and last child.
	It will also remove the one reference that every node gets on its creation.
*/
void
device_node::DeviceRemoved()
{
	// notify children
	NodeList::ConstIterator iterator = Children().GetIterator();
	while (iterator.HasNext()) {
		device_node* child = iterator.Next();

		child->DeviceRemoved();
	}

	// notify devices
	DeviceList::ConstIterator deviceIterator = Devices().GetIterator();
	while (deviceIterator.HasNext()) {
		Device* device = deviceIterator.Next();

		if (device->DeviceModule() != NULL
			&& device->DeviceModule()->device_removed != NULL)
			device->DeviceModule()->device_removed(device->DeviceData());
	}

	fFlags |= NODE_FLAG_DEVICE_REMOVED;

	if (IsInitialized() && DriverModule()->device_removed != NULL)
		DriverModule()->device_removed(this);

	if ((fFlags & B_KEEP_DRIVER_LOADED) != 0) {
		// There is no point in keeping this driver loaded when its device
		// is gone
		UninitDriver();
	}

	UninitUnusedDriver();
	Release();
}


void
device_node::Acquire()
{
	atomic_add(&fRefCount, 1);
}


bool
device_node::Release()
{
	if (atomic_add(&fRefCount, -1) > 1)
		return false;

	delete this;
	return true;
}


void
device_node::AddDevice(Device* device)
{
	fDevices.Add(device);
}


void
device_node::RemoveDevice(Device* device)
{
	fDevices.Remove(device);
}


int
device_node::CompareTo(const device_attr* attributes) const
{
	if (attributes == NULL)
		return -1;

	for (; attributes->name != NULL; attributes++) {
		// find corresponding attribute
		AttributeList::ConstIterator iterator = Attributes().GetIterator();
		device_attr_private* attr = NULL;
		while (iterator.HasNext()) {
			attr = iterator.Next();

			if (!strcmp(attr->name, attributes->name))
				break;
		}
		if (!iterator.HasNext())
			return -1;

		int compare = device_attr_private::Compare(attr, attributes);
		if (compare != 0)
			return compare;
	}

	return 0;
}


device_node*
device_node::FindChild(const device_attr* attributes) const
{
	if (attributes == NULL)
		return NULL;

	NodeList::ConstIterator iterator = Children().GetIterator();
	while (iterator.HasNext()) {
		device_node* child = iterator.Next();

		// ignore nodes that are pending to be removed
		if ((child->Flags() & NODE_FLAG_DEVICE_REMOVED) == 0
			&& !child->CompareTo(attributes))
			return child;
	}

	return NULL;
}


device_node*
device_node::FindChild(const char* moduleName) const
{
	if (moduleName == NULL)
		return NULL;

	NodeList::ConstIterator iterator = Children().GetIterator();
	while (iterator.HasNext()) {
		device_node* child = iterator.Next();

		if (!strcmp(child->ModuleName(), moduleName))
			return child;
	}

	return NULL;
}


void
device_node::Dump(int32 level = 0)
{
	put_level(level);
	dprintf("(%ld) @%p \"%s\" (ref %ld, init %ld)\n", level, this, ModuleName(),
		fRefCount, fInitialized);

	AttributeList::Iterator attribute = Attributes().GetIterator();
	while (attribute.HasNext()) {
		dump_attribute(attribute.Next(), level);
	}

	NodeList::ConstIterator iterator = Children().GetIterator();
	while (iterator.HasNext()) {
		iterator.Next()->Dump(level + 1);
	}
}


//	#pragma mark - root node


static void
init_root_node(void)
{
	device_attr attrs[] = {
		{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "Devices Root"}},
		{B_DEVICE_BUS, B_STRING_TYPE, {string: "root"}},
		{B_DEVICE_FLAGS, B_UINT32_TYPE,
			{ui32: B_FIND_MULTIPLE_CHILDREN | B_KEEP_DRIVER_LOADED }},
		{NULL}
	};

	if (register_node(NULL, DEVICE_MANAGER_ROOT_NAME, attrs, NULL, NULL)
			!= B_OK) {
		dprintf("Cannot register Devices Root Node\n");
	}
}


static driver_module_info sDeviceRootModule = {
	{
		DEVICE_MANAGER_ROOT_NAME,
		0,
		NULL,
	},
	NULL
};


//	#pragma mark -


int
main(int argc, char** argv)
{
	_add_builtin_module((module_info*)&sDeviceManagerModule);
	_add_builtin_module((module_info*)&sDeviceRootModule);

	// bus
	_add_builtin_module((module_info*)&gBusModuleInfo);
	_add_builtin_module((module_info*)&gBusDriverModuleInfo);

	// sample driver
	_add_builtin_module((module_info*)&gDriverModuleInfo);
	_add_builtin_module((module_info*)&gDeviceModuleInfo);

	// generic video driver
	_add_builtin_module((module_info*)&gGenericVideoDriverModuleInfo);
	_add_builtin_module((module_info*)&gGenericVideoDeviceModuleInfo);

	gDeviceManager = &sDeviceManagerModule;

	status_t status = _get_builtin_dependencies();
	if (status < B_OK) {
		fprintf(stderr, "device_manager: Could not initialize modules: %s\n",
			strerror(status));
		return 1;
	}

	recursive_lock_init(&sLock, "device manager");

	init_root_node();
	sRootNode->Dump();

	probe_path("net");
	probe_path("graphics");

	void* netHandle = open_path("net/sample/0");

	uninit_unused();

	puts("remove net driver");
	device_node* busNode = sRootNode->FindChild(BUS_MODULE_NAME);
	bus_trigger_device_removed(busNode);

	close_path(netHandle);
		// the net nodes must be removed with this call

	void* graphicsHandle = open_path("graphics/generic/0");

	// add specific video driver - ie. simulate installing it
	_add_builtin_module((module_info*)&gSpecificVideoDriverModuleInfo);
	_add_builtin_module((module_info*)&gSpecificVideoDeviceModuleInfo);
	sDriverUpdateCycle++;
	probe_path("graphics");

	open_path("graphics/specific/0");
		// this will fail

	close_path(graphicsHandle);
		// the graphics drivers must be switched with this call

	graphicsHandle = open_path("graphics/specific/0");
	close_path(graphicsHandle);

	uninit_unused();

	recursive_lock_destroy(&sLock);
	return 0;
}