
writer) I/O requests. Not used yet. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26966 a95241bf-73f2-0310-859d-f6bbb57e9c96
2243 lines
48 KiB
C++
2243 lines
48 KiB
C++
/*
|
|
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include <kdevice_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 <boot_device.h>
|
|
#include <device_manager_defs.h>
|
|
#include <fs/devfs.h>
|
|
#include <fs/KPath.h>
|
|
#include <kernel.h>
|
|
#include <generic_syscall.h>
|
|
#include <util/AutoLock.h>
|
|
#include <util/DoublyLinkedList.h>
|
|
#include <util/Stack.h>
|
|
|
|
#include "BaseDevice.h"
|
|
#include "devfs_private.h"
|
|
#include "id_generator.h"
|
|
#include "io_requests.h"
|
|
#include "io_resources.h"
|
|
|
|
|
|
//#define TRACE_DEVICE_MANAGER
|
|
#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;
|
|
|
|
// 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 BaseDevice, 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();
|
|
|
|
private:
|
|
const char* fModuleName;
|
|
};
|
|
|
|
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();
|
|
|
|
// 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);
|
|
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;
|
|
};
|
|
|
|
// 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
|
|
};
|
|
|
|
|
|
static device_node *sRootNode;
|
|
static recursive_lock sLock;
|
|
static const char* sGenericContextPath;
|
|
|
|
|
|
// #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 int
|
|
dump_io_request(int argc, char** argv)
|
|
{
|
|
if (argc != 2 || !strcmp(argv[1], "--help")) {
|
|
kprintf("usage: %s <ptr-to-io-request>\n", argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
IORequest* request = (IORequest*)parse_expression(argv[1]);
|
|
request->Dump();
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
dump_io_operation(int argc, char** argv)
|
|
{
|
|
if (argc != 2 || !strcmp(argv[1], "--help")) {
|
|
kprintf("usage: %s <ptr-to-io-operation>\n", argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
IOOperation* operation = (IOOperation*)parse_expression(argv[1]);
|
|
operation->Dump();
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
dump_io_buffer(int argc, char** argv)
|
|
{
|
|
if (argc != 2 || !strcmp(argv[1], "--help")) {
|
|
kprintf("usage: %s <ptr-to-io-buffer>\n", argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
IOBuffer* buffer = (IOBuffer*)parse_expression(argv[1]);
|
|
buffer->Dump();
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
dump_dma_buffer(int argc, char** argv)
|
|
{
|
|
if (argc != 2 || !strcmp(argv[1], "--help")) {
|
|
kprintf("usage: %s <ptr-to-dma-buffer>\n", argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
DMABuffer* buffer = (DMABuffer*)parse_expression(argv[1]);
|
|
buffer->Dump();
|
|
return 0;
|
|
}
|
|
|
|
|
|
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]) {
|
|
// we only support the top-level directory for modules
|
|
return;
|
|
}
|
|
|
|
// we can only iterate over the known modules to find all directories
|
|
KPath path("drivers");
|
|
if (path.Append(subPath) != B_OK)
|
|
return;
|
|
|
|
size_t length = strlen(path.Path()) + 1;
|
|
// account for the separating '/'
|
|
|
|
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 {
|
|
// TODO: implement module directory traversal!
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
// copy back to user space
|
|
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;
|
|
|
|
// copy back to user space
|
|
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();
|
|
|
|
// skip those we already traversed
|
|
while (iterator.HasNext() && last != NULL) {
|
|
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;
|
|
|
|
// copy back to user space
|
|
return user_memcpy(buffer, &cookie, sizeof(device_node_cookie));
|
|
}
|
|
|
|
case DM_GET_NEXT_ATTRIBUTE:
|
|
{
|
|
struct device_attr_info attr_info;
|
|
if (!IS_USER_ADDRESS(buffer))
|
|
return B_BAD_ADDRESS;
|
|
if (bufferSize < sizeof(struct device_attr_info))
|
|
return B_BAD_VALUE;
|
|
if (user_memcpy(&attr_info, buffer, sizeof(struct device_attr_info)) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
device_node* node = (device_node*)attr_info.node_cookie;
|
|
device_attr* last = (device_attr*)attr_info.cookie;
|
|
AttributeList::Iterator iterator = node->Attributes().GetIterator();
|
|
// skip those we already traversed
|
|
while (iterator.HasNext() && last != NULL) {
|
|
device_attr* attr = iterator.Next();
|
|
|
|
if (attr == last)
|
|
break;
|
|
}
|
|
|
|
if (!iterator.HasNext()) {
|
|
attr_info.cookie = 0;
|
|
return B_ENTRY_NOT_FOUND;
|
|
}
|
|
|
|
device_attr* attr = iterator.Next();
|
|
attr_info.cookie = (device_node_cookie)attr;
|
|
strlcpy(attr_info.name, attr->name, 254);
|
|
attr_info.type = attr->type;
|
|
switch (attr_info.type) {
|
|
case B_UINT8_TYPE:
|
|
attr_info.value.ui8 = attr->value.ui8; break;
|
|
case B_UINT16_TYPE:
|
|
attr_info.value.ui16 = attr->value.ui16; break;
|
|
case B_UINT32_TYPE:
|
|
attr_info.value.ui32 = attr->value.ui32; break;
|
|
case B_UINT64_TYPE:
|
|
attr_info.value.ui64 = attr->value.ui64; break;
|
|
case B_STRING_TYPE:
|
|
strlcpy(attr_info.value.string, attr->value.string, 254); break;
|
|
/*case B_RAW_TYPE:
|
|
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;*/
|
|
}
|
|
|
|
// copy back to user space
|
|
return user_memcpy(buffer, &attr_info, sizeof(struct device_attr_info));
|
|
}
|
|
}
|
|
|
|
return B_BAD_HANDLER;
|
|
}
|
|
|
|
|
|
// #pragma mark - Device Manager module API
|
|
|
|
|
|
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) {
|
|
// A node like this one already exists for this parent
|
|
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;
|
|
}
|
|
|
|
|
|
/*! 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, 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);
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
static status_t
|
|
unpublish_device(device_node *node, const char *path)
|
|
{
|
|
if (path == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
RecursiveLocker _(sLock);
|
|
|
|
#if 0
|
|
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;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
struct device_manager_info gDeviceManagerModule = {
|
|
{
|
|
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,
|
|
|
|
// I/O resources
|
|
|
|
// ID generator
|
|
dm_create_id,
|
|
dm_free_id,
|
|
|
|
// 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* moduleName)
|
|
{
|
|
fNode = node;
|
|
fModuleName = strdup(moduleName);
|
|
}
|
|
|
|
|
|
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) {
|
|
// 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 (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();
|
|
}
|
|
|
|
|
|
// #pragma mark - device_node
|
|
|
|
|
|
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;
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Delete resources
|
|
ResourceList::Iterator resourceIterator = fResources.GetIterator();
|
|
while (resourceIterator.HasNext()) {
|
|
io_resource_private* resource = resourceIterator.Next();
|
|
resourceIterator.Remove();
|
|
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 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) {
|
|
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)
|
|
{
|
|
// we must not be destroyed as long as we have children
|
|
Acquire();
|
|
node->fParent = this;
|
|
|
|
int32 priority = node->Priority();
|
|
|
|
// Enforce an order in which the children are traversed - from most
|
|
// specific to least specific child.
|
|
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.Insert(before, 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 registeredFixedCount;
|
|
status = _RegisterFixed(registeredFixedCount);
|
|
if (status != B_OK) {
|
|
UninitUnusedDriver();
|
|
return status;
|
|
}
|
|
|
|
// Register the children the driver wants
|
|
|
|
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) {
|
|
// Nodes with fixed children cannot have any dynamic children, so bail
|
|
// out here
|
|
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_DEVICE_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) {
|
|
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\", %ld\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) {
|
|
// find all paths and add them
|
|
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;
|
|
uint16 interface = 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;
|
|
|
|
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:
|
|
// TODO: check for ahci interface
|
|
_AddPath(*stack, "busses", "scsi");
|
|
_AddPath(*stack, "busses", "ide");
|
|
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");
|
|
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 if (!generic) {
|
|
_AddPath(*stack, "drivers");
|
|
} else {
|
|
// For generic drivers, we only allow busses when the
|
|
// request is more specified
|
|
if (sGenericContextPath != NULL
|
|
&& (!strcmp(sGenericContextPath, "disk")
|
|
|| !strcmp(sGenericContextPath, "ports")
|
|
|| !strcmp(sGenericContextPath, "bus"))) {
|
|
_AddPath(*stack, "busses");
|
|
}
|
|
_AddPath(*stack, "drivers", sGenericContextPath);
|
|
}
|
|
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 not a bus, we don't have to scan it
|
|
if (find_attr(this, B_DEVICE_BUS, false, B_STRING_TYPE) == NULL)
|
|
return B_OK;
|
|
|
|
// If we're not being probed, we honour the B_FIND_CHILD_ON_DEMAND
|
|
// requirements
|
|
if (!IsProbed() && (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()
|
|
{
|
|
device_node* previous = NULL;
|
|
|
|
if (IsProbed() && !fChildren.IsEmpty()
|
|
&& (fFlags & (B_FIND_CHILD_ON_DEMAND | B_FIND_MULTIPLE_CHILDREN))
|
|
== B_FIND_CHILD_ON_DEMAND) {
|
|
// 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);
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
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) {
|
|
// Check if this node matches the device path
|
|
// TODO: maybe make this extendible via settings file?
|
|
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;
|
|
}
|
|
} else {
|
|
// This driver does not support types, but still wants to its
|
|
// children explored on demand only.
|
|
matches = true;
|
|
sGenericContextPath = devicePath;
|
|
}
|
|
|
|
if (matches) {
|
|
fLastUpdateCycle = updateCycle;
|
|
// This node will be probed in this update cycle
|
|
|
|
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> uninit(this,
|
|
&device_node::UninitDriver);
|
|
|
|
// If this child has been probed already, probe it again
|
|
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> uninit(this,
|
|
&device_node::UninitDriver);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/*! 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->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) {
|
|
// 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;
|
|
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();
|
|
|
|
// 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;
|
|
}
|
|
|
|
|
|
/*! This returns the priority or importance of this node. Nodes with higher
|
|
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);
|
|
dprintf("(%ld) @%p \"%s\" (ref %ld, init %ld, 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);
|
|
dprintf("device: %s, %p\n", device->ModuleName(), device->Data());
|
|
}
|
|
|
|
NodeList::ConstIterator iterator = Children().GetIterator();
|
|
while (iterator.HasNext()) {
|
|
iterator.Next()->Dump(level + 1);
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark - root node
|
|
|
|
|
|
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;
|
|
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
|
|
};
|
|
|
|
|
|
// #pragma mark - private kernel API
|
|
|
|
|
|
status_t
|
|
device_manager_probe(const char* path, uint32 updateCycle)
|
|
{
|
|
TRACE(("device_manager_probe(\"%s\")\n", path));
|
|
RecursiveLocker _(sLock);
|
|
|
|
// first, publish directories in the driver directory
|
|
publish_directories(path);
|
|
|
|
return sRootNode->Probe(path, updateCycle);
|
|
}
|
|
|
|
|
|
status_t
|
|
device_manager_init(struct kernel_args* args)
|
|
{
|
|
TRACE(("device manager init\n"));
|
|
|
|
vip_io_request_allocator_init();
|
|
|
|
dm_init_id_generator();
|
|
dm_init_io_resources();
|
|
|
|
recursive_lock_init(&sLock, "device manager");
|
|
init_node_tree();
|
|
|
|
#ifdef TRACE_DEVICE_MANAGER
|
|
sRootNode->Dump();
|
|
#endif
|
|
|
|
register_generic_syscall(DEVICE_MANAGER_SYSCALLS, control_device_manager,
|
|
1, 0);
|
|
|
|
add_debugger_command("dm_tree", &dump_device_nodes,
|
|
"dump device node tree");
|
|
add_debugger_command("io_request", &dump_io_request, "dump an I/O request");
|
|
add_debugger_command("io_operation", &dump_io_operation,
|
|
"dump an I/O operation");
|
|
add_debugger_command("io_buffer", &dump_io_buffer, "dump an I/O buffer");
|
|
add_debugger_command("dma_buffer", &dump_dma_buffer, "dump a DMA buffer");
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
device_manager_init_post_modules(struct kernel_args* args)
|
|
{
|
|
RecursiveLocker _(sLock);
|
|
return sRootNode->Reprobe();
|
|
}
|
|
|