* Copyright 2008-2009, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include <kdevice_manager.h>
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <KernelExport.h>
#include <Locker.h>
#include <module.h>
#include <PCI.h>
#include <boot_device.h>
#include <device_manager_defs.h>
#include <fs/devfs.h>
#include <fs/KPath.h>
#include <generic_syscall.h>
#include <kernel.h>
#include <kmodule.h>
#include <util/AutoLock.h>
#include <util/DoublyLinkedList.h>
#include <util/Stack.h>
#include "AbstractModuleDevice.h"
#include "devfs_private.h"
#include "id_generator.h"
#include "io_resources.h"
#include "IOSchedulerRoster.h"
#ifdef TRACE_DEVICE_MANAGER
# define TRACE(a) dprintf a
#else
# define TRACE(a) ;
#endif
#define DEVICE_MANAGER_ROOT_NAME "system/devices_root/driver_v1"
#define DEVICE_MANAGER_GENERIC_NAME "system/devices_generic/driver_v1"
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;
typedef struct io_resource_info {
struct io_resource_info *prev, *next;
device_node* owner;
io_resource resource;
} io_resource_info;
namespace {
class Device : public AbstractModuleDevice,
public DoublyLinkedListLinkImpl<Device> {
public:
Device(device_node* node, const char* moduleName);
virtual ~Device();
status_t InitCheck() const;
const char* ModuleName() const { return fModuleName; }
virtual status_t InitDevice();
virtual void UninitDevice();
virtual void Removed();
virtual status_t Control(void* cookie, int32 op, void* buffer, size_t length);
void SetRemovedFromParent(bool removed)
{ fRemovedFromParent = removed; }
private:
const char* fModuleName;
bool fRemovedFromParent;
};
}
typedef DoublyLinkedList<Device> DeviceList;
typedef DoublyLinkedList<device_node> NodeList;
struct device_node : DoublyLinkedListLinkImpl<device_node> {
device_node(const char* moduleName,
const device_attr* attrs);
~device_node();
status_t InitCheck() const;
status_t AcquireResources(const io_resource* resources);
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();
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);
status_t Reprobe();
status_t Rescan();
bool IsRegistered() const { return fRegistered; }
bool IsInitialized() const { return fInitialized > 0; }
bool IsProbed() const { return fLastUpdateCycle != 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;
int32 Priority();
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();
status_t _Probe();
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;
ResourceList fResources;
};
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
};
static device_node *sRootNode;
static recursive_lock sLock;
static const char* sGenericContextPath;
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)
kprintf(" ");
}
static void
dump_attribute(device_attr* attr, int32 level)
{
if (attr == NULL)
return;
put_level(level + 2);
kprintf("\"%s\" : ", attr->name);
switch (attr->type) {
case B_STRING_TYPE:
kprintf("string : \"%s\"", attr->value.string);
break;
case B_INT8_TYPE:
case B_UINT8_TYPE:
kprintf("uint8 : %" B_PRIu8 " (%#" B_PRIx8 ")", attr->value.ui8,
attr->value.ui8);
break;
case B_INT16_TYPE:
case B_UINT16_TYPE:
kprintf("uint16 : %" B_PRIu16 " (%#" B_PRIx16 ")", attr->value.ui16,
attr->value.ui16);
break;
case B_INT32_TYPE:
case B_UINT32_TYPE:
kprintf("uint32 : %" B_PRIu32 " (%#" B_PRIx32 ")", attr->value.ui32,
attr->value.ui32);
break;
case B_INT64_TYPE:
case B_UINT64_TYPE:
kprintf("uint64 : %" B_PRIu64 " (%#" B_PRIx64 ")", attr->value.ui64,
attr->value.ui64);
break;
default:
kprintf("raw data");
}
kprintf("\n");
}
static int
dump_device_nodes(int argc, char** argv)
{
sRootNode->Dump();
return 0;
}
static void
publish_directories(const char* subPath)
{
if (gBootDevice < 0) {
if (subPath[0]) {
return;
}
KPath path("drivers");
if (path.Append(subPath) != B_OK)
return;
size_t length = strlen(path.Path()) + 1;
void* list = open_module_list_etc(path.Path(), "driver_v1");
char name[B_FILE_NAME_LENGTH];
size_t nameLength = sizeof(name);
while (read_next_module_name(list, name, &nameLength) == B_OK) {
if (nameLength == length)
continue;
char* leaf = name + length;
char* end = strchr(leaf, '/');
if (end != NULL)
end[0] = '\0';
path.SetTo(subPath);
path.Append(leaf);
devfs_publish_directory(path.Path());
}
close_module_list(list);
} else {
}
}
static status_t
control_device_manager(const char* subsystem, uint32 function, void* buffer,
size_t bufferSize)
{
switch (function) {
case DM_GET_ROOT:
{
device_node_cookie cookie;
if (!IS_USER_ADDRESS(buffer))
return B_BAD_ADDRESS;
if (bufferSize != sizeof(device_node_cookie))
return B_BAD_VALUE;
cookie = (device_node_cookie)sRootNode;
return user_memcpy(buffer, &cookie, sizeof(device_node_cookie));
}
case DM_GET_CHILD:
{
if (!IS_USER_ADDRESS(buffer))
return B_BAD_ADDRESS;
if (bufferSize != sizeof(device_node_cookie))
return B_BAD_VALUE;
device_node_cookie cookie;
if (user_memcpy(&cookie, buffer, sizeof(device_node_cookie)) < B_OK)
return B_BAD_ADDRESS;
device_node* node = (device_node*)cookie;
NodeList::ConstIterator iterator = node->Children().GetIterator();
if (!iterator.HasNext()) {
return B_ENTRY_NOT_FOUND;
}
node = iterator.Next();
cookie = (device_node_cookie)node;
return user_memcpy(buffer, &cookie, sizeof(device_node_cookie));
}
case DM_GET_NEXT_CHILD:
{
if (!IS_USER_ADDRESS(buffer))
return B_BAD_ADDRESS;
if (bufferSize != sizeof(device_node_cookie))
return B_BAD_VALUE;
device_node_cookie cookie;
if (user_memcpy(&cookie, buffer, sizeof(device_node_cookie)) < B_OK)
return B_BAD_ADDRESS;
device_node* last = (device_node*)cookie;
if (!last->Parent())
return B_ENTRY_NOT_FOUND;
NodeList::ConstIterator iterator
= last->Parent()->Children().GetIterator();
while (iterator.HasNext()) {
device_node* node = iterator.Next();
if (node == last)
break;
}
if (!iterator.HasNext())
return B_ENTRY_NOT_FOUND;
device_node* node = iterator.Next();
cookie = (device_node_cookie)node;
return user_memcpy(buffer, &cookie, sizeof(device_node_cookie));
}
case DM_GET_NEXT_ATTRIBUTE:
{
struct device_attr_info attrInfo;
if (!IS_USER_ADDRESS(buffer))
return B_BAD_ADDRESS;
if (bufferSize != sizeof(device_attr_info))
return B_BAD_VALUE;
if (user_memcpy(&attrInfo, buffer, sizeof(device_attr_info)) < B_OK)
return B_BAD_ADDRESS;
device_node* node = (device_node*)attrInfo.node_cookie;
device_attr* last = (device_attr*)attrInfo.cookie;
AttributeList::Iterator iterator = node->Attributes().GetIterator();
while (iterator.HasNext() && last != NULL) {
device_attr* attr = iterator.Next();
if (attr == last)
break;
}
if (!iterator.HasNext()) {
attrInfo.cookie = 0;
return B_ENTRY_NOT_FOUND;
}
device_attr* attr = iterator.Next();
attrInfo.cookie = (device_node_cookie)attr;
if (attr->name != NULL)
strlcpy(attrInfo.name, attr->name, 254);
else
attrInfo.name[0] = '\0';
attrInfo.type = attr->type;
switch (attrInfo.type) {
case B_UINT8_TYPE:
attrInfo.value.ui8 = attr->value.ui8;
break;
case B_UINT16_TYPE:
attrInfo.value.ui16 = attr->value.ui16;
break;
case B_UINT32_TYPE:
attrInfo.value.ui32 = attr->value.ui32;
break;
case B_UINT64_TYPE:
attrInfo.value.ui64 = attr->value.ui64;
break;
case B_STRING_TYPE:
if (attr->value.string != NULL)
strlcpy(attrInfo.value.string, attr->value.string, 254);
else
attrInfo.value.string[0] = '\0';
break;
if (attr.value.raw.length > attr_info->attr.value.raw.length)
attr.value.raw.length = attr_info->attr.value.raw.length;
user_memcpy(attr.value.raw.data, attr_info->attr.value.raw.data,
attr.value.raw.length);
break;*/
}
return user_memcpy(buffer, &attrInfo, sizeof(device_attr_info));
}
}
return B_BAD_HANDLER;
}
static status_t
rescan_node(device_node* node)
{
RecursiveLocker _(sLock);
return node->Rescan();
}
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) {
return B_NAME_IN_USE;
}
RecursiveLocker _(sLock);
device_node* newNode = new(std::nothrow) device_node(moduleName, attrs);
if (newNode == NULL)
return B_NO_MEMORY;
TRACE(("%p: register node \"%s\", parent %p\n", newNode, moduleName,
parent));
status_t status = newNode->InitCheck();
if (status == B_OK)
status = newNode->AcquireResources(ioResources);
if (status == B_OK)
status = newNode->Register(parent);
if (status != B_OK) {
newNode->Release();
return status;
}
if (_node)
*_node = newNode;
return B_OK;
}
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;
while (iterator.HasNext() && last != NULL) {
device_node* node = iterator.Next();
if (node != last)
continue;
}
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, moduleName);
if (device == NULL)
return B_NO_MEMORY;
status_t status = device->InitCheck();
if (status == B_OK)
status = devfs_publish_device(path, device);
if (status != B_OK) {
delete device;
return status;
}
node->AddDevice(device);
device_attr_private* attr;
attr = new(std::nothrow) device_attr_private();
if (attr != NULL) {
char buf[256];
sprintf(buf, "dev/%" B_PRIdINO "/path", device->ID());
attr->name = strdup(buf);
attr->type = B_STRING_TYPE;
attr->value.string = strdup(path);
node->Attributes().Add(attr);
}
attr = new(std::nothrow) device_attr_private();
if (attr != NULL) {
char buf[256];
sprintf(buf, "dev/%" B_PRIdINO "/driver", device->ID());
attr->name = strdup(buf);
attr->type = B_STRING_TYPE;
attr->value.string = strdup(moduleName);
node->Attributes().Add(attr);
}
return B_OK;
}
static status_t
unpublish_device(device_node *node, const char *path)
{
if (path == NULL)
return B_BAD_VALUE;
BaseDevice* baseDevice;
status_t error = devfs_get_device(path, baseDevice);
if (error != B_OK)
return error;
CObjectDeleter<BaseDevice, void, devfs_put_device>
baseDevicePutter(baseDevice);
Device* device = dynamic_cast<Device*>(baseDevice);
if (device == NULL || device->Node() != node)
return B_BAD_VALUE;
return devfs_unpublish_device(device, true);
}
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 = attr->GetDoublyLinkedListLink()->next;
} else {
next = node->Attributes().First();
}
*_attr = next;
return next ? B_OK : B_ENTRY_NOT_FOUND;
}
static status_t
find_child_node(device_node* parent, const device_attr* attributes,
device_node** _node, bool *_lastFound)
{
RecursiveLocker _(sLock);
NodeList::ConstIterator iterator = parent->Children().GetIterator();
device_node* last = *_node;
while (iterator.HasNext()) {
device_node* node = iterator.Next();
if (!node->IsRegistered())
continue;
if (node == last)
*_lastFound = true;
else if (!node->CompareTo(attributes) && *_lastFound) {
if (last != NULL)
last->Release();
node->Acquire();
*_node = node;
return B_OK;
}
if (find_child_node(node, attributes, _node, _lastFound) == B_OK)
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
static status_t
find_child_node(device_node* parent, const device_attr* attributes,
device_node** _node)
{
device_node* last = *_node;
bool lastFound = last == NULL;
status_t status = find_child_node(parent, attributes, _node, &lastFound);
if (status == B_ENTRY_NOT_FOUND && last != NULL && lastFound)
last->Release();
return status;
}
struct device_manager_info gDeviceManagerModule = {
{
B_DEVICE_MANAGER_MODULE_NAME,
0,
NULL
},
rescan_node,
register_node,
unregister_node,
get_driver,
get_root_node,
get_next_child_node,
get_parent_node,
put_node,
publish_device,
unpublish_device,
dm_create_id,
dm_free_id,
get_attr_uint8,
get_attr_uint16,
get_attr_uint32,
get_attr_uint64,
get_attr_string,
get_attr_raw,
get_next_attr,
find_child_node
};
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;
}
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;
}
Device::Device(device_node* node, const char* moduleName)
:
fModuleName(strdup(moduleName)),
fRemovedFromParent(false)
{
fNode = node;
}
Device::~Device()
{
free((char*)fModuleName);
}
status_t
Device::InitCheck() const
{
return fModuleName != NULL ? B_OK : B_NO_MEMORY;
}
status_t
Device::InitDevice()
{
RecursiveLocker _(sLock);
if ((fNode->Flags() & NODE_FLAG_DEVICE_REMOVED) != 0) {
return ENODEV;
}
if ((fNode->Flags() & NODE_FLAG_WAITING_FOR_DRIVER) != 0)
return B_BUSY;
if (fInitialized++ > 0) {
fNode->InitDriver();
return B_OK;
}
status_t status = get_module(ModuleName(), (module_info**)&fDeviceModule);
if (status == B_OK) {
status = fNode->InitDriver();
}
if (status < B_OK) {
fInitialized--;
return status;
}
if (Module()->init_device != NULL)
status = Module()->init_device(fNode->DriverData(), &fDeviceData);
if (status < B_OK) {
fNode->UninitDriver();
fInitialized--;
put_module(ModuleName());
fDeviceModule = NULL;
fDeviceData = NULL;
}
return status;
}
void
Device::UninitDevice()
{
RecursiveLocker _(sLock);
if (fInitialized-- > 1) {
fNode->UninitDriver();
return;
}
TRACE(("uninit driver for node %p\n", this));
if (Module()->uninit_device != NULL)
Module()->uninit_device(fDeviceData);
fDeviceModule = NULL;
fDeviceData = NULL;
put_module(ModuleName());
fNode->UninitDriver();
}
void
Device::Removed()
{
RecursiveLocker _(sLock);
if (!fRemovedFromParent)
fNode->RemoveDevice(this);
delete this;
}
status_t
Device::Control(void* _cookie, int32 op, void* buffer, size_t length)
{
switch (op) {
case B_GET_DRIVER_FOR_DEVICE:
{
char* path = NULL;
status_t status = module_get_path(ModuleName(), &path);
if (status != B_OK)
return status;
if (length != 0 && length <= strlen(path))
return ERANGE;
status = user_strlcpy(static_cast<char*>(buffer), path, length);
free(path);
return status;
}
default:
return AbstractModuleDevice::Control(_cookie, op, buffer, length);;
}
}
device_node::device_node(const char* moduleName, const device_attr* attrs)
{
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;
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++;
}
device_attr_private* attr = new(std::nothrow) device_attr_private();
if (attr != NULL) {
attr->name = strdup("device/driver");
attr->type = B_STRING_TYPE;
attr->value.string = strdup(fModuleName);
fAttributes.Add(attr);
}
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) {
Parent()->_ReleaseWaiting();
}
Parent()->RemoveChild(this);
}
while (device_node* child = fChildren.RemoveHead()) {
delete child;
}
while (Device* device = fDevices.RemoveHead()) {
device->SetRemovedFromParent(true);
devfs_unpublish_device(device, true);
}
while (device_attr_private* attr = fAttributes.RemoveHead()) {
delete attr;
}
while (io_resource_private* resource = fResources.RemoveHead()) {
delete resource;
}
free((char*)fModuleName);
}
status_t
device_node::InitCheck() const
{
return fModuleName != NULL ? B_OK : B_NO_MEMORY;
}
status_t
device_node::AcquireResources(const io_resource* resources)
{
if (resources == NULL)
return B_OK;
for (uint32 i = 0; resources[i].type != 0; i++) {
io_resource_private* resource = new(std::nothrow) io_resource_private;
if (resource == NULL)
return B_NO_MEMORY;
status_t status = resource->Acquire(resources[i]);
if (status != B_OK) {
delete resource;
return status;
}
fResources.Add(resource);
}
return B_OK;
}
status_t
device_node::InitDriver()
{
if (fInitialized++ > 0) {
if (Parent() != NULL) {
Parent()->InitDriver();
}
Acquire();
return B_OK;
}
status_t status = get_module(ModuleName(), (module_info**)&fDriver);
if (status == B_OK && Parent() != NULL) {
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) {
dprintf("driver %s init failed: %s\n", ModuleName(),
strerror(status));
}
}
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)
{
Acquire();
node->fParent = this;
int32 priority = node->Priority();
NodeList::Iterator iterator = fChildren.GetIterator();
device_node* before = NULL;
while (iterator.HasNext()) {
device_node* child = iterator.Next();
if (child->Priority() < priority) {
before = child;
break;
}
}
fChildren.InsertBefore(before, node);
}
void
device_node::RemoveChild(device_node* node)
{
node->fParent = NULL;
fChildren.Remove(node);
Release();
}
Also initializes the driver and keeps it that way on return in case
it returns successfully.
*/
status_t
device_node::Register(device_node* parent)
{
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) {
InitDriver();
}
fFlags |= NODE_FLAG_REGISTER_INITIALIZED;
uint32 registeredFixedCount;
status = _RegisterFixed(registeredFixedCount);
if (status != B_OK) {
UninitUnusedDriver();
return status;
}
if (DriverModule()->register_child_devices != NULL) {
status = DriverModule()->register_child_devices(DriverData());
if (status != B_OK) {
UninitUnusedDriver();
return status;
}
if (!fChildren.IsEmpty()) {
fRegistered = true;
return B_OK;
}
}
if (registeredFixedCount > 0) {
fRegistered = true;
return B_OK;
}
status = _RegisterDynamic();
if (status == B_OK)
fRegistered = true;
else
UninitUnusedDriver();
return status;
}
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) {
TRACE(("register fixed child %s failed: %s\n", attr->value.string,
strerror(status)));
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);
TRACE((" add path: \"%s\", %" B_PRId32 "\n", path->Path(), status));
if (status != B_OK)
delete path;
return status;
}
status_t
device_node::_GetNextDriverPath(void*& cookie, KPath& _path)
{
Stack<KPath*>* stack = NULL;
if (cookie == NULL) {
stack = new(std::nothrow) Stack<KPath*>();
if (stack == NULL)
return B_NO_MEMORY;
StackDeleter<KPath*> stackDeleter(stack);
bool generic = false;
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)
generic = true;
switch (type) {
case PCI_mass_storage:
switch (subType) {
case PCI_scsi:
_AddPath(*stack, "busses", "scsi");
_AddPath(*stack, "busses", "virtio");
break;
case PCI_ide:
_AddPath(*stack, "busses", "ata");
_AddPath(*stack, "busses", "ide");
break;
case PCI_sata:
_AddPath(*stack, "busses", "scsi");
_AddPath(*stack, "busses", "ata");
_AddPath(*stack, "busses", "ide");
break;
case PCI_nvm:
_AddPath(*stack, "drivers", "disk");
break;
default:
_AddPath(*stack, "busses");
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");
_AddPath(*stack, "busses", "virtio");
break;
case PCI_display:
_AddPath(*stack, "drivers", "graphics");
_AddPath(*stack, "busses", "virtio");
break;
case PCI_multimedia:
switch (subType) {
case PCI_audio:
case PCI_hd_audio:
_AddPath(*stack, "drivers", "audio");
_AddPath(*stack, "busses", "virtio");
break;
case PCI_video:
_AddPath(*stack, "drivers", "video");
break;
default:
_AddPath(*stack, "drivers");
break;
}
break;
case PCI_base_peripheral:
switch (subType) {
case PCI_sd_host:
_AddPath(*stack, "busses", "mmc");
break;
case PCI_system_peripheral_other:
_AddPath(*stack, "busses", "mmc");
_AddPath(*stack, "drivers");
break;
default:
_AddPath(*stack, "drivers");
break;
}
break;
case PCI_encryption_decryption:
switch (subType) {
case PCI_encryption_decryption_other:
_AddPath(*stack, "busses", "random");
break;
default:
_AddPath(*stack, "drivers");
break;
}
break;
case PCI_data_acquisition:
switch (subType) {
case PCI_data_acquisition_other:
_AddPath(*stack, "busses", "i2c");
_AddPath(*stack, "drivers");
break;
default:
_AddPath(*stack, "drivers");
break;
}
break;
default:
if (sRootNode == this) {
_AddPath(*stack, "busses/pci");
_AddPath(*stack, "bus_managers");
} else if (!generic) {
_AddPath(*stack, "drivers");
_AddPath(*stack, "busses/virtio");
} else {
if (sGenericContextPath != NULL
&& (!strcmp(sGenericContextPath, "disk")
|| !strcmp(sGenericContextPath, "ports")
|| !strcmp(sGenericContextPath, "bus"))) {
_AddPath(*stack, "busses");
}
const char* bus;
if (get_attr_string(this, B_DEVICE_BUS, &bus, false) == B_OK) {
if (strcmp(bus, "virtio") == 0)
_AddPath(*stack, "busses/scsi");
}
_AddPath(*stack, "drivers", sGenericContextPath);
_AddPath(*stack, "busses/i2c");
_AddPath(*stack, "busses/random");
_AddPath(*stack, "busses/virtio");
_AddPath(*stack, "bus_managers/pci");
_AddPath(*stack, "busses/pci");
_AddPath(*stack, "busses/mmc");
}
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;
}
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);
switch (type) {
case PCI_serial_bus:
case PCI_bridge:
case PCI_encryption_decryption:
case 0:
return true;
}
return false;
}
status_t
device_node::_RegisterDynamic(device_node* previous)
{
if (find_attr(this, B_DEVICE_BUS, false, B_STRING_TYPE) == NULL)
return B_OK;
if (!IsProbed() && (fFlags & B_FIND_CHILD_ON_DEMAND) != 0
&& !_AlwaysRegisterDynamic())
return B_OK;
KPath path;
if ((fFlags & B_FIND_MULTIPLE_CHILDREN) == 0) {
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) {
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;
}
}
}
put_module(bestDriver->info.name);
}
} else {
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()
{
device_node* previous = NULL;
if (IsProbed() && !fChildren.IsEmpty()
&& (fFlags & (B_FIND_CHILD_ON_DEMAND | B_FIND_MULTIPLE_CHILDREN))
== B_FIND_CHILD_ON_DEMAND) {
_RemoveChildren();
previous = _FindCurrentChild();
if (previous != NULL) {
previous->Acquire();
}
}
return _RegisterDynamic(previous);
}
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, &device_node::UninitDriver> uninit(this);
if ((fFlags & B_FIND_CHILD_ON_DEMAND) != 0) {
bool matches = false;
uint16 type = 0;
uint16 subType = 0;
if (get_attr_uint16(this, B_DEVICE_SUB_TYPE, &subType, false) == B_OK
&& get_attr_uint16(this, B_DEVICE_TYPE, &type, false) == B_OK) {
if (!strcmp(devicePath, "disk")) {
matches = type == PCI_mass_storage
|| (type == PCI_base_peripheral
&& (subType == PCI_sd_host
|| subType == PCI_system_peripheral_other));
} 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;
} else if (!strcmp(devicePath, "power")) {
matches = type == PCI_data_acquisition;
} else if (!strcmp(devicePath, "input")) {
matches = type == PCI_data_acquisition
&& subType == PCI_data_acquisition_other;
}
} else {
matches = true;
sGenericContextPath = devicePath;
}
if (matches) {
fLastUpdateCycle = updateCycle;
status = _Probe();
sGenericContextPath = NULL;
return status;
}
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;
}
status_t
device_node::Reprobe()
{
status_t status = InitDriver();
if (status < B_OK)
return status;
MethodDeleter<device_node, bool, &device_node::UninitDriver> uninit(this);
status = _Probe();
if (status != B_OK)
return status;
NodeList::Iterator iterator = fChildren.GetIterator();
while (iterator.HasNext()) {
device_node* child = iterator.Next();
status = child->Reprobe();
if (status != B_OK)
return status;
}
return B_OK;
}
status_t
device_node::Rescan()
{
status_t status = InitDriver();
if (status < B_OK)
return status;
MethodDeleter<device_node, bool, &device_node::UninitDriver> uninit(this);
if (DriverModule()->rescan_child_devices != NULL) {
status = DriverModule()->rescan_child_devices(DriverData());
if (status != B_OK)
return status;
}
NodeList::Iterator iterator = fChildren.GetIterator();
while (iterator.HasNext()) {
device_node* child = iterator.Next();
status = child->Rescan();
if (status != B_OK)
return status;
}
return B_OK;
}
process keeps the driver initialized to optimize the startup procedure;
this function gives this reference away again.
*/
void
device_node::UninitUnusedDriver()
{
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();
}
with the deepest and last child.
It will also remove the one reference that every node gets on its creation.
*/
void
device_node::DeviceRemoved()
{
NodeList::ConstIterator iterator = Children().GetIterator();
while (iterator.HasNext()) {
device_node* child = iterator.Next();
child->DeviceRemoved();
}
DeviceList::ConstIterator deviceIterator = Devices().GetIterator();
while (deviceIterator.HasNext()) {
Device* device = deviceIterator.Next();
if (device->Module() != NULL
&& device->Module()->device_removed != NULL)
device->Module()->device_removed(device->Data());
}
fFlags |= NODE_FLAG_DEVICE_REMOVED;
if (IsInitialized() && DriverModule()->device_removed != NULL)
DriverModule()->device_removed(this);
if ((fFlags & B_KEEP_DRIVER_LOADED) != 0) {
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)
{
char attrName[256];
device_attr_private* attr;
sprintf(attrName, "dev/%" B_PRIdINO "/path", device->ID());
attr = find_attr(this, attrName, false, B_STRING_TYPE);
if (attr != NULL) {
fAttributes.Remove(attr);
delete attr;
}
sprintf(attrName, "dev/%" B_PRIdINO "/driver", device->ID());
attr = find_attr(this, attrName, false, B_STRING_TYPE);
if (attr != NULL) {
fAttributes.Remove(attr);
delete attr;
}
fDevices.Remove(device);
}
int
device_node::CompareTo(const device_attr* attributes) const
{
if (attributes == NULL)
return -1;
for (; attributes->name != NULL; attributes++) {
AttributeList::ConstIterator iterator = Attributes().GetIterator();
device_attr_private* attr = NULL;
bool found = false;
while (iterator.HasNext()) {
attr = iterator.Next();
if (!strcmp(attr->name, attributes->name)) {
found = true;
break;
}
}
if (!found)
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->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;
}
priority are registered/probed first.
Currently, only the B_FIND_MULTIPLE_CHILDREN flag alters the priority;
it might make sense to be able to directly set the priority via an
attribute.
*/
int32
device_node::Priority()
{
return (fFlags & B_FIND_MULTIPLE_CHILDREN) != 0 ? 0 : 100;
}
void
device_node::Dump(int32 level)
{
put_level(level);
kprintf("(%" B_PRId32 ") @%p \"%s\" (ref %" B_PRId32 ", init %" B_PRId32
", module %p, data %p)\n", level, this, ModuleName(), fRefCount,
fInitialized, DriverModule(), DriverData());
AttributeList::Iterator attribute = Attributes().GetIterator();
while (attribute.HasNext()) {
dump_attribute(attribute.Next(), level);
}
DeviceList::Iterator deviceIterator = fDevices.GetIterator();
while (deviceIterator.HasNext()) {
Device* device = deviceIterator.Next();
put_level(level);
kprintf("device: %s, %p\n", device->ModuleName(), device->Data());
}
NodeList::ConstIterator iterator = Children().GetIterator();
while (iterator.HasNext()) {
iterator.Next()->Dump(level + 1);
}
}
static void
init_node_tree(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}
};
device_node* node = NULL;
if (register_node(NULL, DEVICE_MANAGER_ROOT_NAME, attrs, NULL, &node)
!= B_OK) {
dprintf("Cannot register Devices Root Node\n");
}
device_attr genericAttrs[] = {
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "Generic"}},
{B_DEVICE_BUS, B_STRING_TYPE, {.string = "generic"}},
{B_DEVICE_FLAGS, B_UINT32_TYPE, {.ui32 = B_FIND_MULTIPLE_CHILDREN
| B_KEEP_DRIVER_LOADED | B_FIND_CHILD_ON_DEMAND}},
{NULL}
};
if (register_node(node, DEVICE_MANAGER_GENERIC_NAME, genericAttrs, NULL,
NULL) != B_OK) {
dprintf("Cannot register Generic Devices Node\n");
}
}
driver_module_info gDeviceRootModule = {
{
DEVICE_MANAGER_ROOT_NAME,
0,
NULL,
},
};
driver_module_info gDeviceGenericModule = {
{
DEVICE_MANAGER_GENERIC_NAME,
0,
NULL,
},
NULL
};
status_t
device_manager_probe(const char* path, uint32 updateCycle)
{
TRACE(("device_manager_probe(\"%s\")\n", path));
RecursiveLocker _(sLock);
publish_directories(path);
return sRootNode->Probe(path, updateCycle);
}
status_t
device_manager_init(struct kernel_args* args)
{
TRACE(("device manager init\n"));
IOSchedulerRoster::Init();
dm_init_id_generator();
dm_init_io_resources();
recursive_lock_init(&sLock, "device manager");
register_generic_syscall(DEVICE_MANAGER_SYSCALLS, control_device_manager,
1, 0);
add_debugger_command("dm_tree", &dump_device_nodes,
"dump device node tree");
init_node_tree();
return B_OK;
}
status_t
device_manager_init_post_modules(struct kernel_args* args)
{
RecursiveLocker _(sLock);
return sRootNode->Reprobe();
}
recursive_lock*
device_manager_get_lock()
{
return &sLock;
}