More work-in-progress:

* Added a generic (for all devices) and specific (for a specific device) video
  driver to be able to play with the replace mechanism (which is not yet done,
  but works well for the one usage case tested).
* Added reference counting and initialize counting: now, each node owns a
  reference of its parent, and each initialized node owns an initialization
  reference of its parent.
* Added locking.
* Moved dump functionality into a member function.
* The same node can now only added once - ie. if a bus tries to register the
  same device twice, it will fail.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25453 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-05-11 17:00:07 +00:00
parent dd6eebabfe
commit 548c508ff7
7 changed files with 554 additions and 43 deletions

View File

@ -12,6 +12,8 @@ SimpleTest device_manager :
bus.cpp
driver.cpp
generic_video_driver.cpp
specific_video_driver.cpp
: be libkernelland_emu.so
;

View File

@ -71,6 +71,7 @@ register_child_devices(device_node *node)
} kDevices[] = {
{0x1000, 0x0001, PCI_mass_storage, PCI_sata, PCI_sata_ahci},
{0x1001, 0x0001, PCI_network, PCI_ethernet, 0},
{0x1001, 0x0002, PCI_display, 0, 0},
{0x1002, 0x0001, PCI_multimedia, PCI_audio, 0},
{0x1002, 0x0002, PCI_serial_bus, PCI_usb, PCI_usb_ehci},
};

View File

