Forgot to remove now unused files.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25664 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
187c576425
commit
34d976b499
@ -1,532 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Device Manager
|
||||
Node attributes handling.
|
||||
*/
|
||||
|
||||
|
||||
#include "device_manager_private.h"
|
||||
#include "dl_list.h"
|
||||
|
||||
#include <TypeConstants.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static bool is_fixed_attribute(const char *name);
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_uint8(device_node_handle node, const char *name,
|
||||
uint8 *value, bool recursive)
|
||||
{
|
||||
status_t status;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
status = pnp_get_attr_uint8_nolock(node, name, value, recursive);
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_uint8_nolock(device_node_handle node, const char *name,
|
||||
uint8 *value, bool recursive)
|
||||
{
|
||||
device_attr_info *attr = pnp_find_attr_nolock(node, name, recursive, B_UINT8_TYPE);
|
||||
if (attr == NULL)
|
||||
return B_NAME_NOT_FOUND;
|
||||
|
||||
*value = attr->attr.value.ui8;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_uint16(device_node_handle node, const char *name,
|
||||
uint16 *value, bool recursive)
|
||||
{
|
||||
status_t status;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
status = pnp_get_attr_uint16_nolock(node, name, value, recursive);
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_uint16_nolock(device_node_handle node, const char *name,
|
||||
uint16 *value, bool recursive)
|
||||
{
|
||||
device_attr_info *attr = pnp_find_attr_nolock(node, name, recursive, B_UINT16_TYPE);
|
||||
if (attr == NULL)
|
||||
return B_NAME_NOT_FOUND;
|
||||
|
||||
*value = attr->attr.value.ui16;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_uint32(device_node_handle node, const char *name,
|
||||
uint32 *value, bool recursive)
|
||||
{
|
||||
status_t status;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
status = pnp_get_attr_uint32_nolock(node, name, value, recursive);
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_uint32_nolock(device_node_handle node, const char *name,
|
||||
uint32 *value, bool recursive)
|
||||
{
|
||||
device_attr_info *attr = pnp_find_attr_nolock(node, name, recursive, B_UINT32_TYPE);
|
||||
if (attr == NULL)
|
||||
return B_NAME_NOT_FOUND;
|
||||
|
||||
*value = attr->attr.value.ui32;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_uint64(device_node_handle node, const char *name,
|
||||
uint64 *value, bool recursive)
|
||||
{
|
||||
status_t status;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
status = pnp_get_attr_uint64_nolock(node, name, value, recursive);
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_uint64_nolock(device_node_handle node, const char *name,
|
||||
uint64 *value, bool recursive)
|
||||
{
|
||||
device_attr_info *attr = pnp_find_attr_nolock(node, name, recursive, B_UINT64_TYPE);
|
||||
if (attr == NULL)
|
||||
return B_NAME_NOT_FOUND;
|
||||
|
||||
*value = attr->attr.value.ui64;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_string(device_node_handle node, const char *name,
|
||||
char **value, bool recursive)
|
||||
{
|
||||
status_t status;
|
||||
const char *orig_value;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
status = pnp_get_attr_string_nolock(node, name, &orig_value, recursive);
|
||||
if (status == B_OK) {
|
||||
*value = strdup(orig_value);
|
||||
if (orig_value != NULL && *value == NULL)
|
||||
status = B_NO_MEMORY;
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_string_nolock(device_node_handle node, const char *name,
|
||||
const char **value, bool recursive)
|
||||
{
|
||||
device_attr_info *attr = pnp_find_attr_nolock(node, name, recursive, B_STRING_TYPE);
|
||||
if (attr == NULL)
|
||||
return B_NAME_NOT_FOUND;
|
||||
|
||||
*value = attr->attr.value.string;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_raw(device_node_handle node, const char *name,
|
||||
void **data, size_t *length, bool recursive)
|
||||
{
|
||||
const void *orig_data;
|
||||
status_t status;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
status = pnp_get_attr_raw_nolock( node, name, &orig_data, length, recursive );
|
||||
|
||||
if (status == B_OK) {
|
||||
void *tmp_data = malloc(*length);
|
||||
if (tmp_data != NULL) {
|
||||
memcpy(tmp_data, orig_data, *length);
|
||||
*data = tmp_data;
|
||||
} else
|
||||
status = B_NO_MEMORY;
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_raw_nolock(device_node_handle node, const char *name,
|
||||
const void **data, size_t *length, bool recursive)
|
||||
{
|
||||
device_attr_info *attr = pnp_find_attr_nolock(node, name, recursive, B_RAW_TYPE);
|
||||
if (attr == NULL)
|
||||
return B_NAME_NOT_FOUND;
|
||||
|
||||
*data = attr->attr.value.raw.data;
|
||||
*length = attr->attr.value.raw.length;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/** Compare two attributes for */
|
||||
|
||||
int
|
||||
pnp_compare_attrs(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;
|
||||
}
|
||||
|
||||
|
||||
// free node attribute
|
||||
|
||||
void
|
||||
pnp_free_node_attr(device_attr_info *attr)
|
||||
{
|
||||
free((char *)attr->attr.name);
|
||||
|
||||
switch (attr->attr.type) {
|
||||
case B_STRING_TYPE:
|
||||
free((char *)attr->attr.value.string);
|
||||
break;
|
||||
case B_RAW_TYPE:
|
||||
free(attr->attr.value.raw.data);
|
||||
break;
|
||||
}
|
||||
|
||||
free(attr);
|
||||
}
|
||||
|
||||
|
||||
// duplicate node attribute
|
||||
|
||||
status_t
|
||||
pnp_duplicate_node_attr(const device_attr *src, device_attr_info **dest_out)
|
||||
{
|
||||
device_attr_info *dest;
|
||||
status_t res;
|
||||
|
||||
dest = malloc(sizeof(*dest));
|
||||
if (dest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memset(dest, 0, sizeof(*dest));
|
||||
dest->ref_count = 1;
|
||||
dest->attr.name = strdup(src->name);
|
||||
dest->attr.type = src->type;
|
||||
|
||||
switch (src->type) {
|
||||
case B_UINT8_TYPE:
|
||||
case B_UINT16_TYPE:
|
||||
case B_UINT32_TYPE:
|
||||
case B_UINT64_TYPE:
|
||||
dest->attr.value.ui64 = src->value.ui64;
|
||||
break;
|
||||
|
||||
case B_STRING_TYPE:
|
||||
dest->attr.value.string = strdup(src->value.string);
|
||||
if (dest->attr.value.string == NULL) {
|
||||
res = B_NO_MEMORY;
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
|
||||
case B_RAW_TYPE:
|
||||
dest->attr.value.raw.data = malloc(src->value.raw.length);
|
||||
if (dest->attr.value.raw.data == NULL) {
|
||||
res = B_NO_MEMORY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dest->attr.value.raw.length = src->value.raw.length;
|
||||
memcpy(dest->attr.value.raw.data, src->value.raw.data,
|
||||
src->value.raw.length);
|
||||
break;
|
||||
|
||||
default:
|
||||
res = B_BAD_VALUE;
|
||||
goto err;
|
||||
}
|
||||
|
||||
*dest_out = dest;
|
||||
return B_OK;
|
||||
|
||||
err:
|
||||
free(dest);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// public: get first/next attribute of node
|
||||
|
||||
status_t
|
||||
pnp_get_next_attr(device_node_handle node, device_attr_handle *attr)
|
||||
{
|
||||
device_attr_info *next;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
if (*attr != NULL) {
|
||||
// next attribute
|
||||
next = (*attr)->next;
|
||||
// implicit release of previous attribute
|
||||
if (--(*attr)->ref_count == 0)
|
||||
pnp_free_node_attr(*attr);
|
||||
} else {
|
||||
// first attribute
|
||||
next = node->attributes;
|
||||
}
|
||||
|
||||
if (next)
|
||||
++next->ref_count;
|
||||
*attr = next;
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return next ? B_OK : B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
// public: release attribute of node explicitely
|
||||
|
||||
status_t
|
||||
pnp_release_attr(device_node_handle node, device_attr_handle attr)
|
||||
{
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
if (--attr->ref_count == 0)
|
||||
pnp_free_node_attr(attr);
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// public: retrieve data of attribute
|
||||
|
||||
status_t
|
||||
pnp_retrieve_attr(device_attr_handle attr, const device_attr **attr_content)
|
||||
{
|
||||
*attr_content = &attr->attr;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// remove attribute from node, freeing it if not in use
|
||||
// node_lock must be hold
|
||||
|
||||
void
|
||||
pnp_remove_attr_int(device_node_handle node, device_attr_info *attr)
|
||||
{
|
||||
REMOVE_DL_LIST(attr, node->attributes, );
|
||||
|
||||
if (--attr->ref_count == 0)
|
||||
pnp_free_node_attr(attr);
|
||||
}
|
||||
|
||||
|
||||
// public: remove attribute from node
|
||||
|
||||
status_t
|
||||
pnp_remove_attr(device_node_handle node, const char *name)
|
||||
{
|
||||
device_attr_info *attr;
|
||||
|
||||
// don't remove holy attributes
|
||||
if (is_fixed_attribute(name))
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
// find attribute in list
|
||||
attr = pnp_find_attr_nolock(node, name, false, B_ANY_TYPE);
|
||||
if (attr != NULL) {
|
||||
// and remove it from it
|
||||
pnp_remove_attr_int(node, attr);
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return attr ? B_OK : B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
// public: modify attribute's content
|
||||
|
||||
status_t
|
||||
pnp_write_attr(device_node_handle node, const device_attr *attr)
|
||||
{
|
||||
device_attr_info *old_attr, *new_attr;
|
||||
status_t res;
|
||||
|
||||
// don't touch holy attributes
|
||||
if (is_fixed_attribute(attr->name))
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
res = pnp_duplicate_node_attr(attr, &new_attr);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
// check whether it's an add or modify
|
||||
for (old_attr = node->attributes; old_attr != NULL; old_attr = old_attr->next) {
|
||||
if (!strcmp(attr->name, old_attr->attr.name))
|
||||
break;
|
||||
}
|
||||
|
||||
if (old_attr != NULL) {
|
||||
// it's a modify
|
||||
// we first insert new attribute after old one...
|
||||
// (so it appears at same position in attribute list)
|
||||
new_attr->prev = old_attr;
|
||||
new_attr->next = old_attr->next;
|
||||
|
||||
if (old_attr->next == NULL)
|
||||
old_attr->next->prev = new_attr;
|
||||
|
||||
old_attr->next = new_attr;
|
||||
|
||||
// ...and get rid of the old one
|
||||
REMOVE_DL_LIST(old_attr, node->attributes, );
|
||||
|
||||
if (--old_attr->ref_count == 0)
|
||||
pnp_free_node_attr(old_attr);
|
||||
} else {
|
||||
// it's an add
|
||||
// append to end of list;
|
||||
// this is really something to optimize
|
||||
if (node->attributes == NULL) {
|
||||
ADD_DL_LIST_HEAD(new_attr, node->attributes, );
|
||||
} else {
|
||||
device_attr_info *last_attr;
|
||||
|
||||
for (last_attr = node->attributes; last_attr->next != NULL; last_attr = last_attr->next)
|
||||
;
|
||||
|
||||
last_attr->next = new_attr;
|
||||
new_attr->prev = last_attr;
|
||||
new_attr->next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// check whether attribute cannot be modified;
|
||||
// returns true, if that's forbidden
|
||||
|
||||
static bool
|
||||
is_fixed_attribute(const char *name)
|
||||
{
|
||||
static const char *forbidden_list[] = {
|
||||
B_DRIVER_MODULE, // never change driver
|
||||
PNP_BUS_IS_BUS, // never switch between bus/not bus mode
|
||||
};
|
||||
uint32 i;
|
||||
|
||||
// some attributes are to important to get modified or removed directly
|
||||
for (i = 0; i < sizeof(forbidden_list) / sizeof(forbidden_list[0]); ++i) {
|
||||
if (!strcmp(name, forbidden_list[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// same as pnp_find_attr(), but node_lock must be hold and is never released
|
||||
|
||||
device_attr_info *
|
||||
pnp_find_attr_nolock(device_node_handle node, const char *name,
|
||||
bool recursive, type_code type)
|
||||
{
|
||||
do {
|
||||
device_attr_info *attr;
|
||||
|
||||
for (attr = node->attributes; attr != NULL; attr = attr->next) {
|
||||
if (type != B_ANY_TYPE && attr->attr.type != type)
|
||||
continue;
|
||||
|
||||
if (!strcmp(attr->attr.name, name))
|
||||
return attr;
|
||||
}
|
||||
|
||||
node = node->parent;
|
||||
} while (node != NULL && recursive);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Device Manager
|
||||
|
||||
Main file.
|
||||
*/
|
||||
|
||||
#include "device_manager_private.h"
|
||||
#include <kdevice_manager.h>
|
||||
|
||||
#include <generic_syscall.h>
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
//#define TRACE_DEVICE_MANAGER
|
||||
#ifdef TRACE_DEVICE_MANAGER
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
device_node_info *gRootNode;
|
||||
|
||||
bool disable_useraddons;
|
||||
|
||||
int pnp_resource_wait_count;
|
||||
sem_id pnp_resource_wait_sem;
|
||||
|
||||
io_resource_info *io_mem_list, *io_port_list, *isa_dma_list;
|
||||
|
||||
|
||||
static int
|
||||
dump_device_nodes(int argc, char **argv)
|
||||
{
|
||||
dm_dump_node(gRootNode, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
case B_MODULE_UNINIT:
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ToDo: the device manager exports these for now, so that Thomas's driver
|
||||
// can work on both R5 and Haiku without too much hassle.
|
||||
// We might also want to keep it in order to have the possibility to make
|
||||
// substantial changes without breaking compatibility, and eventually
|
||||
// separate it from the kernel for whatever reason.
|
||||
|
||||
device_manager_info gDeviceManagerModule = {
|
||||
{
|
||||
B_DEVICE_MANAGER_MODULE_NAME,
|
||||
0,
|
||||
std_ops
|
||||
},
|
||||
|
||||
pnp_load_driver,
|
||||
pnp_unload_driver,
|
||||
|
||||
dm_rescan,
|
||||
|
||||
dm_register_node,
|
||||
dm_unregister_node,
|
||||
|
||||
dm_get_root,
|
||||
dm_get_next_child_node,
|
||||
dm_get_parent,
|
||||
dm_put_node,
|
||||
|
||||
dm_acquire_io_resources,
|
||||
dm_release_io_resources,
|
||||
|
||||
dm_create_id,
|
||||
dm_free_id,
|
||||
|
||||
pnp_get_attr_uint8,
|
||||
pnp_get_attr_uint16,
|
||||
pnp_get_attr_uint32,
|
||||
pnp_get_attr_uint64,
|
||||
pnp_get_attr_string,
|
||||
pnp_get_attr_raw,
|
||||
|
||||
pnp_get_next_attr,
|
||||
pnp_release_attr,
|
||||
pnp_retrieve_attr,
|
||||
pnp_write_attr,
|
||||
NULL // remove_attr
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
status_t
|
||||
device_manager_init(struct kernel_args *args)
|
||||
{
|
||||
status_t status;
|
||||
// char name[B_FILE_NAME_LENGTH];
|
||||
// size_t size = sizeof(name);
|
||||
// int32 cookie;
|
||||
|
||||
TRACE(("device manager init\n"));
|
||||
|
||||
status = dm_init_id_generator();
|
||||
if (status < B_OK) {
|
||||
panic("could not initialize ID generator\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
status = dm_init_nodes();
|
||||
if (status < B_OK) {
|
||||
panic("could not initialize device nodes\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
pnp_resource_wait_sem = create_sem(0, "pnp_resource_wait_sem");
|
||||
if (pnp_resource_wait_sem < 0) {
|
||||
panic("could not create resource wait sem\n");
|
||||
return pnp_resource_wait_sem;
|
||||
}
|
||||
|
||||
pnp_resource_wait_count = 0;
|
||||
io_mem_list = io_port_list = isa_dma_list = NULL;
|
||||
pnp_fs_emulation_nesting = 0;
|
||||
|
||||
dm_init_root_node();
|
||||
|
||||
#ifdef TRACE_DEVICE_MANAGER
|
||||
{
|
||||
// dump root node
|
||||
device_node_info *node = gRootNode;
|
||||
while (node && node->parent != NULL)
|
||||
node = node->parent;
|
||||
dm_dump_node(node, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// build initial device tree; register all root bus_managers
|
||||
|
||||
#if 0
|
||||
cookie = 0;
|
||||
while (get_next_loaded_module_name(&cookie, name, &size) == B_OK) {
|
||||
if (strncmp(name, "bus_managers", 12))
|
||||
continue;
|
||||
|
||||
dprintf(": %s\n", name);
|
||||
size = sizeof(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
add_debugger_command("dm_tree", &dump_device_nodes, "dump device node tree");
|
||||
register_generic_syscall(DEVICE_MANAGER_SYSCALLS, device_manager_control, 1, 0);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// hold gNodeLock
|
||||
static void
|
||||
device_manager_rescan_bus(device_node_info *parent)
|
||||
{
|
||||
device_node_info *node = NULL;
|
||||
char *dummy;
|
||||
|
||||
if (pnp_get_attr_string(parent, B_DRIVER_BUS, &dummy, false) == B_OK) {
|
||||
free(dummy);
|
||||
dm_rescan(parent);
|
||||
}
|
||||
|
||||
while ((node = (device_node_info *)list_get_next_item(&parent->children, node)) != NULL) {
|
||||
device_manager_rescan_bus(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
status_t
|
||||
device_manager_init_post_modules(struct kernel_args *args)
|
||||
{
|
||||
TRACE(("device_manager_init_post_modules()\n"));
|
||||
device_manager_rescan_bus(gRootNode);
|
||||
TRACE(("device_manager_init_post_modules() rescan done\n"));
|
||||
return B_OK;
|
||||
}
|
||||
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
Distributed under the terms of the NewOS License.
|
||||
|
||||
Macros for double linked lists
|
||||
*/
|
||||
|
||||
#ifndef _DL_LIST_H
|
||||
#define _DL_LIST_H
|
||||
|
||||
#define REMOVE_DL_LIST( item, head, prefix ) \
|
||||
do { \
|
||||
if( item->prefix##prev ) \
|
||||
item->prefix##prev->prefix##next = item->prefix##next; \
|
||||
else \
|
||||
head = item->prefix##next; \
|
||||
\
|
||||
if( item->prefix##next ) \
|
||||
item->prefix##next->prefix##prev = item->prefix##prev; \
|
||||
} while( 0 )
|
||||
|
||||
#define ADD_DL_LIST_HEAD( item, head, prefix ) \
|
||||
do { \
|
||||
item->prefix##next = head; \
|
||||
item->prefix##prev = NULL; \
|
||||
\
|
||||
if( (head) ) \
|
||||
(head)->prefix##prev = item; \
|
||||
\
|
||||
(head) = item; \
|
||||
} while( 0 )
|
||||
|
||||
#define REMOVE_CDL_LIST( item, head, prefix ) \
|
||||
do { \
|
||||
item->prefix##next->prefix##prev = item->prefix##prev; \
|
||||
item->prefix##prev->prefix##next = item->prefix##next; \
|
||||
\
|
||||
if( item == (head) ) { \
|
||||
if( item->prefix##next != item ) \
|
||||
(head) = item->prefix##next; \
|
||||
else \
|
||||
(head) = NULL; \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
#define ADD_CDL_LIST_TAIL( item, type, head, prefix ) \
|
||||
do { \
|
||||
type *old_head = head; \
|
||||
\
|
||||
if( old_head ) { \
|
||||
type *first, *last; \
|
||||
\
|
||||
first = old_head; \
|
||||
last = first->prefix##prev; \
|
||||
\
|
||||
item->prefix##next = first; \
|
||||
item->prefix##prev = last; \
|
||||
first->prefix##prev = item; \
|
||||
last->prefix##next = item; \
|
||||
} else { \
|
||||
head = item; \
|
||||
item->prefix##next = item->prefix##prev = item; \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
#define ADD_CDL_LIST_HEAD( item, type, head, prefix ) \
|
||||
do { \
|
||||
type *old_head = head; \
|
||||
\
|
||||
head = item; \
|
||||
if( old_head ) { \
|
||||
type *first, *last; \
|
||||
\
|
||||
first = old_head; \
|
||||
last = first->prefix##prev; \
|
||||
\
|
||||
item->prefix##next = first; \
|
||||
item->prefix##prev = last; \
|
||||
first->prefix##prev = item; \
|
||||
last->prefix##next = item; \
|
||||
} else { \
|
||||
item->prefix##next = item->prefix##prev = item; \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
#endif
|
@ -1,343 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Device Manager
|
||||
|
||||
device driver loader.
|
||||
*/
|
||||
|
||||
#include "device_manager_private.h"
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
//#define TRACE_DRIVER_LOADER
|
||||
#ifdef TRACE_DRIVER_LOADER
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
/** unblock loading of driver
|
||||
* node_lock must be hold
|
||||
*/
|
||||
|
||||
void
|
||||
pnp_unblock_load(device_node_info *node)
|
||||
{
|
||||
if (--node->load_block_count == 0 && node->num_blocked_loads > 0) {
|
||||
TRACE(("Releasing blocked loads of %p\n", node));
|
||||
|
||||
release_sem_etc(node->load_block_sem,
|
||||
node->num_blocked_loads, B_DO_NOT_RESCHEDULE);
|
||||
|
||||
node->num_blocked_loads = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** if loading of driver is blocked, wait until it is unblocked
|
||||
* node_lock must be hold
|
||||
*/
|
||||
|
||||
static void
|
||||
pnp_wait_load_block(device_node_info *node)
|
||||
{
|
||||
if (node->load_block_count > 0) {
|
||||
TRACE(("Loading of %p is blocked - wait until unblock\n", node));
|
||||
|
||||
// make sure we get informed about unblock
|
||||
++node->num_blocked_loads;
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
acquire_sem(node->load_block_sem);
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
TRACE(("Continuing loading of %p\n", node));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** load driver for real
|
||||
* (loader_lock must be hold)
|
||||
*/
|
||||
|
||||
static status_t
|
||||
load_driver_int(device_node_info *node, void *user_cookie)
|
||||
{
|
||||
driver_module_info *driver;
|
||||
char *moduleName;
|
||||
void *cookie;
|
||||
status_t status;
|
||||
|
||||
TRACE(("load_driver_int()\n"));
|
||||
|
||||
status = pnp_get_attr_string(node, B_DRIVER_MODULE, &moduleName, false);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
TRACE(("%s\n", moduleName));
|
||||
|
||||
status = get_module(moduleName, (module_info **)&driver);
|
||||
if (status < B_OK) {
|
||||
dprintf("Cannot load module %s\n", moduleName);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (driver->init_driver == NULL) {
|
||||
dprintf("Driver %s has no init_driver hook\n", moduleName);
|
||||
status = B_ERROR;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
status = driver->init_driver(node, user_cookie, &cookie);
|
||||
if (status < B_OK) {
|
||||
dprintf("init driver failed (node %p, %s): %s\n", node, moduleName, strerror(status));
|
||||
goto err2;
|
||||
}
|
||||
|
||||
node->driver = driver;
|
||||
node->cookie = cookie;
|
||||
|
||||
free(moduleName);
|
||||
return B_OK;
|
||||
|
||||
err2:
|
||||
put_module(moduleName);
|
||||
err1:
|
||||
free(moduleName);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/** public: load driver */
|
||||
|
||||
status_t
|
||||
pnp_load_driver(device_node_handle node, void *user_cookie,
|
||||
driver_module_info **interface, void **cookie)
|
||||
{
|
||||
status_t res;
|
||||
|
||||
TRACE(("pnp_load_driver()\n"));
|
||||
|
||||
if (node == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
if (!node->registered) {
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
TRACE(("Refused loading as driver got unregistered\n"));
|
||||
return B_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
// increase ref_count whenever someone loads the driver
|
||||
// to keep node information alive
|
||||
++node->ref_count;
|
||||
|
||||
if (node->load_count == 0) {
|
||||
// driver is not loaded
|
||||
// tell others that someone will load the driver
|
||||
// (after this, noone is allowed to block loading)
|
||||
++node->loading;
|
||||
|
||||
// wait until loading is not blocked
|
||||
pnp_wait_load_block(node);
|
||||
|
||||
// make sure noone else load/unloads/notifies the driver
|
||||
pnp_start_hook_call_nolock(node);
|
||||
|
||||
if (!node->registered) {
|
||||
// device got lost meanwhile
|
||||
TRACE(("Device got lost during wait\n"));
|
||||
res = B_NAME_NOT_FOUND;
|
||||
} else if (node->load_count == 0) {
|
||||
// noone loaded the driver meanwhile, so
|
||||
// we have to do that
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
TRACE(("Driver will be loaded\n"));
|
||||
|
||||
res = load_driver_int(node, user_cookie);
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
} else {
|
||||
// some other thread loaded the driver meanwhile
|
||||
TRACE(("Driver got loaded during wait\n"));
|
||||
res = B_OK;
|
||||
}
|
||||
|
||||
// info: the next statements may have a strange order,
|
||||
// but as we own node_lock, order is not important
|
||||
|
||||
// finish loading officially
|
||||
// (don't release node_lock before load_count gets increased!)
|
||||
--node->loading;
|
||||
|
||||
// unblock concurrent load/unload/notification
|
||||
pnp_finish_hook_call_nolock(node);
|
||||
|
||||
if (res == B_OK) {
|
||||
// everything went fine - increase load counter
|
||||
++node->load_count;
|
||||
} else {
|
||||
// loading failed or device got removed - restore reference count;
|
||||
// but first (i.e. as long as ref_count is increased) get
|
||||
// rid of waiting children
|
||||
dm_put_node_nolock(node);
|
||||
}
|
||||
} else {
|
||||
// driver is already loaded, so increase load_count
|
||||
++node->load_count;
|
||||
res = B_OK;
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
if (res == B_OK) {
|
||||
if (interface)
|
||||
*interface = node->driver;
|
||||
|
||||
if (cookie)
|
||||
*cookie = node->cookie;
|
||||
|
||||
TRACE(("load_driver: Success \"%s\"\n",
|
||||
((struct module_info *)node->driver)->name));
|
||||
} else {
|
||||
TRACE(("load_driver: Failure (%s)\n", strerror(res)));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/** unload driver for real */
|
||||
|
||||
static status_t
|
||||
unload_driver_int(device_node_info *node)
|
||||
{
|
||||
driver_module_info *driver = node->driver;
|
||||
char *moduleName;
|
||||
status_t status;
|
||||
|
||||
status = pnp_get_attr_string(node, B_DRIVER_MODULE, &moduleName, false);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
TRACE(("unload_driver_int: %s\n", moduleName));
|
||||
|
||||
if (driver->uninit_driver == NULL) {
|
||||
// it has no uninit - we can't unload it, so it stays in memory forever
|
||||
TRACE(("Driver %s has no uninit_device hook\n", moduleName));
|
||||
status = B_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = driver->uninit_driver(node->cookie);
|
||||
if (status != B_OK) {
|
||||
TRACE(("Failed to uninit driver %s (%s)\n", moduleName, strerror(status)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
// if it was unregistered a while ago but couldn't get cleaned up
|
||||
// because it was in use, do the cleanup now
|
||||
if (!node->registered) {
|
||||
if (driver->device_cleanup != NULL)
|
||||
driver->device_cleanup(node);
|
||||
}
|
||||
|
||||
put_module(moduleName);
|
||||
|
||||
out:
|
||||
free(moduleName);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/** public: unload driver */
|
||||
|
||||
status_t
|
||||
pnp_unload_driver(device_node_handle node)
|
||||
{
|
||||
status_t res;
|
||||
|
||||
TRACE(("pnp_unload_driver: %p\n", node));
|
||||
|
||||
if (node == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
if (node->loading > 0 || node->load_count == 1) {
|
||||
// we may have to unload the driver because:
|
||||
// - load_count will be zero after decrement, or
|
||||
// - there is a concurrent load/unload call, so we
|
||||
// don't know what the future load_count will be
|
||||
|
||||
// signal active unload;
|
||||
// don't decrease load_count - the driver is still loaded!
|
||||
++node->loading;
|
||||
|
||||
// wait for concurrent load/unload/notification
|
||||
pnp_start_hook_call_nolock(node);
|
||||
|
||||
if (node->load_count == 1) {
|
||||
// node is still to be unloaded
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
TRACE(("unloading %p\n", node));
|
||||
res = unload_driver_int(node);
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
} else {
|
||||
// concurrent load/unload either unloaded the driver or
|
||||
// increased load_count so unload is not necessary or forbidden
|
||||
TRACE(("don't unload as current load_count is %ld\n",
|
||||
node->load_count));
|
||||
res = B_OK;
|
||||
}
|
||||
|
||||
// signal unload finished
|
||||
--node->loading;
|
||||
|
||||
if (res == B_OK) {
|
||||
// everything is fine, so decrease load_count ...
|
||||
--node->load_count;
|
||||
// ... and reference count
|
||||
dm_put_node_nolock(node);
|
||||
} else {
|
||||
// unloading failed: leave loaded in memory forever
|
||||
// as load_count is not decreased, the driver will never
|
||||
// be unloaded, and as reference count is not decreased,
|
||||
// the node will exist forever
|
||||
TRACE(("Unload failed - leaving driver of %p in memory\n", node));
|
||||
}
|
||||
|
||||
// unblock others
|
||||
pnp_finish_hook_call_nolock(node);
|
||||
} else {
|
||||
// no concurrent load/unload and load_count won't reach zero
|
||||
--node->load_count;
|
||||
// each load increased reference count by one - time to undo that
|
||||
dm_put_node_nolock(node);
|
||||
|
||||
res = B_OK;
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -1,679 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Device Manager
|
||||
|
||||
device node handling.
|
||||
|
||||
To make sure that nodes persist as long as somebody is using them, we
|
||||
maintain a reference count (ref_count) which is increased
|
||||
- when the node is officially registered
|
||||
- if someone loads the corresponding driver
|
||||
- for each child
|
||||
|
||||
node_lock must be hold when updating of reference count, children
|
||||
dependency list and during (un)-registration.
|
||||
*/
|
||||
|
||||
|
||||
#include "device_manager_private.h"
|
||||
#include "dl_list.h"
|
||||
|
||||
#include <kernel.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <TypeConstants.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
//#define TRACE_NODES
|
||||
#ifdef TRACE_NODES
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
benaphore gNodeLock;
|
||||
|
||||
uint32 sNodeID; // nodes counter
|
||||
|
||||
|
||||
static void
|
||||
put_level(int32 level)
|
||||
{
|
||||
while (level-- > 0)
|
||||
dprintf(" ");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dump_attribute(device_attr_info *attr, int32 level)
|
||||
{
|
||||
if (attr == NULL)
|
||||
return;
|
||||
|
||||
put_level(level + 2);
|
||||
dprintf("\"%s\" : ", attr->attr.name);
|
||||
switch (attr->attr.type) {
|
||||
case B_STRING_TYPE:
|
||||
dprintf("string : \"%s\"", attr->attr.value.string);
|
||||
break;
|
||||
case B_UINT8_TYPE:
|
||||
dprintf("uint8 : %u (%#x)", attr->attr.value.ui8, attr->attr.value.ui8);
|
||||
break;
|
||||
case B_UINT16_TYPE:
|
||||
dprintf("uint16 : %u (%#x)", attr->attr.value.ui16, attr->attr.value.ui16);
|
||||
break;
|
||||
case B_UINT32_TYPE:
|
||||
dprintf("uint32 : %lu (%#lx)", attr->attr.value.ui32, attr->attr.value.ui32);
|
||||
break;
|
||||
case B_UINT64_TYPE:
|
||||
dprintf("uint64 : %Lu (%#Lx)", attr->attr.value.ui64, attr->attr.value.ui64);
|
||||
break;
|
||||
default:
|
||||
dprintf("raw data");
|
||||
}
|
||||
dprintf("\n");
|
||||
|
||||
dump_attribute(attr->next, level);
|
||||
}
|
||||
|
||||
|
||||
/** free attributes of node
|
||||
* if an attribute is still in use, it's deletion is postponed
|
||||
*/
|
||||
|
||||
static void
|
||||
free_node_attrs(device_node_info *node)
|
||||
{
|
||||
device_attr_info *attr, *next_attr;
|
||||
|
||||
for (attr = node->attributes; attr != NULL; attr = next_attr) {
|
||||
next_attr = attr->next;
|
||||
|
||||
pnp_remove_attr_int(node, attr);
|
||||
}
|
||||
|
||||
node->attributes = NULL;
|
||||
}
|
||||
|
||||
|
||||
// free node's I/O resource list, setting it to NULL
|
||||
// (resources must have been released already)
|
||||
|
||||
static void
|
||||
free_node_resources(device_node_info *node)
|
||||
{
|
||||
free(node->io_resources);
|
||||
node->io_resources = NULL;
|
||||
}
|
||||
|
||||
|
||||
// free node
|
||||
// (node lock must be hold)
|
||||
|
||||
static void
|
||||
free_node(device_node_info *node)
|
||||
{
|
||||
const char *generator_name, *driver_name;
|
||||
uint32 auto_id;
|
||||
|
||||
// ToDo: we lose memory here if the attributes are available!
|
||||
if (pnp_get_attr_string_nolock(node, B_DRIVER_MODULE, &driver_name, false) != B_OK)
|
||||
driver_name = "?";
|
||||
|
||||
TRACE(("free_node(node: %p, driver: %s)\n", node, driver_name));
|
||||
|
||||
// free associated auto ID, if requested
|
||||
if (pnp_get_attr_string_nolock(node, PNP_MANAGER_ID_GENERATOR,
|
||||
&generator_name, false) == B_OK) {
|
||||
if (pnp_get_attr_uint32(node, PNP_MANAGER_AUTO_ID, &auto_id, false) != B_OK) {
|
||||
TRACE(("Cannot find corresponding auto_id of generator %s", generator_name));
|
||||
} else
|
||||
dm_free_id(generator_name, auto_id);
|
||||
}
|
||||
|
||||
dm_release_node_resources(node);
|
||||
|
||||
delete_sem(node->hook_sem);
|
||||
delete_sem(node->load_block_sem);
|
||||
|
||||
free_node_attrs(node);
|
||||
free_node_resources(node);
|
||||
free(node);
|
||||
}
|
||||
|
||||
|
||||
// copy node attributes into node's attribute list
|
||||
|
||||
static status_t
|
||||
copy_node_attrs(device_node_info *node, const device_attr *src)
|
||||
{
|
||||
status_t res;
|
||||
const device_attr *src_attr;
|
||||
device_attr_info *last_attr = NULL;
|
||||
|
||||
node->attributes = NULL;
|
||||
|
||||
// take care to keep attributes in same order;
|
||||
// this is (currently) not necessary but a naive implementation would
|
||||
// reverse order, which looks a bit odd (at least for the driver writer)
|
||||
for (src_attr = src; src_attr->name != NULL; ++src_attr) {
|
||||
device_attr_info *new_attr;
|
||||
|
||||
res = pnp_duplicate_node_attr( src_attr, &new_attr );
|
||||
if (res != B_OK)
|
||||
goto err;
|
||||
|
||||
//list_add_link_to_head(&node->attributes, new_attr);
|
||||
ADD_DL_LIST_HEAD( new_attr, node->attributes, );
|
||||
|
||||
last_attr = new_attr;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
|
||||
err:
|
||||
free_node_attrs(node);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// allocate array of node I/O resources;
|
||||
// all resource handles are set to zero, so if something goes wrong
|
||||
// later on, resources aren't transferred to node
|
||||
|
||||
static status_t
|
||||
allocate_node_resource_array(device_node_info *node, const io_resource_handle *resources)
|
||||
{
|
||||
const io_resource_handle *resource;
|
||||
int num_resources = 0;
|
||||
|
||||
if (resources != NULL) {
|
||||
for (resource = resources; *resource != NULL; ++resource)
|
||||
++num_resources;
|
||||
}
|
||||
|
||||
node->num_io_resources = num_resources;
|
||||
|
||||
node->io_resources = malloc((num_resources + 1) * sizeof(io_resource_handle));
|
||||
if (node->io_resources == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memset(node->io_resources, 0, (num_resources + 1) * sizeof(io_resource_handle));
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// Device Manager private functions
|
||||
|
||||
|
||||
/** allocate device node info structure;
|
||||
* initially, ref_count is one to make sure node won't get destroyed by mistake
|
||||
*/
|
||||
|
||||
status_t
|
||||
dm_allocate_node(const device_attr *attrs, const io_resource_handle *resources,
|
||||
device_node_info **_node)
|
||||
{
|
||||
device_node_info *node;
|
||||
status_t res;
|
||||
|
||||
node = calloc(1, sizeof(*node));
|
||||
if (node == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
res = copy_node_attrs(node, attrs);
|
||||
if (res < 0)
|
||||
goto err;
|
||||
|
||||
res = allocate_node_resource_array(node, resources);
|
||||
if (res < 0)
|
||||
goto err2;
|
||||
|
||||
res = node->hook_sem = create_sem(0, "pnp_hook");
|
||||
if (res < 0)
|
||||
goto err3;
|
||||
|
||||
res = node->load_block_sem = create_sem(0, "pnp_load_block");
|
||||
if (res < 0)
|
||||
goto err4;
|
||||
|
||||
list_init(&node->children);
|
||||
|
||||
node->parent = NULL;
|
||||
node->rescan_depth = 0;
|
||||
node->registered = false;
|
||||
#if 0
|
||||
node->verifying = false;
|
||||
node->redetected = false;
|
||||
node->init_finished = false;
|
||||
#endif
|
||||
node->ref_count = 1;
|
||||
node->load_count = 0;
|
||||
node->blocked_by_rescan = false;
|
||||
node->automatically_loaded = false;
|
||||
|
||||
node->num_waiting_hooks = 0;
|
||||
node->num_blocked_loads = 0;
|
||||
node->load_block_count = 0;
|
||||
node->loading = 0;
|
||||
node->internal_id = sNodeID++;
|
||||
|
||||
TRACE(("dm_allocate_node(): new node %p\n", node));
|
||||
|
||||
*_node = node;
|
||||
|
||||
return B_OK;
|
||||
|
||||
err4:
|
||||
delete_sem(node->hook_sem);
|
||||
err3:
|
||||
free_node_resources(node);
|
||||
err2:
|
||||
free_node_attrs(node);
|
||||
err:
|
||||
free(node);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
dm_add_child_node(device_node_info *parent, device_node_info *node)
|
||||
{
|
||||
TRACE(("dm_add_child_node(parent = %p, child = %p)\n", parent, node));
|
||||
|
||||
if (parent == NULL)
|
||||
return;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
// parent must not be destroyed as long as it has children
|
||||
parent->ref_count++;
|
||||
node->parent = parent;
|
||||
|
||||
// tell parent about us, so we get unregistered automatically if parent
|
||||
// gets unregistered
|
||||
list_add_item(&parent->children, node);
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
dm_get_node_nolock(device_node_info *node)
|
||||
{
|
||||
node->ref_count++;
|
||||
}
|
||||
|
||||
|
||||
// increase ref_count of node
|
||||
|
||||
void
|
||||
dm_get_node(device_node_info *node)
|
||||
{
|
||||
TRACE(("dm_get_device_node(%p)\n", node));
|
||||
|
||||
if (node == NULL)
|
||||
return;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
node->ref_count++;
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
|
||||
// remove node reference and clean it up if necessary
|
||||
// (gNodeLock must be hold)
|
||||
|
||||
void
|
||||
dm_put_node_nolock(device_node_info *node)
|
||||
{
|
||||
do {
|
||||
device_node_info *parent;
|
||||
|
||||
TRACE(("pnp_remove_node_ref_internal(ref_count of %p: %ld)\n", node, node->ref_count - 1));
|
||||
|
||||
// unregistered devices lose their I/O resources as soon as they
|
||||
// are unloaded
|
||||
if (!node->registered && node->loading == 0 && node->load_count == 0)
|
||||
dm_release_node_resources(node);
|
||||
|
||||
if (--node->ref_count > 0)
|
||||
return;
|
||||
|
||||
TRACE(("cleaning up %p (parent: %p)\n", node, node->parent));
|
||||
|
||||
// time to clean up
|
||||
parent = node->parent;
|
||||
|
||||
if (parent != NULL)
|
||||
list_remove_item(&parent->children, node);
|
||||
|
||||
free_node(node);
|
||||
|
||||
// unrolled recursive call: decrease ref_count of parent as well
|
||||
node = parent;
|
||||
} while (node != NULL);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
// public: find node with some node attributes given
|
||||
|
||||
device_node_info *
|
||||
dm_find_device(device_node_info *parent, const device_attr *attrs)
|
||||
{
|
||||
device_node_info *node, *found_node;
|
||||
|
||||
found_node = NULL;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
for (node = gNodeList; node; node = node->next) {
|
||||
const device_attr *attr;
|
||||
|
||||
// list contains removed devices too, so skip them
|
||||
if (!node->registered)
|
||||
continue;
|
||||
|
||||
if (parent != (device_node_info *)-1 && parent != node->parent)
|
||||
continue;
|
||||
|
||||
for (attr = attrs; attr && attr->name; ++attr) {
|
||||
device_attr_info *other = pnp_find_attr_nolock(node, attr->name, false, attr->type);
|
||||
if (other == NULL || pnp_compare_attrs(attr, &other->attr))
|
||||
break;
|
||||
}
|
||||
|
||||
if (attr != NULL && attr->name != NULL)
|
||||
continue;
|
||||
|
||||
// we found a node
|
||||
if (found_node != NULL)
|
||||
// but it wasn't the only one
|
||||
return NULL;
|
||||
|
||||
// even though we found a node, keep on search to make sure no other
|
||||
// node is matched too
|
||||
found_node = node;
|
||||
}
|
||||
|
||||
err:
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return found_node;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
dm_dump_node(device_node_info *node, int32 level)
|
||||
{
|
||||
device_node_info *child = NULL;
|
||||
|
||||
if (node == NULL)
|
||||
return;
|
||||
|
||||
put_level(level);
|
||||
dprintf("(%ld) @%p \"%s\"\n", level, node, node->driver ? node->driver->info.name : "---");
|
||||
dump_attribute(node->attributes, level);
|
||||
|
||||
while ((child = (device_node_info *)list_get_next_item(&node->children, child)) != NULL) {
|
||||
dm_dump_node(child, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
dm_init_nodes(void)
|
||||
{
|
||||
sNodeID = 1;
|
||||
return benaphore_init(&gNodeLock, "device nodes");
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// Functions part of the module API
|
||||
|
||||
|
||||
/** remove node reference and clean it up if necessary */
|
||||
|
||||
void
|
||||
dm_put_node(device_node_info *node)
|
||||
{
|
||||
TRACE(("dm_put_node(%p)\n", node));
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
dm_put_node_nolock(node);
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
|
||||
device_node_info *
|
||||
dm_get_root(void)
|
||||
{
|
||||
dm_get_node(gRootNode);
|
||||
return gRootNode;
|
||||
}
|
||||
|
||||
|
||||
device_node_info *
|
||||
dm_get_parent(device_node_info *node)
|
||||
{
|
||||
dm_get_node(node->parent);
|
||||
return node->parent;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
dm_get_next_child_node(device_node_info *parent, device_node_info **_node,
|
||||
const device_attr *attrs)
|
||||
{
|
||||
device_node_info *node = *_node;
|
||||
device_node_info *nodeToPut = node;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
while ((node = (device_node_info *)list_get_next_item(&parent->children, node)) != NULL) {
|
||||
const device_attr *attr;
|
||||
|
||||
// list contains removed devices too, so skip them
|
||||
if (!node->registered)
|
||||
continue;
|
||||
|
||||
for (attr = attrs; attr && attr->name; ++attr) {
|
||||
device_attr_info *other = pnp_find_attr_nolock(node, attr->name, false, attr->type);
|
||||
if (other == NULL || pnp_compare_attrs(attr, &other->attr))
|
||||
break;
|
||||
}
|
||||
|
||||
if (attr != NULL && attr->name != NULL)
|
||||
continue;
|
||||
|
||||
// we found a node
|
||||
dm_get_node_nolock(node);
|
||||
*_node = node;
|
||||
|
||||
if (nodeToPut != NULL)
|
||||
dm_put_node_nolock(nodeToPut);
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (nodeToPut != NULL)
|
||||
dm_put_node_nolock(nodeToPut);
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
// hold gNodeLock
|
||||
static device_node_info *
|
||||
device_manager_find_device(device_node_info *parent, uint32 id)
|
||||
{
|
||||
device_node_info *node = NULL;
|
||||
|
||||
if (parent->internal_id == id)
|
||||
return parent;
|
||||
|
||||
while ((node = (device_node_info *)list_get_next_item(&parent->children, node)) != NULL) {
|
||||
device_node_info *child = device_manager_find_device(node, id);
|
||||
if (child != NULL)
|
||||
return child;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
device_manager_control(const char *subsystem, uint32 function, void *buffer, size_t bufferSize)
|
||||
{
|
||||
switch (function) {
|
||||
case DM_GET_ROOT: {
|
||||
uint32 cookie;
|
||||
if (!IS_USER_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
if (bufferSize < sizeof(uint32))
|
||||
return B_BAD_VALUE;
|
||||
cookie = gRootNode->internal_id;
|
||||
|
||||
// copy back to user space
|
||||
if (user_memcpy(buffer, &cookie, sizeof(uint32)) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
return B_OK;
|
||||
}
|
||||
case DM_GET_CHILD: {
|
||||
uint32 cookie;
|
||||
device_node_info *node;
|
||||
device_node_info *child;
|
||||
|
||||
if (!IS_USER_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
if (bufferSize < sizeof(uint32))
|
||||
return B_BAD_VALUE;
|
||||
if (user_memcpy(&cookie, buffer, sizeof(uint32)) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
node = device_manager_find_device(gRootNode, cookie);
|
||||
if (!node) {
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
child = (device_node_info *)list_get_next_item(&node->children, NULL);
|
||||
if (child)
|
||||
cookie = child->internal_id;
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
if (!child)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
// copy back to user space
|
||||
if (user_memcpy(buffer, &cookie, sizeof(uint32)) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
return B_OK;
|
||||
}
|
||||
case DM_GET_NEXT_CHILD: {
|
||||
uint32 cookie;
|
||||
device_node_info *node;
|
||||
device_node_info *child;
|
||||
|
||||
if (!IS_USER_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
if (bufferSize < sizeof(uint32))
|
||||
return B_BAD_VALUE;
|
||||
if (user_memcpy(&cookie, buffer, sizeof(uint32)) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
node = device_manager_find_device(gRootNode, cookie);
|
||||
if (!node) {
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
child = (device_node_info *)list_get_next_item(&node->parent->children, node);
|
||||
if (child)
|
||||
cookie = child->internal_id;
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
if (!child)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
// copy back to user space
|
||||
if (user_memcpy(buffer, &cookie, sizeof(uint32)) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
return B_OK;
|
||||
}
|
||||
case DM_GET_NEXT_ATTRIBUTE: {
|
||||
struct dev_attr attr;
|
||||
device_node_info *node;
|
||||
uint32 i = 0;
|
||||
device_attr_info *attr_info;
|
||||
if (!IS_USER_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
if (bufferSize < sizeof(struct dev_attr))
|
||||
return B_BAD_VALUE;
|
||||
if (user_memcpy(&attr, buffer, sizeof(struct dev_attr)) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
node = device_manager_find_device(gRootNode, attr.node_cookie);
|
||||
if (!node) {
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
for (attr_info = node->attributes; attr.cookie > i && attr_info != NULL; attr_info = attr_info->next) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!attr_info) {
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
attr.cookie++;
|
||||
|
||||
strlcpy(attr.name, attr_info->attr.name, 254);
|
||||
attr.type = attr_info->attr.type;
|
||||
switch (attr_info->attr.type) {
|
||||
case B_UINT8_TYPE:
|
||||
attr.value.ui8 = attr_info->attr.value.ui8; break;
|
||||
case B_UINT16_TYPE:
|
||||
attr.value.ui16 = attr_info->attr.value.ui16; break;
|
||||
case B_UINT32_TYPE:
|
||||
attr.value.ui32 = attr_info->attr.value.ui32; break;
|
||||
case B_UINT64_TYPE:
|
||||
attr.value.ui64 = attr_info->attr.value.ui64; break;
|
||||
case B_STRING_TYPE:
|
||||
strlcpy(attr.value.string, attr_info->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;*/
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
// copy back to user space
|
||||
if (user_memcpy(buffer, &attr, sizeof(struct dev_attr)) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
return B_OK;
|
||||
}
|
||||
};
|
||||
return B_BAD_HANDLER;
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Device Manager
|
||||
|
||||
Execution and synchronization of driver hook calls.
|
||||
*/
|
||||
|
||||
|
||||
#include "device_manager_private.h"
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define TRACE_NOTIFICATIONS
|
||||
#ifdef TRACE_NOTIFICATIONS
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
/** notify driver that it's device got removed */
|
||||
|
||||
status_t
|
||||
dm_notify_unregistration(device_node_info *node)
|
||||
{
|
||||
driver_module_info *driver;
|
||||
bool loaded;
|
||||
char *module_name;
|
||||
void *cookie;
|
||||
status_t res;
|
||||
|
||||
res = pnp_get_attr_string(node, B_DRIVER_MODULE, &module_name, false);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
TRACE(("notify_device_removed(node: %p, consumer: %s)\n", node, module_name));
|
||||
|
||||
// block concurrent load/unload calls
|
||||
pnp_start_hook_call(node);
|
||||
|
||||
#if 0
|
||||
// don't notify driver if it doesn't know that the device was
|
||||
// published
|
||||
if (!node->init_finished)
|
||||
goto skip;
|
||||
#endif
|
||||
|
||||
// don't take node->loading into account - we want to know
|
||||
// whether driver is loaded, not whether it is about to get loaded
|
||||
loaded = node->load_count > 0;
|
||||
|
||||
if (loaded) {
|
||||
TRACE(("Already loaded\n"));
|
||||
driver = node->driver;
|
||||
cookie = node->cookie;
|
||||
} else {
|
||||
TRACE(("Not loaded yet\n"));
|
||||
|
||||
// driver is not loaded - load it temporarily without
|
||||
// calling init_device and pass <cookie>=NULL to tell
|
||||
// the driver about
|
||||
res = get_module(module_name, (module_info **)&driver);
|
||||
if (res < B_OK)
|
||||
goto err;
|
||||
|
||||
cookie = NULL;
|
||||
}
|
||||
|
||||
// this hook is optional, so ignore it silently if NULL
|
||||
if (driver->device_removed != NULL)
|
||||
driver->device_removed(node, cookie);
|
||||
else
|
||||
dprintf("Driver %s has no device_removed hook\n", module_name);
|
||||
|
||||
// if driver wasn't loaded, call its cleanup method
|
||||
if (!loaded) {
|
||||
// again: this hook is optional
|
||||
if (driver->device_cleanup != NULL)
|
||||
driver->device_cleanup(node);
|
||||
}
|
||||
|
||||
if (!loaded)
|
||||
put_module(module_name);
|
||||
|
||||
skip:
|
||||
pnp_finish_hook_call(node);
|
||||
free(module_name);
|
||||
return B_OK;
|
||||
|
||||
err:
|
||||
free(module_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/** start driver hook call; must be called before a
|
||||
* load/unload/notification hook is called
|
||||
*/
|
||||
|
||||
void
|
||||
pnp_start_hook_call(device_node_info *node)
|
||||
{
|
||||
bool blocked;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
blocked = ++node->num_waiting_hooks > 1;
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
if (blocked) {
|
||||
TRACE(("Hook call of %p is blocked\n", node));
|
||||
|
||||
acquire_sem(node->hook_sem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** start driver hook call; same pnp_start_hook_call(), but
|
||||
* node_lock must be hold
|
||||
*/
|
||||
|
||||
void
|
||||
pnp_start_hook_call_nolock(device_node_info *node)
|
||||
{
|
||||
if (++node->num_waiting_hooks > 1) {
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
TRACE(("Hook call of %p is blocked\n", node));
|
||||
acquire_sem(node->hook_sem);
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** finish driver hook call; must be called after a load/unload/notification
|
||||
* hook is called
|
||||
*/
|
||||
|
||||
void
|
||||
pnp_finish_hook_call(device_node_info *node)
|
||||
{
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
if (--node->num_waiting_hooks > 1) {
|
||||
TRACE(("Releasing other hook calls of %p\n", node));
|
||||
|
||||
release_sem(node->hook_sem);
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
|
||||
/** finish driver hook call; same pnp_finish_hook_call(), but
|
||||
* node_lock must be hold
|
||||
*/
|
||||
|
||||
void
|
||||
pnp_finish_hook_call_nolock(device_node_info *node)
|
||||
{
|
||||
if (--node->num_waiting_hooks > 1) {
|
||||
TRACE(("Releasing other hook calls of %p\n", node));
|
||||
|
||||
release_sem(node->hook_sem);
|
||||
}
|
||||
}
|
@ -1,243 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Device Manager
|
||||
|
||||
Expansion of patterns.
|
||||
Used to expand consumer/connector/device names etc.
|
||||
*/
|
||||
|
||||
|
||||
#include "device_manager_private.h"
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <TypeConstants.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
//#define TRACE_PATTERNS
|
||||
#ifdef TRACE_PATTERNS
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
/** expand node attribute
|
||||
* node - node to get attributes from
|
||||
* *pattern - name of attribute, ending with "%" (takes care of "^" sequences)
|
||||
* on return, *patterns points to trailing "%"
|
||||
* buffer - buffer for name
|
||||
* dst - string to write content to
|
||||
* node_lock must be hold
|
||||
*/
|
||||
|
||||
static status_t
|
||||
expand_attr(device_node_info *node, const char **pattern, char *buffer, char *dst)
|
||||
{
|
||||
device_attr_info *attr;
|
||||
const char *str;
|
||||
int buffer_len;
|
||||
|
||||
// extract attribute name
|
||||
buffer_len = 0;
|
||||
|
||||
for (str = *pattern; *str; ++str) {
|
||||
if (*str == '^' && *(str + 1) != 0)
|
||||
// copy escaped character directly
|
||||
++str;
|
||||
else if (*str == '%')
|
||||
break;
|
||||
|
||||
buffer[buffer_len++] = *str;
|
||||
}
|
||||
|
||||
*pattern = str;
|
||||
|
||||
buffer[buffer_len] = '\0';
|
||||
|
||||
// get attribute content and add it to path
|
||||
attr = pnp_find_attr_nolock(node, buffer, true, B_ANY_TYPE);
|
||||
if (attr == NULL) {
|
||||
dprintf("Reference to unknown attribute %s\n", buffer);
|
||||
return B_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
switch (attr->attr.type) {
|
||||
case B_UINT8_TYPE:
|
||||
sprintf(buffer, "%02x", attr->attr.value.ui8);
|
||||
strlcat(dst, buffer, PATH_MAX);
|
||||
break;
|
||||
case B_UINT16_TYPE:
|
||||
sprintf(buffer, "%04x", attr->attr.value.ui16);
|
||||
strlcat(dst, buffer, PATH_MAX);
|
||||
break;
|
||||
case B_UINT32_TYPE:
|
||||
sprintf(buffer, "%08lx", attr->attr.value.ui32);
|
||||
strlcat(dst, buffer, PATH_MAX);
|
||||
break;
|
||||
case B_UINT64_TYPE:
|
||||
sprintf(buffer, "%16Lx", attr->attr.value.ui64);
|
||||
strlcat(dst, buffer, PATH_MAX);
|
||||
break;
|
||||
case B_STRING_TYPE: {
|
||||
const char *str;
|
||||
|
||||
//strlcat(dst, "\"", PATH_MAX);
|
||||
|
||||
for (str = attr->attr.value.string; *str; ++str) {
|
||||
char ch;
|
||||
|
||||
ch = *str;
|
||||
if (ch >= 32 && ch <= 126 && ch != '/' && ch != '%' && ch != '"') {
|
||||
buffer[0] = ch;
|
||||
buffer[1] = 0;
|
||||
} else {
|
||||
// convert unprintable/forbidden characters to numbers
|
||||
sprintf(buffer, "%%%u%%", (unsigned char)ch);
|
||||
}
|
||||
|
||||
strlcat(dst, buffer, PATH_MAX);
|
||||
}
|
||||
|
||||
//strlcat(dst, "\"", PATH_MAX);
|
||||
break;
|
||||
}
|
||||
case B_RAW_TYPE:
|
||||
default:
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/** expand pattern.
|
||||
* the pattern is both expanded and the position of inidiual parts returned
|
||||
* pattern - pattern to expand
|
||||
* dest - expanded name is appended to this string
|
||||
* buffer - buffer of MAX_PATH+1 size supplied by caller
|
||||
* term_array - list of lengths of individual sub-names
|
||||
* with index 0 containing the length of the shortest and
|
||||
* num_parts-1 - length of the longest sub-name
|
||||
* <term_array> and <num_parts> can be NULL
|
||||
* node_lock must be hold
|
||||
*/
|
||||
|
||||
status_t
|
||||
pnp_expand_pattern(device_node_info *node, const char *pattern, char *dest,
|
||||
char *buffer, size_t *term_array, int *num_parts)
|
||||
{
|
||||
const char *str;
|
||||
int id;
|
||||
status_t res;
|
||||
int buffer_len = 0;
|
||||
|
||||
TRACE(("expand_pattern(pattern: %s)\n", pattern));
|
||||
|
||||
for (str = pattern, id = 0; *str; ++str) {
|
||||
switch (*str) {
|
||||
case '^':
|
||||
// character following "^" is copied directly
|
||||
// exception: if there is a trailing "^" at end of string,
|
||||
// we copy it directly
|
||||
if (*(str + 1) != 0)
|
||||
++str;
|
||||
|
||||
buffer[buffer_len++] = *str;
|
||||
break;
|
||||
|
||||
case '|':
|
||||
// end of one name part
|
||||
buffer[buffer_len] = '\0';
|
||||
buffer_len = 0;
|
||||
strlcat(dest, buffer, PATH_MAX);
|
||||
|
||||
TRACE(("%d: %s\n", id, dest));
|
||||
|
||||
if (term_array != NULL)
|
||||
term_array[id++] = strlen(dest);
|
||||
break;
|
||||
|
||||
case '%':
|
||||
// begin of place-holder
|
||||
buffer[buffer_len] = '\0';
|
||||
buffer_len = 0;
|
||||
strlcat(dest, buffer, PATH_MAX);
|
||||
|
||||
// skip "%"
|
||||
++str;
|
||||
|
||||
res = expand_attr(node, &str, buffer, dest);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
break;
|
||||
|
||||
default:
|
||||
// normal character
|
||||
buffer[buffer_len++] = *str;
|
||||
}
|
||||
|
||||
// avoid overflow of buffer
|
||||
if (buffer_len > PATH_MAX)
|
||||
buffer_len = PATH_MAX;
|
||||
}
|
||||
|
||||
// append trailing characters
|
||||
buffer[buffer_len] = 0;
|
||||
buffer_len = 0;
|
||||
strlcat(dest, buffer, PATH_MAX);
|
||||
|
||||
if (term_array != NULL)
|
||||
term_array[id++] = strlen(dest);
|
||||
|
||||
if (num_parts != NULL)
|
||||
*num_parts = id;
|
||||
|
||||
TRACE(("result: %s\n", dest));
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/** expand pattern as specified by attribute <attr_name>
|
||||
* node_lock must be hold
|
||||
*/
|
||||
|
||||
status_t
|
||||
pnp_expand_pattern_attr(device_node_info *node, const char *attr_name,
|
||||
char **expanded)
|
||||
{
|
||||
const char *pattern;
|
||||
status_t res;
|
||||
|
||||
TRACE(("pnp_expand_pattern_attr()\n"));
|
||||
|
||||
if (pnp_get_attr_string_nolock(node, attr_name, &pattern, false) != B_OK) {
|
||||
*expanded = strdup("");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
*expanded = malloc((PATH_MAX + 1) * 2);
|
||||
if (*expanded == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
**expanded = 0;
|
||||
res = pnp_expand_pattern(node, pattern,
|
||||
*expanded, *expanded + PATH_MAX + 1, NULL, NULL);
|
||||
|
||||
if (res != B_OK)
|
||||
free(*expanded);
|
||||
|
||||
return res;
|
||||
}
|
@ -1,382 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Device Manager
|
||||
|
||||
Device Node Registration.
|
||||
*/
|
||||
|
||||
|
||||
#include "device_manager_private.h"
|
||||
#include "dl_list.h"
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
//#define TRACE_REGISTRATION
|
||||
#ifdef TRACE_REGISTRATION
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
/** mark node being registered, so it can be accessed via load_driver() */
|
||||
|
||||
static status_t
|
||||
mark_node_registered(device_node_info *node)
|
||||
{
|
||||
status_t res;
|
||||
|
||||
TRACE(("mark_node_registered(%p)\n", node));
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
if (node->parent && !node->parent->registered) {
|
||||
TRACE(("Cannot mark registered: parent is already unregistered\n"));
|
||||
res = B_NOT_ALLOWED;
|
||||
} else {
|
||||
res = B_OK;
|
||||
node->registered = true;
|
||||
++node->ref_count;
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return res;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/** Checks whether the device is redetected and removes any other
|
||||
* device that is on same connection and has the same device identifier.
|
||||
* \a redetected is set true if device is already registered;
|
||||
* the node must not yet be linked into parent node's children list
|
||||
*/
|
||||
|
||||
static status_t
|
||||
is_device_redetected(device_node_info *node, device_node_info *parent, bool *redetected)
|
||||
{
|
||||
device_node_info *sibling;
|
||||
status_t res;
|
||||
bool found = false;
|
||||
bool same_device = false;
|
||||
bool unregister_sibling;
|
||||
|
||||
TRACE(("Checking %p, parent %p\n", node, parent));
|
||||
|
||||
*redetected = false;
|
||||
|
||||
if (parent == NULL) {
|
||||
TRACE(("done\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// we keep the lock very long, but this is the only way to be sure that
|
||||
// no one else (un)-registers a conflicting note during the tests
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
{
|
||||
char *connection, *device_identifier;
|
||||
|
||||
// get our connection and device id...
|
||||
if (pnp_expand_pattern_attr(node, PNP_DRIVER_CONNECTION, &connection) != B_OK)
|
||||
connection = strdup("");
|
||||
|
||||
if (pnp_expand_pattern_attr(node, PNP_DRIVER_DEVICE_IDENTIFIER,
|
||||
&device_identifier) != B_OK)
|
||||
device_identifier = strdup("");
|
||||
|
||||
TRACE(("connection: %s, device_identifier: %s\n", connection, device_identifier));
|
||||
|
||||
// ...and compare it with our siblings
|
||||
for (sibling = parent->children; sibling != NULL; sibling = sibling->siblings_next) {
|
||||
char *sibling_connection, *sibling_device_identifier;
|
||||
bool same_connection;
|
||||
|
||||
// ignore dead siblings
|
||||
if (!sibling->registered)
|
||||
break;
|
||||
|
||||
TRACE(("%p\n", sibling));
|
||||
|
||||
if (pnp_expand_pattern_attr(sibling, PNP_DRIVER_CONNECTION,
|
||||
&sibling_connection) != B_OK)
|
||||
sibling_connection = strdup("");
|
||||
|
||||
same_connection = !strcmp(connection, sibling_connection);
|
||||
free(sibling_connection);
|
||||
|
||||
if (!same_connection)
|
||||
continue;
|
||||
|
||||
// found a device on same connection - check whether it's the same device
|
||||
found = true;
|
||||
|
||||
if (pnp_expand_pattern_attr(sibling, PNP_DRIVER_DEVICE_IDENTIFIER,
|
||||
&sibling_device_identifier) != B_OK)
|
||||
sibling_device_identifier = strdup("");
|
||||
|
||||
TRACE(("device %s is on same connection\n", sibling_device_identifier));
|
||||
|
||||
same_device = !strcmp(device_identifier, sibling_device_identifier);
|
||||
free(sibling_device_identifier);
|
||||
break;
|
||||
}
|
||||
|
||||
free(connection);
|
||||
free(device_identifier);
|
||||
}
|
||||
|
||||
if (found) {
|
||||
TRACE(("found device on same connection\n"));
|
||||
if (!same_device) {
|
||||
// there is a different device on the same connection -
|
||||
// kick out the old device
|
||||
TRACE(("different device was detected\n"));
|
||||
|
||||
*redetected = false;
|
||||
unregister_sibling = true;
|
||||
} else {
|
||||
const char *driver_name, *old_driver_name;
|
||||
const char *driver_type, *old_driver_type;
|
||||
|
||||
TRACE(("it's the same device\n"));
|
||||
|
||||
// found same device on same connection - make sure it's still the same driver
|
||||
if (pnp_get_attr_string_nolock(node, PNP_DRIVER_DRIVER, &driver_name, false) != B_OK
|
||||
|| pnp_get_attr_string_nolock(sibling, PNP_DRIVER_DRIVER, &old_driver_name, false) != B_OK
|
||||
|| pnp_get_attr_string_nolock(node, PNP_DRIVER_TYPE, &driver_type, false) != B_OK
|
||||
|| pnp_get_attr_string_nolock(sibling, PNP_DRIVER_TYPE, &old_driver_type, false) != B_OK) {
|
||||
// these attributes _must_ be there, so this cannot happen
|
||||
// (but we are prepared)
|
||||
res = B_ERROR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (strcmp(driver_name, old_driver_name) != 0
|
||||
|| strcmp(driver_type, old_driver_type) != 0) {
|
||||
// driver has changed - replace device node
|
||||
TRACE(("driver got replaced\n"));
|
||||
|
||||
*redetected = false;
|
||||
unregister_sibling = true;
|
||||
} else {
|
||||
// it's the same driver for the same device
|
||||
TRACE(("redetected device\n"));
|
||||
|
||||
*redetected = true;
|
||||
unregister_sibling = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no collision on the device's connection
|
||||
TRACE(("no collision\n"));
|
||||
|
||||
*redetected = false;
|
||||
unregister_sibling = false;
|
||||
}
|
||||
|
||||
if (*redetected && sibling->verifying)
|
||||
sibling->redetected = true;
|
||||
|
||||
if (unregister_sibling) {
|
||||
// increase ref_count to make sure node still exists
|
||||
// when gNodeLock has been released
|
||||
++sibling->ref_count;
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
pnp_unregister_device(sibling);
|
||||
pnp_remove_node_ref_nolock(sibling);
|
||||
} else
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return B_OK;
|
||||
|
||||
err:
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static status_t
|
||||
push_node_on_stack(struct list *list, device_node_info *node)
|
||||
{
|
||||
struct node_entry *entry = (struct node_entry *)malloc(sizeof(struct node_entry));
|
||||
if (entry == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
dm_get_node_nolock(node);
|
||||
entry->node = node;
|
||||
list_add_item(list, entry);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static device_node_info *
|
||||
pop_node_from_stack(struct list *list)
|
||||
{
|
||||
device_node_info *node;
|
||||
|
||||
struct node_entry *entry = (struct node_entry *)list_remove_head_item(list);
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
|
||||
node = entry->node;
|
||||
free(entry);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// Public functions part of the module API
|
||||
|
||||
|
||||
/** Register device node.
|
||||
* In terms of I/O resources: if registration fails, they are freed; reason is
|
||||
* that they may have been transferred to node before error and back-transferring
|
||||
* them would be complicated.
|
||||
*/
|
||||
|
||||
status_t
|
||||
dm_register_node(device_node_handle parent, const device_attr *attrs,
|
||||
const io_resource_handle *ioResources, device_node_handle *_node)
|
||||
{
|
||||
device_node_info *newNode;
|
||||
char *driverName = NULL;
|
||||
status_t status = B_OK;
|
||||
struct list stack;
|
||||
|
||||
status = dm_allocate_node(attrs, ioResources, &newNode);
|
||||
if (status != B_OK) {
|
||||
// always "consume" I/O resources
|
||||
dm_release_io_resources(ioResources);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (pnp_get_attr_string(newNode, B_DRIVER_MODULE, &driverName, false) != B_OK)
|
||||
status = B_BAD_VALUE;
|
||||
|
||||
TRACE(("dm_register_node(driver = %s)\n", driverName));
|
||||
|
||||
free(driverName);
|
||||
|
||||
if (status != B_OK) {
|
||||
dprintf("device_manager: Missing driver module name.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
// transfer resources to device, unregistering all colliding devices;
|
||||
// this cannot fail - we've already allocated the resource handle array
|
||||
dm_assign_io_resources(newNode, ioResources);
|
||||
|
||||
// make it public
|
||||
dm_add_child_node(parent, newNode);
|
||||
|
||||
// The following is done to reduce the stack usage of deeply nested
|
||||
// child device nodes.
|
||||
// There is no other need to delay the complete registration process
|
||||
// the way done here. This approach is also slightly different as
|
||||
// the registration might fail later than it used in case of errors.
|
||||
|
||||
if (parent == NULL || parent->registered) {
|
||||
// register all device nodes not yet registered - build a list
|
||||
// that contain all of them, and then empty it one by one (during
|
||||
// iteration, there might be new nodes added)
|
||||
|
||||
device_node_info *node = newNode;
|
||||
|
||||
list_init(&stack);
|
||||
push_node_on_stack(&stack, newNode);
|
||||
|
||||
while ((node = pop_node_from_stack(&stack)) != NULL) {
|
||||
device_node_info *child = NULL;
|
||||
|
||||
// register all fixed child device nodes as well
|
||||
status = dm_register_fixed_child_devices(node);
|
||||
if (status != B_OK)
|
||||
goto err2;
|
||||
|
||||
node->registered = true;
|
||||
|
||||
// and now let it register all child devices, if it's a bus
|
||||
status = dm_register_child_devices(node);
|
||||
if (status != B_OK)
|
||||
goto err2;
|
||||
|
||||
// push all new nodes on the stack
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
while ((child = (device_node_info *)list_get_next_item(&node->children, child)) != NULL) {
|
||||
if (!child->registered)
|
||||
push_node_on_stack(&stack, child);
|
||||
}
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
dm_put_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (_node)
|
||||
*_node = newNode;
|
||||
|
||||
return B_OK;
|
||||
|
||||
err2:
|
||||
{
|
||||
// nodes popped from the stack also need their reference count released
|
||||
device_node_info *node;
|
||||
while ((node = pop_node_from_stack(&stack)) != NULL) {
|
||||
dm_put_node(node);
|
||||
}
|
||||
}
|
||||
err:
|
||||
// this also releases the I/O resources
|
||||
dm_put_node(newNode);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/** Unregister device node.
|
||||
* This also makes sure that all children of this node are unregistered.
|
||||
*/
|
||||
|
||||
status_t
|
||||
dm_unregister_node(device_node_info *node)
|
||||
{
|
||||
device_node_info *child, *nextChild;
|
||||
|
||||
TRACE(("pnp_unregister_device(%p)\n", node));
|
||||
|
||||
if (node == NULL)
|
||||
return B_OK;
|
||||
|
||||
// unregistered node and all children
|
||||
benaphore_lock(&gNodeLock);
|
||||
if (node->parent != NULL)
|
||||
list_remove_item(&node->parent->children, node);
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
// tell driver about their unregistration
|
||||
dm_notify_unregistration(node);
|
||||
|
||||
// unregister children recursively
|
||||
for (child = list_get_first_item(&node->children); child; child = nextChild) {
|
||||
nextChild = (device_node_info *)child->siblings.next;
|
||||
dm_unregister_node(child);
|
||||
}
|
||||
|
||||
dm_put_node(node);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2002/2003, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Device Manager
|
||||
|
||||
Interface of device root module.
|
||||
|
||||
This module creates the root devices (usually PCI and ISA) to
|
||||
initiate the PnP device scan.
|
||||
*/
|
||||
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <Drivers.h>
|
||||
|
||||
#include "device_manager_private.h"
|
||||
|
||||
|
||||
#define TRACE_ROOT_NODE
|
||||
#ifdef TRACE_ROOT_NODE
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
#define PNP_ROOT_MODULE_NAME "system/devices_root/driver_v1"
|
||||
|
||||
|
||||
void
|
||||
dm_init_root_node(void)
|
||||
{
|
||||
device_attr attrs[] = {
|
||||
{ B_DRIVER_MODULE, B_STRING_TYPE, { string: PNP_ROOT_MODULE_NAME }},
|
||||
{ B_DRIVER_PRETTY_NAME, B_STRING_TYPE, { string: "Devices Root" }},
|
||||
|
||||
{ B_DRIVER_BUS, B_STRING_TYPE, { string: "root" }},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
if (dm_register_node(NULL, attrs, NULL, &gRootNode) != B_OK) {
|
||||
// ToDo: don't panic for now
|
||||
dprintf("Cannot register Devices Root Node\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
root_init_driver(device_node_handle node, void *user_cookie, void **_cookie)
|
||||
{
|
||||
*_cookie = NULL;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
root_uninit_driver(void *cookie)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
root_std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
case B_MODULE_UNINIT:
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
driver_module_info gDeviceRootModule = {
|
||||
{
|
||||
PNP_ROOT_MODULE_NAME,
|
||||
0,
|
||||
root_std_ops,
|
||||
},
|
||||
|
||||
NULL, // supported devices
|
||||
NULL, // register device
|
||||
root_init_driver,
|
||||
root_uninit_driver,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Scanning for consumers.
|
||||
|
||||
Logic to execute (re)scanning of nodes.
|
||||
|
||||
During initial registration, problems with fixed consumers are
|
||||
fatal. During rescan, this is ignored
|
||||
(TODO: should this be done more consistently?)
|
||||
*/
|
||||
|
||||
|
||||
#include "device_manager_private.h"
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
//#define TRACE_SCAN
|
||||
#ifdef TRACE_SCAN
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
static status_t scan(device_node_info *node, bool rescan);
|
||||
|
||||
|
||||
static int32 sRescanning = 0;
|
||||
|
||||
|
||||
#if 0
|
||||
// execute deferred probing of children
|
||||
// (node_lock must be hold)
|
||||
|
||||
void
|
||||
pnp_probe_waiting_children_nolock(device_node_info *node)
|
||||
{
|
||||
TRACE(("execute deferred probing of parent %p\n", node));
|
||||
|
||||
while (node->unprobed_children) {
|
||||
device_node_info *child = node->unprobed_children;
|
||||
|
||||
REMOVE_DL_LIST(child, node->unprobed_children, unprobed_);
|
||||
|
||||
// child may have been removed meanwhile
|
||||
if (child->registered) {
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
if (pnp_initial_scan(child) == B_OK)
|
||||
pnp_load_driver_automatically(node, false);
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
}
|
||||
|
||||
// reference count was increment to keep node alive in wannabe list;
|
||||
// this is not necessary anymore
|
||||
pnp_remove_node_ref(node);
|
||||
}
|
||||
|
||||
TRACE((".. done.\n"));
|
||||
}
|
||||
|
||||
|
||||
// execute deferred probing of children
|
||||
|
||||
void
|
||||
pnp_probe_waiting_children(device_node_info *node)
|
||||
{
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
pnp_probe_waiting_children_nolock(node);
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/** ask bus to (re-)scan for connected devices */
|
||||
|
||||
static void
|
||||
scan_bus(device_node_info *node, bool rescan)
|
||||
{
|
||||
// busses can register their children themselves
|
||||
bus_module_info *interface;
|
||||
status_t status;
|
||||
void *cookie;
|
||||
|
||||
// load driver during rescan
|
||||
|
||||
status = pnp_load_driver(node, NULL, (driver_module_info **)&interface, &cookie);
|
||||
if (status < B_OK) {
|
||||
// we couldn't load a driver that had already registered itself...
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE(("scan bus\n"));
|
||||
|
||||
// block other notifications (currently, only "removed" could be
|
||||
// called as driver is/will stay loaded and scanning the bus
|
||||
// concurrently has been assured to not happen)
|
||||
pnp_start_hook_call(node);
|
||||
|
||||
if (rescan) {
|
||||
if (interface->rescan_bus != NULL)
|
||||
interface->rescan_bus(cookie);
|
||||
} else {
|
||||
if (interface->register_child_devices != NULL)
|
||||
interface->register_child_devices(cookie);
|
||||
}
|
||||
|
||||
pnp_finish_hook_call(node);
|
||||
|
||||
pnp_unload_driver(node);
|
||||
|
||||
// time to execute delayed probing
|
||||
// pnp_probe_waiting_children(node);
|
||||
}
|
||||
|
||||
|
||||
/** recursively scan children of node (using depth-scan).
|
||||
* node_lock must be hold
|
||||
*/
|
||||
|
||||
static status_t
|
||||
recursive_scan(device_node_info *node)
|
||||
{
|
||||
device_node_info *child = NULL;
|
||||
status_t status;
|
||||
|
||||
TRACE(("recursive_scan(node = %p)\n", node));
|
||||
|
||||
while ((child = list_get_next_item(&node->children, child)) != NULL) {
|
||||
dm_get_node_nolock(child);
|
||||
|
||||
// during rescan, we must release node_lock to not deadlock
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
status = scan(child, true);
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
// unlock current child as it's not accessed anymore
|
||||
dm_put_node_nolock(child);
|
||||
|
||||
// ignore errors because of touching unregistered nodes
|
||||
if (status != B_OK && status != B_NAME_NOT_FOUND)
|
||||
return status;
|
||||
}
|
||||
|
||||
// no need unlock last child (don't be loop already)
|
||||
TRACE(("recursive_scan(): done\n"));
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/** (re)scan for child nodes
|
||||
*/
|
||||
|
||||
static status_t
|
||||
scan(device_node_info *node, bool rescan)
|
||||
{
|
||||
status_t status = B_OK;
|
||||
uint8 dummy;
|
||||
bool isBus;
|
||||
|
||||
TRACE(("scan(node = %p, mode: %s)\n", node, rescan ? "rescan" : "register"));
|
||||
|
||||
isBus = pnp_get_attr_uint8(node, PNP_BUS_IS_BUS, &dummy, false) == B_OK;
|
||||
|
||||
// do the real thing - scan node
|
||||
if (!rescan) {
|
||||
uint8 loadDriversLater = false;
|
||||
char *deviceType = NULL;
|
||||
pnp_get_attr_uint8(node, B_DRIVER_FIND_DEVICES_ON_DEMAND, &loadDriversLater, false);
|
||||
pnp_get_attr_string(node, B_DRIVER_DEVICE_TYPE, &deviceType, false);
|
||||
|
||||
if (isBus)
|
||||
scan_bus(node, false);
|
||||
|
||||
// ask possible children to register their nodes
|
||||
|
||||
if (!loadDriversLater && deviceType == NULL) {
|
||||
status = dm_register_dynamic_child_devices(node);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
free(deviceType);
|
||||
} else {
|
||||
if (!isBus)
|
||||
status = dm_register_dynamic_child_devices(node);
|
||||
}
|
||||
|
||||
// scan children recursively;
|
||||
// keep the node_lock to make sure noone removes children meanwhile
|
||||
if (rescan && isBus) {
|
||||
scan_bus(node, true);
|
||||
benaphore_lock(&gNodeLock);
|
||||
status = recursive_scan(node);
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
TRACE(("scan(): done (%p) - %s\n", node, strerror(status)));
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
/** Rescan device node (only works if it's a bus) */
|
||||
|
||||
status_t
|
||||
dm_rescan(device_node_info *node)
|
||||
{
|
||||
status_t err;
|
||||
|
||||
// only allow a single rescan at a time
|
||||
if (atomic_add(&sRescanning, 1) > 1) {
|
||||
dprintf("dm_rescan already scanning\n");
|
||||
atomic_add(&sRescanning, -1);
|
||||
return B_BUSY;
|
||||
}
|
||||
|
||||
err = scan(node, true);
|
||||
atomic_add(&sRescanning, -1);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/** execute registration of fully constructed node */
|
||||
|
||||
status_t
|
||||
dm_register_child_devices(device_node_info *node)
|
||||
{
|
||||
return scan(node, false);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user