@ -29,6 +29,10 @@
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;
@ -113,6 +117,14 @@ struct device_node : DoublyLinkedListLinkImpl<device_node> {
bool IsRegistered() const { return fRegistered; }
bool IsInitialized() const { return fInitialized > 0; }
void Acquire();
bool Release();
int CompareTo(const device_attr* attributes) const;
device_node* FindChild(const device_attr* attributes) const;
void Dump(int32 level = 0);
private:
status_t _RegisterFixed(uint32& registered);
bool _AlwaysRegisterDynamic();
@ -361,27 +373,6 @@ dump_attribute(device_attr* attr, int32 level)
}
void
dm_dump_node(device_node* node, int32 level)
{
if (node == NULL)
return;
put_level(level);
dprintf("(%ld) @%p \"%s\"\n", level, node, node->ModuleName());
AttributeList::Iterator attribute = node->Attributes().GetIterator();
while (attribute.HasNext()) {
dump_attribute(attribute.Next(), level);
}
NodeList::ConstIterator iterator = node->Children().GetIterator();
while (iterator.HasNext()) {
dm_dump_node(iterator.Next(), level + 1);
}
}
static void
uninit_unused()
{
@ -434,6 +425,8 @@ device_node::device_node(const char* moduleName, const device_attr* attrs,
device_node::~device_node()
{
TRACE(("delete node %p\n", this));
// Delete children
NodeList::Iterator nodeIterator = fChildren.GetIterator();
while (nodeIterator.HasNext()) {
@ -464,10 +457,20 @@ device_node::InitCheck()
status_t
device_node::InitDriver()
{
if (fInitialized++ > 0)
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 have to be initialized
status = Parent()->InitDriver();
}
if (status < B_OK) {
fInitialized--;
return status;
@ -475,12 +478,17 @@ device_node::InitDriver()
if (fDriver->init_driver != NULL)
status = fDriver->init_driver(this, &fDriverData);
if (status < B_OK) {
fInitialized--;
put_module(ModuleName());
fDriver = NULL;
fDriverData = NULL;
return status;
}
Acquire();
return B_OK;
}
@ -488,8 +496,11 @@ device_node::InitDriver()
bool
device_node::UninitDriver()
{
if (fInitialized-- > 1)
if (fInitialized-- > 1) {
Release();
return false;
}
TRACE(("uninit driver for node %p\n", this));
if (fDriver->uninit_driver != NULL)
fDriver->uninit_driver(this);
@ -499,8 +510,12 @@ device_node::UninitDriver()
put_module(ModuleName());
Release();
if (Parent() != NULL)
Parent()->UninitDriver();
if ((fFlags & NODE_FLAG_REMOVE_ON_UNINIT) != 0)
delete this;
Release();
return true;
}
@ -510,7 +525,7 @@ void
device_node::AddChild(device_node* node)
{
// we must not be destroyed as long as we have children
fRefCount++;
Acquire();
node->fParent = this;
fChildren.Add(node);
}
@ -519,10 +534,9 @@ device_node::AddChild(device_node* node)
void
device_node::RemoveChild(device_node* node)
{
fRefCount--;
// TODO: we may need to destruct ourselves here!
node->fParent = NULL;
fChildren.Remove(node);
Release();
}
@ -784,7 +798,8 @@ device_node::_RegisterPath(const char* path)
while (_GetNextDriver(list, driver) == B_OK) {
float support = driver->supports_device(this);
if (support > 0.0) {
printf(" register module \"%s\", support %f\n", driver->info.name, support);
TRACE((" register module \"%s\", support %f\n", driver->info.name,
support));
if (driver->register_device(this) == B_OK)
count++;
}
@ -835,7 +850,8 @@ device_node::_RegisterDynamic()
}
if (bestDriver != NULL) {
printf(" register best module \"%s\", support %f\n", bestDriver->info.name, bestSupport);
TRACE((" register best module \"%s\", support %f\n",
bestDriver->info.name, bestSupport));
bestDriver->register_device(this);
put_module(bestDriver->info.name);
}
@ -861,9 +877,10 @@ device_node::_RemoveChildren()
if (!child->IsInitialized()) {
// this child is not used currently, and can be removed safely
iterator.Remove();
fRefCount--;
child->fParent = NULL;
delete child;
if (Release())
panic("died early");
} else
child->fFlags |= NODE_FLAG_REMOVE_ON_UNINIT;
}
@ -875,6 +892,12 @@ device_node::_RemoveChildren()
status_t
device_node::Probe(const char* devicePath)
{
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 (dm_get_attr_uint16(this, B_DEVICE_TYPE, &type, false) == B_OK
@ -917,7 +940,7 @@ device_node::Probe(const char* devicePath)
while (iterator.HasNext()) {
device_node* child = iterator.Next();
status_t status = child->Probe(devicePath);
status = child->Probe(devicePath);
if (status != B_OK)
return status;
}
@ -965,6 +988,91 @@ device_node::UninitUnusedChildren()
}
void
device_node::Acquire()
{
atomic_add(&fRefCount, 1);
}
bool
device_node::Release()
{
if (atomic_add(&fRefCount, -1) > 1)
return false;
if (Parent() != NULL)
Parent()->RemoveChild(this);
delete this;
return true;
}
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();
if (!child->CompareTo(attributes))
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 - Device Manager module API
@ -983,6 +1091,11 @@ register_device(device_node* parent, const char* moduleName,
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,
@ -999,16 +1112,16 @@ register_device(device_node* parent, const char* moduleName,
if (status != B_OK)
goto err1;
status = newNode->InitDriver();
if (status != B_OK)
goto err1;
// make it public
if (parent != NULL)
parent->AddChild(newNode);
else
sRootNode = newNode;
status = newNode->InitDriver();
if (status != B_OK)
goto err1;
#if 0
// The following is done to reduce the stack usage of deeply nested
// child device nodes.
@ -1063,8 +1176,11 @@ get_driver(device_node* node, driver_module_info** _module, void** _data)
static device_node*
device_root(void)
get_device_root(void)
{
if (sRootNode != NULL)
sRootNode->Acquire();
return sRootNode;
}
@ -1080,13 +1196,23 @@ get_next_child_device(device_node* parent, device_node* _node,
static device_node*
get_parent(device_node* node)
{
return NULL;
if (node == NULL)
return NULL;
RecursiveLocker _(sLock);
device_node* parent = node->Parent();
parent->Acquire();
return parent;
}
static void
put_device_node(device_node* node)
{
RecursiveLocker _(sLock);
node->Release();
}
@ -1229,7 +1355,7 @@ static struct device_manager_info sDeviceManagerModule = {
register_device,
unregister_device,
get_driver,
device_root,
get_device_root,
get_next_child_device,
get_parent,
put_device_node,
@ -1284,11 +1410,19 @@ main(int argc, char** argv)
{
_add_builtin_module((module_info*)&sDeviceManagerModule);
_add_builtin_module((module_info*)&sDeviceRootModule);
_add_builtin_module((module_info*)&gDeviceModuleInfo);
_add_builtin_module((module_info*)&gDriverModuleInfo);
// 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();
@ -1301,9 +1435,21 @@ main(int argc, char** argv)
recursive_lock_init(&sLock, "device manager");
dm_init_root_node();
dm_dump_node(sRootNode, 0);
sRootNode->Dump();
probe_path("net");
probe_path("graphics");
// TODO: opened devices need to keep a "initialized" reference of the
// device_node
sRootNode->Dump();
uninit_unused();
// add specific video driver - ie. simulate installing it
_add_builtin_module((module_info*)&gSpecificVideoDriverModuleInfo);
_add_builtin_module((module_info*)&gSpecificVideoDeviceModuleInfo);
probe_path("graphics");
uninit_unused();
recursive_lock_destroy(&sLock);

View File

@ -72,7 +72,7 @@ typedef struct device_manager_info {
status_t (*get_driver)(device_node *node, driver_module_info **_module,
void **_cookie);
device_node *(*root_device)();
device_node *(*get_root_device)();
status_t (*get_next_child_device)(device_node *parent, device_node *node,
const device_attr *attrs);
device_node *(*get_parent)(device_node *node);

View File

@ -159,7 +159,7 @@ struct driver_module_info gDriverModuleInfo = {
0,
NULL,
},
supports_device,
register_device,
init_driver,

View File

@ -0,0 +1,182 @@
/*
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "bus.h"
#include <KernelExport.h>
#include <PCI.h>
#define DRIVER_MODULE_NAME "drivers/graphics/generic_driver/driver_v1"
// #pragma mark - driver
static float
supports_device(device_node *parent)
{
bus_for_driver_module_info* module;
void* data;
gDeviceManager->get_driver(parent, (driver_module_info**)&module, &data);
if (strcmp(module->info.info.name, BUS_FOR_DRIVER_NAME))
return -1;
uint16 type;
if (gDeviceManager->get_attr_uint16(parent, B_DEVICE_TYPE, &type, false)
!= B_OK)
return -1;
if (type == PCI_display)
return 0.1;
return 0.0;
}
static status_t
register_device(device_node *parent)
{
return gDeviceManager->register_device(parent, DRIVER_MODULE_NAME, NULL,
NULL, NULL);
}
static status_t
init_driver(device_node *node, void **_cookie)
{
// also publishes any devices/nodes with dedicated calls
return B_OK;
}
static void
uninit_driver(device_node *node)
{
}
static status_t
register_child_devices(device_node *node)
{
return B_OK;
}
static void
device_removed(device_node *node)
{
}
// #pragma mark - device
static status_t
init_device(void *deviceCookie)
{
// called once before one or several open() calls
return B_ERROR;
}
static void
uninit_device(void *deviceCookie)
{
// supposed to free deviceCookie, called when the last reference to
// the device is closed
}
static status_t
device_open(void *deviceCookie, int openMode, void **_cookie)
{
// deviceCookie is an object attached to the published device
return B_ERROR;
}
static status_t
device_close(void *cookie)
{
return B_ERROR;
}
static status_t
device_free(void *cookie)
{
return B_ERROR;
}
static status_t
device_read(void *cookie, off_t pos, void *buffer, size_t *_length)
{
return B_ERROR;
}
static status_t
device_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
{
return B_ERROR;
}
static status_t
device_ioctl(void *cookie, int32 op, void *buffer, size_t length)
{
return B_ERROR;
}
static status_t
device_io(void *cookie, io_request *request)
{
// new function to deal with I/O requests directly.
return B_ERROR;
}
// #pragma mark -
struct driver_module_info gGenericVideoDriverModuleInfo = {
{
DRIVER_MODULE_NAME,
0,
NULL,
},
supports_device,
register_device,
init_driver,
uninit_driver,
register_child_devices,
NULL,
device_removed,
};
struct device_module_info gGenericVideoDeviceModuleInfo = {
{
"drivers/graphics/generic_driver/device_v1",
0,
NULL,
},
init_device,
uninit_device,
device_open,
device_close,
device_free,
device_read,
device_write,
device_ioctl,
device_io,
};

View File

@ -0,0 +1,180 @@
/*
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "bus.h"
#include <KernelExport.h>
#include <PCI.h>
#define DRIVER_MODULE_NAME "drivers/graphics/specific_driver/driver_v1"
// #pragma mark - driver
static float
supports_device(device_node *parent)
{
bus_for_driver_module_info* module;
void* data;
gDeviceManager->get_driver(parent, (driver_module_info**)&module, &data);
if (strcmp(module->info.info.name, BUS_FOR_DRIVER_NAME))
return -1;
bus_info info;
if (module->get_bus_info(data, &info) == B_OK
&& info.vendor_id == 0x1001
&& info.device_id == 0x0002)
return 0.8;
return 0.0;
}
static status_t
register_device(device_node *parent)
{
return gDeviceManager->register_device(parent, DRIVER_MODULE_NAME, NULL,
NULL, NULL);
}
static status_t
init_driver(device_node *node, void **_cookie)
{
// also publishes any devices/nodes with dedicated calls
return B_OK;
}
static void
uninit_driver(device_node *node)
{
}
static status_t
register_child_devices(device_node *node)
{
return B_OK;
}
static void
device_removed(device_node *node)
{
}
// #pragma mark - device
static status_t
init_device(void *deviceCookie)
{
// called once before one or several open() calls
return B_ERROR;
}
static void
uninit_device(void *deviceCookie)
{
// supposed to free deviceCookie, called when the last reference to
// the device is closed
}
static status_t
device_open(void *deviceCookie, int openMode, void **_cookie)
{
// deviceCookie is an object attached to the published device
return B_ERROR;
}
static status_t
device_close(void *cookie)
{
return B_ERROR;
}
static status_t
device_free(void *cookie)
{
return B_ERROR;
}
static status_t
device_read(void *cookie, off_t pos, void *buffer, size_t *_length)
{
return B_ERROR;
}
static status_t
device_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
{
return B_ERROR;
}
static status_t
device_ioctl(void *cookie, int32 op, void *buffer, size_t length)
{
return B_ERROR;
}
static status_t
device_io(void *cookie, io_request *request)
{
// new function to deal with I/O requests directly.
return B_ERROR;
}
// #pragma mark -
struct driver_module_info gSpecificVideoDriverModuleInfo = {
{
DRIVER_MODULE_NAME,
0,
NULL,
},
supports_device,
register_device,
init_driver,
uninit_driver,
register_child_devices,
NULL,
device_removed,
};
struct device_module_info gSpecificVideoDeviceModuleInfo = {
{
"drivers/graphics/specific_driver/device_v1",
0,
NULL,
},
init_device,
uninit_device,
device_open,
device_close,
device_free,
device_read,
device_write,
device_ioctl,
device_io,
};