Start of the integration of Thomas' pnp_manager (which is now called device_manager).
Nothing is tested right now, and it's not yet complete, but it's changed so that it does compile in our tree (and looks a bit more like the other files, too). git-svn-id: file:///srv/svn/repos/haiku/trunk/current@7345 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
8addb7c3d8
commit
fe01b8fa14
22
src/kernel/core/device_manager/Jamfile
Normal file
22
src/kernel/core/device_manager/Jamfile
Normal file
@ -0,0 +1,22 @@
|
||||
SubDir OBOS_TOP src kernel core device_manager ;
|
||||
|
||||
UsePrivateHeaders [ FDirName kernel boot platform $(OBOS_BOOT_PLATFORM) ] ;
|
||||
UsePrivateHeaders [ FDirName kernel util ] ;
|
||||
|
||||
KernelMergeObject kernel_device_manager.o :
|
||||
attributes.c
|
||||
# boot_hack.c
|
||||
device_manager.c
|
||||
# driver_loader.c
|
||||
id_generator.c
|
||||
io_resources.c
|
||||
# module_loader.c
|
||||
nodes.c
|
||||
# notifications.c
|
||||
# patterns.c
|
||||
# probe.c
|
||||
registration.c
|
||||
# scan.c
|
||||
:
|
||||
-fno-pic -Wno-unused -D_KERNEL_MODE
|
||||
;
|
489
src/kernel/core/device_manager/attributes.c
Normal file
489
src/kernel/core/device_manager/attributes.c
Normal file
@ -0,0 +1,489 @@
|
||||
/*
|
||||
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the NewOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of PnP Manager
|
||||
Node attributes handling.
|
||||
*/
|
||||
|
||||
|
||||
#include "device_manager_private.h"
|
||||
#include "dl_list.h"
|
||||
#include <pnp_bus.h>
|
||||
|
||||
#include <TypeConstants.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static bool is_fixed_attribute(const char *name);
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_uint8(pnp_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(pnp_node_handle node, const char *name,
|
||||
uint8 *value, bool recursive)
|
||||
{
|
||||
pnp_node_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(pnp_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(pnp_node_handle node, const char *name,
|
||||
uint16 *value, bool recursive)
|
||||
{
|
||||
pnp_node_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(pnp_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(pnp_node_handle node, const char *name,
|
||||
uint32 *value, bool recursive)
|
||||
{
|
||||
pnp_node_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(pnp_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(pnp_node_handle node, const char *name,
|
||||
uint64 *value, bool recursive)
|
||||
{
|
||||
pnp_node_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(pnp_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(pnp_node_handle node, const char *name,
|
||||
const char **value, bool recursive)
|
||||
{
|
||||
pnp_node_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(pnp_node_handle node, const char *name,
|
||||
void **data, size_t *len, bool recursive)
|
||||
{
|
||||
const void *orig_data;
|
||||
status_t status;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
status = pnp_get_attr_raw_nolock( node, name, &orig_data, len, recursive );
|
||||
|
||||
if (status == B_OK) {
|
||||
void *tmp_data = malloc(*len);
|
||||
if (tmp_data != NULL) {
|
||||
memcpy(tmp_data, orig_data, *len);
|
||||
*data = tmp_data;
|
||||
} else
|
||||
status = B_NO_MEMORY;
|
||||
}
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
pnp_get_attr_raw_nolock(pnp_node_handle node, const char *name,
|
||||
const void **data, size_t *len, bool recursive)
|
||||
{
|
||||
pnp_node_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;
|
||||
*len = attr->attr.value.raw.len;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// free node attribute
|
||||
|
||||
void
|
||||
pnp_free_node_attr(pnp_node_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 pnp_node_attr *src, pnp_node_attr_info **dest_out)
|
||||
{
|
||||
pnp_node_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.len);
|
||||
if (dest->attr.value.raw.data == NULL) {
|
||||
res = B_NO_MEMORY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dest->attr.value.raw.len = src->value.raw.len;
|
||||
memcpy(dest->attr.value.raw.data, src->value.raw.data,
|
||||
src->value.raw.len);
|
||||
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(pnp_node_handle node, pnp_node_attr_handle *attr)
|
||||
{
|
||||
pnp_node_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;
|
||||
}
|
||||
|
||||
++next->ref_count;
|
||||
*attr = next;
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// public: release attribute of node explicitely
|
||||
|
||||
status_t
|
||||
pnp_release_attr(pnp_node_handle node,pnp_node_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(pnp_node_attr_handle attr, const pnp_node_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(pnp_node_handle node, pnp_node_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(pnp_node_handle node, const char *name)
|
||||
{
|
||||
pnp_node_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(pnp_node_handle node, const pnp_node_attr *attr)
|
||||
{
|
||||
pnp_node_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 {
|
||||
pnp_node_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[] = {
|
||||
PNP_DRIVER_DRIVER, // never change driver
|
||||
PNP_DRIVER_TYPE, // never change type
|
||||
PNP_BUS_IS_BUS, // never switch between bus/not bus mode
|
||||
PNP_DRIVER_ALWAYS_LOADED // don't confuse us by changing auto-load
|
||||
};
|
||||
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
|
||||
|
||||
pnp_node_attr_info *
|
||||
pnp_find_attr_nolock(pnp_node_handle node, const char *name,
|
||||
bool recursive, type_code type )
|
||||
{
|
||||
do {
|
||||
pnp_node_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;
|
||||
}
|
||||
|
167
src/kernel/core/device_manager/device_manager.c
Normal file
167
src/kernel/core/device_manager/device_manager.c
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of PnP Manager
|
||||
|
||||
Main file.
|
||||
*/
|
||||
|
||||
#include "device_manager_private.h"
|
||||
|
||||
#include <KernelExport.h>
|
||||
//#include <driver_settings.h>
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <string.h>
|
||||
//#include <stdio.h>
|
||||
//#include <dirent.h>
|
||||
|
||||
//#define TRACE_DEVICE_MANAGER
|
||||
#ifdef TRACE_DEVICE_MANAGER
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
benaphore node_lock;
|
||||
pnp_node_info *node_list;
|
||||
|
||||
bool disable_useraddons;
|
||||
|
||||
//int pnp_load_wait_count;
|
||||
//sem_id pnp_load_wait_sem;
|
||||
int pnp_resource_wait_count;
|
||||
sem_id pnp_resource_wait_sem;
|
||||
|
||||
io_resource_info *io_mem_list, *io_port_list, *isa_dma_list;
|
||||
|
||||
#if 0
|
||||
void
|
||||
check_settings()
|
||||
{
|
||||
void *settings;
|
||||
|
||||
settings = load_driver_settings( B_SAFEMODE_DRIVER_SETTINGS );
|
||||
disable_useraddons = get_driver_boolean_parameter( settings, "disableuseraddons", 0, 1 );
|
||||
unload_driver_settings(settings);
|
||||
|
||||
// if( disable_useraddons )
|
||||
// SHOW_INFO0( 2, "User pnp drivers disabled" );
|
||||
}
|
||||
#endif
|
||||
|
||||
static status_t
|
||||
init()
|
||||
{
|
||||
status_t status;
|
||||
|
||||
TRACE(("device manager init\n"));
|
||||
|
||||
status = id_generator_init();
|
||||
if (status < B_OK) {
|
||||
panic("could not initialize ID generator\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
status = nodes_init();
|
||||
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;
|
||||
}
|
||||
|
||||
// node_list = NULL;
|
||||
/* pnp_load_wait_count = 0;*/
|
||||
pnp_resource_wait_count = 0;
|
||||
io_mem_list = io_port_list = isa_dma_list = NULL;
|
||||
pnp_fs_emulation_nesting = 0;
|
||||
|
||||
return B_OK;
|
||||
/*
|
||||
err3:
|
||||
delete_sem( pnp_load_wait_sem );
|
||||
err2:
|
||||
DELETE_BEN( &node_lock );
|
||||
err:
|
||||
DELETE_BEN( &generator_lock );
|
||||
return res;*/
|
||||
}
|
||||
|
||||
#if 0
|
||||
static status_t
|
||||
uninit()
|
||||
{
|
||||
SHOW_FLOW0( 0, "" );
|
||||
|
||||
DELETE_BEN( &node_lock );
|
||||
DELETE_BEN( &generator_lock );
|
||||
/* delete_sem( pnp_load_wait_sem );*/
|
||||
delete_sem( pnp_resource_wait_sem );
|
||||
return B_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static status_t
|
||||
std_ops(int32 op, ...)
|
||||
{
|
||||
switch( op ) {
|
||||
case B_MODULE_INIT:
|
||||
return init();
|
||||
|
||||
case B_MODULE_UNINIT:
|
||||
return B_OK;
|
||||
// return uninit();
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
device_manager_info gDeviceManagerModule = {
|
||||
{
|
||||
DEVICE_MANAGER_MODULE_NAME,
|
||||
0,
|
||||
std_ops
|
||||
},
|
||||
|
||||
pnp_load_driver,
|
||||
pnp_unload_driver,
|
||||
|
||||
pnp_rescan,
|
||||
|
||||
pnp_register_device,
|
||||
pnp_unregister_device,
|
||||
|
||||
pnp_acquire_io_resources,
|
||||
pnp_release_io_resources,
|
||||
|
||||
pnp_find_device,
|
||||
|
||||
pnp_create_id,
|
||||
pnp_free_id,
|
||||
|
||||
pnp_get_parent,
|
||||
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
|
||||
};
|
||||
|
260
src/kernel/core/device_manager/device_manager_private.h
Normal file
260
src/kernel/core/device_manager/device_manager_private.h
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Device Manager
|
||||
Internal header.
|
||||
*/
|
||||
|
||||
#include <device_manager.h>
|
||||
#include <util/list.h>
|
||||
|
||||
#include <lock.h>
|
||||
|
||||
#define GENERATOR_MAX_ID 64
|
||||
#define UNIVERSAL_SUBDIR "/universal"
|
||||
#define GENERIC_SUBDIR "/generic"
|
||||
|
||||
// driver registration directories
|
||||
#define SYSTEM_DRIVER_REGISTRATION "/boot/beos/add-ons/kernel/"PNP_DIR"registration/"
|
||||
#define COMMON_DRIVER_REGISTRATION "/boot/home/config/add-ons/kernel/"PNP_DIR"registration/"
|
||||
|
||||
// module directories
|
||||
#define SYSTEM_MODULES_DIR "/boot/beos/add-ons/kernel/"
|
||||
#define COMMON_MODULES_DIR "/boot/home/config/add-ons/kernel/"
|
||||
|
||||
#define PNP_BOOT_LINKS "pnp_bootlinks"
|
||||
|
||||
|
||||
// info about ID generator
|
||||
typedef struct id_generator {
|
||||
struct list_link link;
|
||||
struct id_generator *prev, *next;
|
||||
int ref_count;
|
||||
char *name;
|
||||
uint32 num_ids;
|
||||
uint8 alloc_map[(GENERATOR_MAX_ID + 7) / 8];
|
||||
} id_generator;
|
||||
|
||||
|
||||
// info about a PnP node
|
||||
typedef struct pnp_node_info {
|
||||
struct pnp_node_info *prev, *next;
|
||||
struct pnp_node_info *children_prev, *children_next;
|
||||
struct pnp_node_info *notify_prev, *notify_next;
|
||||
struct pnp_node_info *parent;
|
||||
struct pnp_node_info *children;
|
||||
int ref_count; // reference count; see registration.c
|
||||
pnp_driver_info *driver;
|
||||
void *cookie;
|
||||
bool registered; // true, if device is officially existent
|
||||
bool init_finished; // true, if publish_device has been completed
|
||||
// (sync with loader_lock)
|
||||
bool blocked_by_rescan; // true, if device is blocked because of rescan
|
||||
bool verifying; // true, if driver is being verified by rescan
|
||||
bool redetected; // true, if driver was redetected during rescan
|
||||
int num_waiting_hooks; // number of waiting hook calls
|
||||
sem_id hook_sem; // sem for waiting hook calls
|
||||
int load_block_count; // load-block nest count
|
||||
int num_blocked_loads; // number of waiting load calls
|
||||
sem_id load_block_sem; // sem for waiting load calls
|
||||
int load_count; // # of finished load_driver() calls
|
||||
int loading; // # of active (un)load_driver() calls
|
||||
uint rescan_depth; // > 0 if scanning is active
|
||||
struct pnp_node_attr_info *attributes; // list of attributes
|
||||
uint num_io_resources; // number of I/O resources
|
||||
io_resource_handle *io_resources; // array of I/O resource (NULL-terminated)
|
||||
int defer_probing; // > 0 defer probing for consumers of children
|
||||
bool automatically_loaded; // loaded automatically because PNP_DRIVER_ALWAYS_LOADED
|
||||
struct pnp_node_info *unprobed_children; // list of un-probed children
|
||||
struct pnp_node_info *unprobed_prev, *unprobed_next; // link for unprobed_children
|
||||
} pnp_node_info;
|
||||
|
||||
|
||||
// info about a node attribute
|
||||
typedef struct pnp_node_attr_info {
|
||||
struct pnp_node_attr_info *prev, *next;
|
||||
int ref_count; // reference count
|
||||
pnp_node_attr attr;
|
||||
} pnp_node_attr_info;
|
||||
|
||||
|
||||
// I/O resource
|
||||
typedef struct io_resource_info {
|
||||
struct io_resource_info *prev, *next;
|
||||
pnp_node_info *owner; // associated node; NULL for temporary allocation
|
||||
io_resource resource; // info about actual resource
|
||||
} io_resource_info;
|
||||
|
||||
|
||||
extern status_t id_generator_init(void);
|
||||
extern status_t nodes_init(void);
|
||||
|
||||
// global lock
|
||||
// whenever you do something in terms of nodes, grab it
|
||||
extern benaphore gNodeLock;
|
||||
// list of nodes (includes removed devices!)
|
||||
extern pnp_node_info *node_list;
|
||||
|
||||
// true, if user addons are disabled via safemode
|
||||
extern bool disable_useraddons;
|
||||
|
||||
|
||||
// # of allocate_io_resource() calls waiting for a retry
|
||||
extern int pnp_resource_wait_count;
|
||||
// semaphore for allocate_io_resource() call retry
|
||||
extern sem_id pnp_resource_wait_sem;
|
||||
|
||||
// list of driver registration directories
|
||||
extern const char *pnp_registration_dirs[];
|
||||
|
||||
|
||||
// list of I/O memory, I/O ports and ISA DMA channels
|
||||
extern io_resource_info *io_mem_list, *io_port_list, *isa_dma_list;
|
||||
|
||||
|
||||
// nesting of pnp_load_boot_links calls
|
||||
int pnp_fs_emulation_nesting;
|
||||
|
||||
|
||||
// attributes.c
|
||||
|
||||
pnp_node_attr_info *pnp_find_attr_nolock( pnp_node_handle node, const char *name,
|
||||
bool recursive, type_code type );
|
||||
status_t pnp_get_attr_uint8( pnp_node_handle node, const char *name,
|
||||
uint8 *value, bool recursive );
|
||||
status_t pnp_get_attr_uint8_nolock( pnp_node_handle node, const char *name,
|
||||
uint8 *value, bool recursive );
|
||||
status_t pnp_get_attr_uint16( pnp_node_handle node, const char *name,
|
||||
uint16 *value, bool recursive );
|
||||
status_t pnp_get_attr_uint16_nolock( pnp_node_handle node, const char *name,
|
||||
uint16 *value, bool recursive );
|
||||
status_t pnp_get_attr_uint32( pnp_node_handle node, const char *name,
|
||||
uint32 *value, bool recursive );
|
||||
status_t pnp_get_attr_uint32_nolock( pnp_node_handle node, const char *name,
|
||||
uint32 *value, bool recursive );
|
||||
status_t pnp_get_attr_uint64( pnp_node_handle node, const char *name,
|
||||
uint64 *value, bool recursive );
|
||||
status_t pnp_get_attr_uint64_nolock( pnp_node_handle node, const char *name,
|
||||
uint64 *value, bool recursive );
|
||||
status_t pnp_get_attr_string( pnp_node_handle node, const char *name,
|
||||
char **value, bool recursive );
|
||||
status_t pnp_get_attr_string_nolock( pnp_node_handle node, const char *name,
|
||||
const char **value, bool recursive );
|
||||
status_t pnp_get_attr_raw( pnp_node_handle node, const char *name,
|
||||
void **data, size_t *len, bool recursive );
|
||||
status_t pnp_get_attr_raw_nolock( pnp_node_handle node, const char *name,
|
||||
const void **data, size_t *len, bool recursive );
|
||||
void pnp_free_node_attr( pnp_node_attr_info *attr );
|
||||
status_t pnp_duplicate_node_attr(
|
||||
const pnp_node_attr *src, pnp_node_attr_info **dest_out );
|
||||
status_t pnp_get_next_attr(
|
||||
pnp_node_handle node, pnp_node_attr_handle *attr );
|
||||
status_t pnp_release_attr(
|
||||
pnp_node_handle node, pnp_node_attr_handle attr );
|
||||
status_t pnp_retrieve_attr(
|
||||
pnp_node_attr_handle attr, const pnp_node_attr **attr_content );
|
||||
status_t pnp_write_attr(
|
||||
pnp_node_handle node, const pnp_node_attr *attr );
|
||||
void pnp_remove_attr_int( pnp_node_handle node, pnp_node_attr_info *attr );
|
||||
status_t pnp_remove_attr(pnp_node_handle node, const char *name);
|
||||
|
||||
|
||||
// boot_hack.c
|
||||
#if 0
|
||||
void pnp_load_boot_links( void );
|
||||
void pnp_unload_boot_links( void );
|
||||
char *pnp_boot_safe_realpath(
|
||||
const char *file_name, char *resolved_path );
|
||||
int pnp_boot_safe_lstat( const char *file_name, struct stat *info );
|
||||
DIR *pnp_boot_safe_opendir( const char *dirname );
|
||||
int pnp_boot_safe_closedir( DIR *dirp );
|
||||
struct dirent *pnp_boot_safe_readdir( DIR *dirp );
|
||||
#endif
|
||||
|
||||
// driver_loader.c
|
||||
|
||||
status_t pnp_load_driver( pnp_node_handle node, void *user_cookie,
|
||||
pnp_driver_info **interface, void **cookie );
|
||||
status_t pnp_unload_driver( pnp_node_handle node );
|
||||
void pnp_unblock_load( pnp_node_info *node );
|
||||
void pnp_load_driver_automatically( pnp_node_info *node, bool after_rescan );
|
||||
void pnp_unload_driver_automatically( pnp_node_info *node, bool before_rescan );
|
||||
|
||||
|
||||
// id_generator.c
|
||||
|
||||
int32 pnp_create_id( const char *name );
|
||||
status_t pnp_free_id( const char *name, uint32 id );
|
||||
|
||||
|
||||
// io_resources.c
|
||||
|
||||
status_t pnp_acquire_io_resources(
|
||||
io_resource *resources, io_resource_handle *handles );
|
||||
status_t pnp_release_io_resources( const io_resource_handle *handles );
|
||||
void pnp_assign_io_resources( pnp_node_info *node, const io_resource_handle *handles );
|
||||
void pnp_release_node_resources( pnp_node_info *node );
|
||||
|
||||
|
||||
// nodes.c
|
||||
|
||||
status_t pnp_alloc_node(
|
||||
const pnp_node_attr *attrs, const io_resource_handle *resources,
|
||||
pnp_node_info **new_node );
|
||||
void pnp_create_node_links( pnp_node_info *node, pnp_node_info *parent );
|
||||
void pnp_add_node_ref( pnp_node_info *node );
|
||||
void pnp_remove_node_ref( pnp_node_info *node );
|
||||
void pnp_remove_node_ref_nolock(pnp_node_info *node);
|
||||
pnp_node_handle pnp_find_device( pnp_node_handle parent, const pnp_node_attr *attrs );
|
||||
pnp_node_handle pnp_get_parent( pnp_node_handle node );
|
||||
|
||||
|
||||
// notifications.c
|
||||
|
||||
status_t pnp_notify_probe_by_module( pnp_node_info *node,
|
||||
const char *consumer_name );
|
||||
void pnp_notify_unregistration( pnp_node_info *notify_list );
|
||||
void pnp_start_hook_call( pnp_node_info *node );
|
||||
void pnp_start_hook_call_nolock( pnp_node_info *node );
|
||||
void pnp_finish_hook_call( pnp_node_info *node );
|
||||
void pnp_finish_hook_call_nolock( pnp_node_info *node );
|
||||
|
||||
|
||||
// patterns.c
|
||||
|
||||
status_t pnp_expand_pattern( pnp_node_info *node,
|
||||
const char *pattern, char *dest, char *buffer, size_t *term_array,
|
||||
int *num_parts );
|
||||
status_t pnp_expand_pattern_attr( pnp_node_info *node, const char *attr_name,
|
||||
char **expanded );
|
||||
|
||||
|
||||
// probe.c
|
||||
status_t pnp_notify_fixed_consumers( pnp_node_handle node );
|
||||
status_t pnp_notify_dynamic_consumers( pnp_node_info *node );
|
||||
|
||||
|
||||
// registration.c
|
||||
|
||||
status_t pnp_register_device(
|
||||
pnp_node_handle parent, const pnp_node_attr *attrs,
|
||||
const io_resource_handle *resources, pnp_node_handle *node );
|
||||
status_t pnp_unregister_device( pnp_node_handle node );
|
||||
void pnp_unregister_node_rec( pnp_node_info *node, pnp_node_info **dependency_list );
|
||||
void pnp_unref_unregistered_nodes( pnp_node_info *node_list );
|
||||
void pnp_defer_probing_of_children_nolock( pnp_node_info *node );
|
||||
void pnp_defer_probing_of_children( pnp_node_info *node );
|
||||
void pnp_probe_waiting_children_nolock( pnp_node_info *node );
|
||||
void pnp_probe_waiting_children( pnp_node_info *node );
|
||||
|
||||
|
||||
// scan.c
|
||||
|
||||
status_t pnp_rescan( pnp_node_handle node, uint depth );
|
||||
status_t pnp_rescan_int( pnp_node_info *node, uint depth,
|
||||
bool ignore_fixed_consumers );
|
||||
status_t pnp_initial_scan( pnp_node_info *node );
|
||||
|
86
src/kernel/core/device_manager/dl_list.h
Normal file
86
src/kernel/core/device_manager/dl_list.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
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
|
232
src/kernel/core/device_manager/id_generator.c
Normal file
232
src/kernel/core/device_manager/id_generator.c
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of the Device Manager
|
||||
|
||||
ID generator.
|
||||
|
||||
Generators are created on demand and deleted if all their
|
||||
IDs are freed. They have a ref_count which is increased
|
||||
whenever someone messes with the generator.
|
||||
|
||||
Whenever a generator is search for, generator_lock is hold.
|
||||
|
||||
To find out which ID are allocated, a bitmap is used that
|
||||
contain up to GENERATOR_MAX_ID bits. This is a hard limit,
|
||||
but it's simple to implement. If someone really needs lots of
|
||||
IDs, we can still rewrite the code. For simplicity, we use
|
||||
sGeneratorLock instead of a generator specific lock during
|
||||
allocation; changing it is straightforward as everything
|
||||
is prepared for that.
|
||||
*/
|
||||
|
||||
|
||||
#include "device_manager_private.h"
|
||||
#include <KernelExport.h>
|
||||
#include <util/list.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define TRACE_ID_GENERATOR
|
||||
#ifdef TRACE_ID_GENERATOR
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
struct list sGenerators;
|
||||
static benaphore sGeneratorLock;
|
||||
|
||||
|
||||
// create new generator
|
||||
// sGeneratorLock must be hold
|
||||
|
||||
static id_generator *
|
||||
create_generator(const char *name)
|
||||
{
|
||||
id_generator *generator;
|
||||
|
||||
TRACE(("create_generator(name: %s)\n", name));
|
||||
|
||||
generator = malloc(sizeof(*generator));
|
||||
if (generator == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(generator, 0, sizeof(*generator));
|
||||
|
||||
generator->name = strdup(name);
|
||||
if (generator->name == NULL) {
|
||||
free(generator);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
generator->ref_count = 1;
|
||||
|
||||
list_add_link_to_head(&sGenerators, generator);
|
||||
// ADD_DL_LIST_HEAD( generator, generator_list, );
|
||||
return generator;
|
||||
}
|
||||
|
||||
|
||||
// allocate id
|
||||
|
||||
static int32
|
||||
create_id_internal(id_generator *generator)
|
||||
{
|
||||
uint32 id;
|
||||
|
||||
TRACE(("create_id_internal(name: %s)\n", generator->name));
|
||||
|
||||
// see above: we use global instead of local lock
|
||||
benaphore_lock(&sGeneratorLock);
|
||||
|
||||
// simple bit search
|
||||
for (id = 0; id < GENERATOR_MAX_ID; ++id) {
|
||||
if ((generator->alloc_map[id / 8] & (1 << (id & 7))) == 0) {
|
||||
TRACE(("id: %lu\n", id));
|
||||
|
||||
generator->alloc_map[id / 8] |= 1 << (id & 7);
|
||||
++generator->num_ids;
|
||||
|
||||
benaphore_unlock(&sGeneratorLock);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
benaphore_unlock(&sGeneratorLock);
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// find generator by name
|
||||
// sGeneratorLock must be hold
|
||||
|
||||
static id_generator *
|
||||
find_generator(const char *name)
|
||||
{
|
||||
id_generator *generator = NULL;
|
||||
|
||||
TRACE(("find_generator(name: %s)\n", name));
|
||||
|
||||
while ((generator = list_get_next_item(&sGenerators, generator)) != NULL) {
|
||||
if (!strcmp(generator->name, name)) {
|
||||
// increase ref_count, so it won't go away
|
||||
++generator->ref_count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return generator;
|
||||
}
|
||||
|
||||
|
||||
// decrease ref_count, deleting generator if not used anymore
|
||||
|
||||
static void
|
||||
release_generator(id_generator *generator)
|
||||
{
|
||||
TRACE(("release_generator(name: %s)\n", generator->name));
|
||||
|
||||
benaphore_lock(&sGeneratorLock);
|
||||
|
||||
if (--generator->ref_count == 0) {
|
||||
// noone messes with generator
|
||||
if (generator->num_ids == 0) {
|
||||
TRACE(("Destroy %s\n", generator->name));
|
||||
// no IDs is allocated - destroy generator
|
||||
list_remove_link(generator);
|
||||
//REMOVE_DL_LIST( generator, generator_list, );
|
||||
free(generator->name);
|
||||
free(generator);
|
||||
}
|
||||
}
|
||||
|
||||
benaphore_unlock(&sGeneratorLock);
|
||||
}
|
||||
|
||||
|
||||
// public: create automatic ID
|
||||
|
||||
int32
|
||||
pnp_create_id(const char *name)
|
||||
{
|
||||
id_generator *generator;
|
||||
int32 id;
|
||||
|
||||
// find generator, create new if not there
|
||||
benaphore_lock(&sGeneratorLock);
|
||||
|
||||
generator = find_generator(name);
|
||||
if (generator == NULL)
|
||||
generator = create_generator(name);
|
||||
|
||||
benaphore_unlock(&sGeneratorLock);
|
||||
|
||||
if (generator == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
// get ID
|
||||
id = create_id_internal(generator);
|
||||
|
||||
release_generator(generator);
|
||||
|
||||
TRACE(("create_id: name: %s, id: %ld", name, id));
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
// public: free automatically generated ID
|
||||
|
||||
status_t
|
||||
pnp_free_id(const char *name, uint32 id)
|
||||
{
|
||||
id_generator *generator;
|
||||
|
||||
TRACE(("free_id(name: %s, id: %ld)\n", name, id));
|
||||
|
||||
// find generator
|
||||
benaphore_lock(&sGeneratorLock);
|
||||
|
||||
generator = find_generator(name);
|
||||
|
||||
benaphore_unlock(&sGeneratorLock);
|
||||
|
||||
if (generator == NULL) {
|
||||
dprintf("Generator %s doesn't exist\n", name);
|
||||
return B_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
// free ID
|
||||
|
||||
// make sure it's really allocated
|
||||
// (very important to keep <num_ids> in sync
|
||||
if ((generator->alloc_map[id / 8] & (1 << (id & 7))) == 0) {
|
||||
dprintf("id %ld of generator %s wasn't allocated", id, generator->name);
|
||||
|
||||
release_generator(generator);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
generator->alloc_map[id / 8] &= ~(1 << (id & 7));
|
||||
generator->num_ids--;
|
||||
|
||||
release_generator(generator);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
id_generator_init(void)
|
||||
{
|
||||
list_init(&sGenerators);
|
||||
return benaphore_init(&sGeneratorLock, "id generator");
|
||||
}
|
||||
|
568
src/kernel/core/device_manager/io_resources.c
Normal file
568
src/kernel/core/device_manager/io_resources.c
Normal file
@ -0,0 +1,568 @@
|
||||
/*
|
||||
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of PnP Manager
|
||||
|
||||
I/O resource manager.
|
||||
|
||||
I/O resources are allocated in two flavours:
|
||||
1. explicit allocation for hardware detection ("temporary allocation")
|
||||
2. implicit allocation for registered devices
|
||||
|
||||
If hardware detection fails, the resources are freed explicitely,
|
||||
if it succeeds, ownership is transfered to the device node.
|
||||
|
||||
Resources allocation collision is handled in the following way:
|
||||
- during ownership transfer to a device node, all other device nodes
|
||||
that collide in terms of resources are removed
|
||||
- if two temporary allocations collide, one must wait
|
||||
- if a temporary allocation collides with a device node whose driver is
|
||||
loaded, the temporary allocation fails (waiting for the driver may
|
||||
be hopeless as the driver may stay in memory forever)
|
||||
- if a temporary allocation collides with a device node whose driver is
|
||||
not loaded, loading of the driver is blocked
|
||||
|
||||
To avoid deadlocks between temporary allocations, they are done atomically:
|
||||
either the entire allocation succeeds or fails. If it fails because of a
|
||||
collision with another temporary allocation, a complete retry is done as
|
||||
soon as someone released some resource.
|
||||
*/
|
||||
|
||||
#include "device_manager_private.h"
|
||||
#include "dl_list.h"
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define TRACE_IO_RESOURCES
|
||||
#ifdef TRACE_IO_RESOURCES
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
// forward declarations to avoid inlining
|
||||
static void unblock_temporary_allocation(void);
|
||||
static void release_range(io_resource_info **list, io_resource_info *resource,
|
||||
io_resource_info *up_to);
|
||||
static void unregister_colliding_node_range(io_resource_info *list,
|
||||
io_resource_info *resource);
|
||||
|
||||
|
||||
// validate I/O resource data
|
||||
|
||||
static status_t
|
||||
validate_io_resource(io_resource *src)
|
||||
{
|
||||
TRACE(("validate_io_resource()\n"));
|
||||
|
||||
switch (src->type) {
|
||||
case IO_MEM:
|
||||
if (src->base + src->len < src->base)
|
||||
return B_BAD_VALUE;
|
||||
break;
|
||||
case IO_PORT:
|
||||
if ((uint16)src->base != src->base
|
||||
|| (uint16)src->len != src->len
|
||||
|| (uint16)(src->base + src->len) < src->base)
|
||||
return B_BAD_VALUE;
|
||||
break;
|
||||
case ISA_DMA_CHANNEL:
|
||||
if (src->base > 8 || src->len != 1)
|
||||
return B_BAD_VALUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
TRACE(("success!\n"));
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// allocate info structure for I/O resources
|
||||
|
||||
static status_t
|
||||
alloc_io_resource_info(io_resource *src, io_resource_info **dest_out)
|
||||
{
|
||||
io_resource_info *dest;
|
||||
|
||||
TRACE(("alloc_io_resource_info()\n"));
|
||||
|
||||
dest = calloc(1, sizeof(*dest));
|
||||
if (dest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
dest->owner = NULL;
|
||||
|
||||
dest->resource.type = src->type;
|
||||
dest->resource.base = src->base;
|
||||
dest->resource.len = src->len;
|
||||
|
||||
*dest_out = dest;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// acquire I/O range;
|
||||
// returns B_WOULD_BLOCK on collision with temporary allocation
|
||||
// node_lock must be hold
|
||||
|
||||
static status_t
|
||||
acquire_range(io_resource_info **list, io_resource_info *resource)
|
||||
{
|
||||
io_resource_info *cur;
|
||||
uint32 base = resource->resource.base;
|
||||
uint32 len = resource->resource.len;
|
||||
status_t res = B_OK;
|
||||
|
||||
TRACE(("acquire_range(base %lx, len %lx)\n", base, len));
|
||||
|
||||
for (cur = *list; cur != NULL; cur = cur->next) {
|
||||
// we need the "base + len - 1" trick to avoid wrap around at 4 GB
|
||||
if (cur->resource.base >= base
|
||||
&& cur->resource.base + len - 1 <= base + len - 1) {
|
||||
pnp_node_info *owner = cur->owner;
|
||||
|
||||
TRACE(("collision\n"));
|
||||
|
||||
if (owner == NULL) {
|
||||
// collision with temporary allocation - block ourself
|
||||
TRACE(("collision with temporary allocation - retry later\n"));
|
||||
|
||||
res = B_WOULD_BLOCK;
|
||||
break;
|
||||
} else {
|
||||
// collision with device
|
||||
if (owner->loading + owner->load_count > 0) {
|
||||
// driver is already loaded - give up
|
||||
// (driver may get loaded forever)
|
||||
TRACE(("collision with loaded driver - giving up\n"));
|
||||
|
||||
res = B_BUSY;
|
||||
break;
|
||||
} else {
|
||||
// driver is not loaded - block loading
|
||||
TRACE(("collision with unloaded driver - block driver %p\n", owner));
|
||||
|
||||
++owner->load_block_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cur != NULL) {
|
||||
// collided with someone
|
||||
TRACE(("Resource collision\n"));
|
||||
|
||||
release_range(list, resource, cur);
|
||||
return res;
|
||||
}
|
||||
|
||||
ADD_DL_LIST_HEAD(resource, *list, );
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// free I/O resource info
|
||||
|
||||
static void
|
||||
free_io_resource_info(io_resource_info *resource)
|
||||
{
|
||||
TRACE(("free_io_resource_info"));
|
||||
|
||||
free(resource);
|
||||
}
|
||||
|
||||
|
||||
// acquire I/O resource
|
||||
// a I/O resource info structure is allocated and added to
|
||||
// appropriate resource list (i.e. mem/port/channel) plus
|
||||
// all colliding resources (including ours) are marked blocked;
|
||||
// returns B_WOULD_BLOCK on collision with temporary allocation;
|
||||
// node_lock must be hold
|
||||
|
||||
static status_t
|
||||
acquire_io_resource(io_resource *src, io_resource_handle *dest_out)
|
||||
{
|
||||
io_resource_info *dest;
|
||||
status_t res;
|
||||
|
||||
TRACE(("acquire_io_resource(type=%ld, base=%lx, len=%lx)\n", src->type, src->base, src->len));
|
||||
|
||||
res = validate_io_resource(src);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
res = alloc_io_resource_info(src, &dest);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
switch (src->type) {
|
||||
case IO_MEM:
|
||||
res = acquire_range(&io_mem_list, dest);
|
||||
break;
|
||||
case IO_PORT:
|
||||
res = acquire_range(&io_port_list, dest);
|
||||
break;
|
||||
case ISA_DMA_CHANNEL:
|
||||
res = acquire_range(&isa_dma_list, dest);
|
||||
break;
|
||||
}
|
||||
|
||||
if (res != B_OK)
|
||||
// collided with someone
|
||||
goto err;
|
||||
|
||||
*dest_out = dest;
|
||||
|
||||
TRACE(("done\n"));
|
||||
return B_OK;
|
||||
|
||||
err:
|
||||
free_io_resource_info(dest);
|
||||
|
||||
TRACE(("failed: %s\n", strerror(res)));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// release I/O resource
|
||||
// node_lock must be hold
|
||||
|
||||
static void
|
||||
release_io_resource(io_resource_info *resource)
|
||||
{
|
||||
TRACE(("release_io_resource()\n"));
|
||||
|
||||
switch (resource->resource.type) {
|
||||
case IO_MEM:
|
||||
release_range(&io_mem_list, resource, NULL);
|
||||
break;
|
||||
case IO_PORT:
|
||||
release_range(&io_port_list, resource, NULL);
|
||||
break;
|
||||
case ISA_DMA_CHANNEL:
|
||||
release_range(&isa_dma_list, resource, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
free_io_resource_info(resource);
|
||||
}
|
||||
|
||||
|
||||
// release I/O range, which can be I/O memory, ports and ISA DMA channels
|
||||
// up_to - up to but not including colliding range to be released
|
||||
// (NULL for normal release, not-NULL if used during failed allocation)
|
||||
// blocked users get notified;
|
||||
// node_lock must be hold
|
||||
|
||||
static void
|
||||
release_range(io_resource_info **list, io_resource_info *resource, io_resource_info *up_to)
|
||||
{
|
||||
io_resource_info *cur;
|
||||
uint32 base = resource->resource.base;
|
||||
uint32 len = resource->resource.len;
|
||||
|
||||
TRACE(("release_range()\n"));
|
||||
|
||||
for (cur = *list; cur != NULL && cur != up_to; cur = cur->next) {
|
||||
if (cur == resource)
|
||||
// ignore ourselves
|
||||
continue;
|
||||
|
||||
// we need the "base + len - 1" trick to avoid wrap around at 4 GB
|
||||
if (cur->resource.base >= base && cur->resource.base + len - 1 <= base + len - 1) {
|
||||
pnp_node_info *owner = cur->owner;
|
||||
|
||||
TRACE(("unblock\n"));
|
||||
|
||||
if (owner != NULL) {
|
||||
// collision with driver - unblock it
|
||||
TRACE(("collision with driver - unblock driver %p\n", owner));
|
||||
|
||||
pnp_unblock_load(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (up_to == NULL) {
|
||||
// normal release - remove from list and
|
||||
// notify blocked temporary allocations
|
||||
REMOVE_DL_LIST(resource, *list, );
|
||||
|
||||
unblock_temporary_allocation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// wait until someone freed some resource or
|
||||
// allocated some resource permanently;
|
||||
// node_lock must be hold
|
||||
|
||||
static void
|
||||
wait_for_resources(void)
|
||||
{
|
||||
++pnp_resource_wait_count;
|
||||
|
||||
// we have to release while waiting
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
acquire_sem(pnp_resource_wait_sem);
|
||||
|
||||
TRACE(("retrying resource allocation"));
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
|
||||
// try to acquire list of resources.
|
||||
// returns B_WOULD_BLOCK on collision with temporary allocation
|
||||
// node_lock must be hold
|
||||
|
||||
static status_t
|
||||
try_acquire_io_resources(io_resource *resources, io_resource_handle *handles)
|
||||
{
|
||||
io_resource *resource;
|
||||
io_resource_handle *handle, *handle2;
|
||||
status_t res = B_OK;
|
||||
|
||||
for (resource = resources, handle = handles;
|
||||
resource->type != 0; ++resource, ++handle) {
|
||||
res = acquire_io_resource(resource, handle);
|
||||
if (res != B_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (res != B_OK)
|
||||
goto err;
|
||||
|
||||
*handle++ = NULL;
|
||||
|
||||
return B_OK;
|
||||
|
||||
err:
|
||||
// collision detected: free all previously acquired resources
|
||||
for (handle2 = handle; handle2 != handle; ++handle2)
|
||||
release_io_resource(*handle2);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// acquire I/O resources.
|
||||
// node_lock must be hold
|
||||
|
||||
static
|
||||
status_t acquire_io_resources(io_resource *resources, io_resource_handle *handles)
|
||||
{
|
||||
// try allocation until we either got all resources or
|
||||
// detected a collision with a loaded device node
|
||||
while (true) {
|
||||
status_t res;
|
||||
|
||||
// allocate all resources
|
||||
res = try_acquire_io_resources( resources, handles );
|
||||
if (res == B_OK)
|
||||
return B_OK;
|
||||
|
||||
if (res == B_WOULD_BLOCK) {
|
||||
// collision with temporary allocation:
|
||||
// wait until some resource is freed or blocked forever
|
||||
// by a loaded driver
|
||||
wait_for_resources();
|
||||
} else {
|
||||
// collided with loaded node - no point waiting for the node
|
||||
TRACE(("Giving up because collision with loaded device node\n"));
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// public: acquire I/O resources
|
||||
|
||||
status_t
|
||||
pnp_acquire_io_resources(io_resource *resources, io_resource_handle *handles)
|
||||
{
|
||||
status_t res;
|
||||
|
||||
TRACE(("pnp_acquire_io_resources()\n"));
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
res = acquire_io_resources(resources, handles);
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
TRACE(("done (%s)", strerror(res)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// public: release I/O resources
|
||||
|
||||
status_t
|
||||
pnp_release_io_resources(const io_resource_handle *handles)
|
||||
{
|
||||
io_resource_info *const *resource;
|
||||
|
||||
if (handles == NULL)
|
||||
return B_OK;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
for (resource = handles; *resource != NULL; ++resource)
|
||||
release_io_resource(*resource);
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// unregister devices that collide with one of our I/O resources
|
||||
|
||||
static void
|
||||
unregister_colliding_nodes(const io_resource_handle *resources)
|
||||
{
|
||||
const io_resource_handle *resource;
|
||||
|
||||
for (resource = resources; *resource != NULL; ++resource) {
|
||||
switch ((*resource)->resource.type) {
|
||||
case IO_MEM:
|
||||
unregister_colliding_node_range(io_mem_list, *resource);
|
||||
break;
|
||||
case IO_PORT:
|
||||
unregister_colliding_node_range(io_port_list, *resource);
|
||||
break;
|
||||
case ISA_DMA_CHANNEL:
|
||||
unregister_colliding_node_range(isa_dma_list, *resource);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// unregister device nodes that collide with I/O resource
|
||||
|
||||
static void
|
||||
unregister_colliding_node_range(io_resource_info *list, io_resource_info *resource)
|
||||
{
|
||||
io_resource_info *cur;
|
||||
uint32 base = resource->resource.base;
|
||||
uint32 len = resource->resource.len;
|
||||
|
||||
do {
|
||||
for (cur = list; cur != NULL; cur = cur->next) {
|
||||
// ignore ourself
|
||||
if (cur == resource)
|
||||
continue;
|
||||
|
||||
// we need the "base + len - 1" trick to avoid wrap around at 4 GB
|
||||
if (cur->resource.base >= base
|
||||
&& cur->resource.base + len - 1 <= base + len - 1) {
|
||||
pnp_node_handle owner = cur->owner;
|
||||
|
||||
if (owner == NULL)
|
||||
panic("Transfer of I/O resources from temporary to device node collided with other temporary allocation");
|
||||
|
||||
TRACE(("Deleting colliding node %p\n", owner));
|
||||
|
||||
// make sure node still exists after we'll have released lock
|
||||
++owner->ref_count;
|
||||
|
||||
// we have to release lock during unregistration
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
// unregister device node - we own its resources now
|
||||
// (its resources are freed by the next remove_node_ref call)
|
||||
pnp_unregister_device(owner);
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
pnp_remove_node_ref_nolock(owner);
|
||||
// restart loop as resource list may have changed meanwhile
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (cur != NULL);
|
||||
}
|
||||
|
||||
|
||||
// transfer temporary I/O resources to device node;
|
||||
// colliding devices are unregistered
|
||||
|
||||
void
|
||||
pnp_assign_io_resources(pnp_node_info *node, const io_resource_handle *resources)
|
||||
{
|
||||
io_resource_handle *resource;
|
||||
|
||||
if (resources == NULL)
|
||||
return;
|
||||
|
||||
unregister_colliding_nodes(resources);
|
||||
|
||||
memcpy(node->io_resources, resources,
|
||||
(node->num_io_resources + 1) * sizeof(io_resource_handle));
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
for (resource = node->io_resources; *resource != NULL; ++resource) {
|
||||
(*resource)->owner = node;
|
||||
}
|
||||
|
||||
// as the resources were only temporary previously, other temporary
|
||||
// allocations may be waiting for us; time to wake them, so they can
|
||||
// give up
|
||||
unblock_temporary_allocation();
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
|
||||
// unblock other temporary allocations so they can retry
|
||||
// node_lock must be hold
|
||||
|
||||
static void
|
||||
unblock_temporary_allocation(void)
|
||||
{
|
||||
if (pnp_resource_wait_count > 0) {
|
||||
TRACE(("unblock concurrent temporary allocation\n"));
|
||||
|
||||
release_sem_etc(pnp_resource_wait_sem, pnp_resource_wait_count, 0);
|
||||
pnp_resource_wait_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// release I/O resources of a device node and set list to NULL;
|
||||
// users previously blocked by our resource alloction are notified;
|
||||
// node_lock must be hold
|
||||
|
||||
void
|
||||
pnp_release_node_resources(pnp_node_info *node)
|
||||
{
|
||||
io_resource_handle *resource;
|
||||
|
||||
if (*node->io_resources != NULL)
|
||||
TRACE(("freeing I/O resources of node %p\n", node));
|
||||
|
||||
for (resource = node->io_resources; *resource != NULL; ++resource) {
|
||||
// set owner to NULL as we don't want to resource anymore
|
||||
(*resource)->owner = NULL;
|
||||
release_io_resource(*resource);
|
||||
}
|
||||
|
||||
// make sure we won't release resources twice
|
||||
*node->io_resources = NULL;
|
||||
}
|
||||
|
444
src/kernel/core/device_manager/nodes.c
Normal file
444
src/kernel/core/device_manager/nodes.c
Normal file
@ -0,0 +1,444 @@
|
||||
/*
|
||||
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of PnP Manager
|
||||
|
||||
PnP 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 <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
|
||||
|
||||
|
||||
// increase ref_count of node
|
||||
|
||||
void
|
||||
pnp_add_node_ref(pnp_node_info *node)
|
||||
{
|
||||
TRACE(("add_node_ref(%p)\n", node));
|
||||
|
||||
if (node == NULL)
|
||||
return;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
node->ref_count++;
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
|
||||
// free attributes of node
|
||||
// if an attribute is still in use, it's deletion is postponed
|
||||
|
||||
static void
|
||||
free_node_attrs(pnp_node_info *node)
|
||||
{
|
||||
pnp_node_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(pnp_node_info *node)
|
||||
{
|
||||
free(node->io_resources);
|
||||
node->io_resources = NULL;
|
||||
}
|
||||
|
||||
|
||||
// free node
|
||||
// (node lock must be hold)
|
||||
|
||||
static void
|
||||
free_node(pnp_node_info *node)
|
||||
{
|
||||
const char *generator_name, *driver_name, *type;
|
||||
uint32 auto_id;
|
||||
|
||||
if (pnp_get_attr_string_nolock(node, PNP_DRIVER_DRIVER, &driver_name, false) != B_OK)
|
||||
driver_name = "?";
|
||||
|
||||
if (pnp_get_attr_string_nolock(node, PNP_DRIVER_TYPE, &type, false) != B_OK)
|
||||
type = "?";
|
||||
|
||||
TRACE(("free_node(node: %p, driver: %s, type: %s)\n", node, driver_name, type));
|
||||
|
||||
// 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
|
||||
pnp_free_id(generator_name, auto_id);
|
||||
}
|
||||
|
||||
pnp_release_node_resources(node);
|
||||
|
||||
//list_remove_link(node);
|
||||
REMOVE_DL_LIST( node, node_list, );
|
||||
|
||||
delete_sem(node->hook_sem);
|
||||
delete_sem(node->load_block_sem);
|
||||
|
||||
free_node_attrs(node);
|
||||
free_node_resources(node);
|
||||
free(node);
|
||||
}
|
||||
|
||||
|
||||
// remove node reference and clean it up if necessary
|
||||
// (node_lock must be hold)
|
||||
|
||||
void
|
||||
pnp_remove_node_ref_nolock(pnp_node_info *node)
|
||||
{
|
||||
do {
|
||||
pnp_node_info *parent;
|
||||
|
||||
TRACE(("pnp_remove_node_ref_internal(ref_count of %p: %d)\n", node, node->ref_count - 1));
|
||||
|
||||
// unregistered devices loose their I/O resources as soon as they
|
||||
// are unloaded
|
||||
if (!node->registered && node->loading == 0 && node->load_count == 0)
|
||||
pnp_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);
|
||||
REMOVE_DL_LIST(node, parent->children, children_ );
|
||||
|
||||
free_node(node);
|
||||
|
||||
// unrolled recursive call: decrease ref_count of parent as well
|
||||
node = parent;
|
||||
} while (node != NULL);
|
||||
}
|
||||
|
||||
|
||||
// remove node reference and clean it up if necessary
|
||||
|
||||
void
|
||||
pnp_remove_node_ref(pnp_node_info *node)
|
||||
{
|
||||
TRACE(("pnp_remove_node_ref(%p)\n", node));
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
pnp_remove_node_ref_nolock(node);
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
|
||||
// copy node attributes into node's attribute list
|
||||
|
||||
static status_t
|
||||
copy_node_attrs(pnp_node_info *node, const pnp_node_attr *src)
|
||||
{
|
||||
status_t res;
|
||||
const pnp_node_attr *src_attr;
|
||||
pnp_node_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) {
|
||||
pnp_node_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(pnp_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;
|
||||
}
|
||||
|
||||
|
||||
// allocate device node info structure;
|
||||
// initially, ref_count is one to make sure node won't get destroyed by mistake
|
||||
|
||||
status_t
|
||||
pnp_alloc_node(const pnp_node_attr *attrs, const io_resource_handle *resources,
|
||||
pnp_node_info **new_node)
|
||||
{
|
||||
pnp_node_info *node;
|
||||
status_t res;
|
||||
|
||||
TRACE(("pnp_alloc_node()\n"));
|
||||
|
||||
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;
|
||||
|
||||
node->parent = NULL;
|
||||
node->rescan_depth = 0;
|
||||
node->verifying = false;
|
||||
node->redetected = false;
|
||||
node->init_finished = false;
|
||||
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->defer_probing = 0;
|
||||
node->unprobed_children = NULL;
|
||||
|
||||
// make public (well, almost: it's not officially registered yet)
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
ADD_DL_LIST_HEAD(node, node_list, );
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
*new_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;
|
||||
}
|
||||
|
||||
|
||||
// create links to parent node
|
||||
|
||||
void
|
||||
pnp_create_node_links(pnp_node_info *node, pnp_node_info *parent)
|
||||
{
|
||||
TRACE(("pnp_create_node_links(%p, parent=%p)\n", node, parent));
|
||||
|
||||
if (parent == NULL)
|
||||
return;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
TRACE(("Adding parent link"));
|
||||
|
||||
// parent must not be destroyed
|
||||
parent->ref_count++;
|
||||
node->parent = parent;
|
||||
|
||||
// tell parent about us, so we get unregistered automatically if parent
|
||||
// gets unregistered
|
||||
ADD_DL_LIST_HEAD(node, parent->children, children_ );
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
|
||||
// public: get parent of node
|
||||
|
||||
pnp_node_handle
|
||||
pnp_get_parent(pnp_node_handle node)
|
||||
{
|
||||
return node->parent;
|
||||
}
|
||||
|
||||
|
||||
// public: find node with some node attributes given
|
||||
|
||||
pnp_node_info *
|
||||
pnp_find_device(pnp_node_info *parent, const pnp_node_attr *attrs)
|
||||
{
|
||||
pnp_node_info *node, *found_node;
|
||||
|
||||
found_node = NULL;
|
||||
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
for (node = node_list; node; node = node->next) {
|
||||
const pnp_node_attr *attr;
|
||||
|
||||
// list contains removed devices too, so skip them
|
||||
if (!node->registered)
|
||||
continue;
|
||||
|
||||
if (parent != (pnp_node_info *)-1 && parent != node->parent)
|
||||
continue;
|
||||
|
||||
for (attr = attrs; attr && attr->name; ++attr) {
|
||||
bool equal = true;
|
||||
|
||||
switch (attr->type) {
|
||||
case B_UINT8_TYPE: {
|
||||
uint8 value;
|
||||
|
||||
equal = pnp_get_attr_uint8_nolock( node, attr->name, &value, false ) == B_OK
|
||||
&& value == attr->value.ui8;
|
||||
break;
|
||||
}
|
||||
case B_UINT16_TYPE: {
|
||||
uint16 value;
|
||||
|
||||
equal = pnp_get_attr_uint16_nolock( node, attr->name, &value, false ) == B_OK
|
||||
&& value == attr->value.ui16;
|
||||
break;
|
||||
}
|
||||
case B_UINT32_TYPE: {
|
||||
uint32 value;
|
||||
|
||||
equal = pnp_get_attr_uint32_nolock( node, attr->name, &value, false ) == B_OK
|
||||
&& value == attr->value.ui32;
|
||||
break;
|
||||
}
|
||||
case B_UINT64_TYPE: {
|
||||
uint64 value;
|
||||
|
||||
equal = pnp_get_attr_uint64_nolock( node, attr->name, &value, false ) == B_OK
|
||||
&& value == attr->value.ui64;
|
||||
break;
|
||||
}
|
||||
case B_STRING_TYPE: {
|
||||
const char *str;
|
||||
|
||||
equal = pnp_get_attr_string_nolock( node, attr->name, &str, false ) == B_OK
|
||||
&& strcmp( str, attr->value.string ) == 0;
|
||||
break;
|
||||
}
|
||||
case B_RAW_TYPE: {
|
||||
const void *data;
|
||||
size_t len;
|
||||
|
||||
equal = pnp_get_attr_raw_nolock( node, attr->name, &data, &len, false ) == B_OK
|
||||
&& len == attr->value.raw.len
|
||||
&& !memcmp(data, attr->value.raw.data, len);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!equal)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
nodes_init(void)
|
||||
{
|
||||
return benaphore_init(&gNodeLock, "device nodes");
|
||||
}
|
||||
|
492
src/kernel/core/device_manager/registration.c
Normal file
492
src/kernel/core/device_manager/registration.c
Normal file
@ -0,0 +1,492 @@
|
||||
/*
|
||||
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the NewOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of PnP Manager
|
||||
|
||||
PnP 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
|
||||
|
||||
|
||||
// decrease ref_count of unregistered nodes, specified by <node_list>
|
||||
|
||||
void
|
||||
pnp_unref_unregistered_nodes(pnp_node_info *node_list)
|
||||
{
|
||||
pnp_node_info *node, *next_node;
|
||||
|
||||
for (node = node_list; node; node = next_node) {
|
||||
next_node = node->notify_next;
|
||||
|
||||
pnp_remove_node_ref(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// mark node being registered, so it can be accessed via load_driver()
|
||||
|
||||
static status_t
|
||||
mark_node_registered(pnp_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;
|
||||
}
|
||||
|
||||
|
||||
// check whether device is redetected and remove
|
||||
// any device that is on same connection.
|
||||
// *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(pnp_node_info *node, pnp_node_info *parent, bool *redetected)
|
||||
{
|
||||
pnp_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
|
||||
// noone else (un)-registers a conflicting note during the tests
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
{
|
||||
char *connection, *device_identifier;
|
||||
|
||||
// get connection and devide id of us...
|
||||
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->children_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 node_lock 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;
|
||||
}
|
||||
|
||||
|
||||
// postpone searching for consumers if necessary
|
||||
// return: true, if postponed
|
||||
|
||||
static bool
|
||||
pnp_postpone_probing(pnp_node_info *node)
|
||||
{
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
// ask parent(!) if probing is to be postponed
|
||||
if (node->parent == NULL || !node->parent->defer_probing) {
|
||||
benaphore_unlock(&gNodeLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
// yes: this happens if the new node is a child of a bus node whose
|
||||
// rescan has not been finished
|
||||
TRACE(("postpone probing of node %p\n", node));
|
||||
|
||||
ADD_DL_LIST_HEAD(node, node->parent->unprobed_children, unprobed_ );
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// public: 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
|
||||
pnp_register_device(pnp_node_handle parent, const pnp_node_attr *attrs,
|
||||
const io_resource_handle *io_resources, pnp_node_handle *node)
|
||||
{
|
||||
pnp_node_info *node_inf;
|
||||
bool redetected;
|
||||
status_t res = B_OK;
|
||||
|
||||
res = pnp_alloc_node(attrs, io_resources, &node_inf);
|
||||
if (res != B_OK)
|
||||
goto err;
|
||||
|
||||
{
|
||||
char *driver_name, *type;
|
||||
|
||||
if (pnp_get_attr_string(node_inf, PNP_DRIVER_DRIVER, &driver_name, false) != B_OK) {
|
||||
dprintf("Missing driver filename in node\n");
|
||||
res = B_BAD_VALUE;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (pnp_get_attr_string(node_inf, PNP_DRIVER_TYPE, &type, false) != B_OK) {
|
||||
dprintf("Missing type in node registered by %s\n", driver_name);
|
||||
|
||||
free(driver_name);
|
||||
res = B_BAD_VALUE;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
TRACE(("driver: %s, type: %s\n", driver_name, type));
|
||||
|
||||
free(driver_name);
|
||||
free(type);
|
||||
}
|
||||
|
||||
// check whether this device already existed and thus is redetected
|
||||
res = is_device_redetected(node_inf, parent, &redetected);
|
||||
if (res != B_OK)
|
||||
goto err1;
|
||||
|
||||
if (redetected) {
|
||||
// upon redetect, resources are released instead of transferred and
|
||||
// no node is returned
|
||||
*node = NULL;
|
||||
res = B_OK;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
// transfer resources to device, unregistering all colliding devices;
|
||||
// this cannot fail - we've already allocated the resource handle array
|
||||
pnp_assign_io_resources(node_inf, io_resources);
|
||||
|
||||
pnp_create_node_links(node_inf, parent);
|
||||
|
||||
// make it public
|
||||
res = mark_node_registered(node_inf);
|
||||
if (res != B_OK)
|
||||
goto err2;
|
||||
|
||||
// from now on, node won't get freed as ref_count has been increased by registration
|
||||
|
||||
// check whether searching for consumers should be deferred
|
||||
if (pnp_postpone_probing(node_inf)) {
|
||||
// return without decrementing ref_count - else node may get
|
||||
// lost before deferred probe
|
||||
*node = node_inf;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
res = pnp_initial_scan(node_inf);
|
||||
if (res != B_OK)
|
||||
goto err2;
|
||||
|
||||
pnp_load_driver_automatically(node_inf, false);
|
||||
|
||||
*node = node_inf;
|
||||
|
||||
TRACE(("done: node=%p\n", *node));
|
||||
|
||||
// alloc_node has set ref_count to one for safety, correct this now
|
||||
pnp_remove_node_ref(node_inf);
|
||||
return res;
|
||||
|
||||
err2:
|
||||
// use this exit after i/o resources have been transferred to node
|
||||
pnp_remove_node_ref(node_inf);
|
||||
return res;
|
||||
|
||||
err1:
|
||||
// alloc_node has set ref_count to one for safety, correct this now
|
||||
pnp_remove_node_ref(node_inf);
|
||||
err:
|
||||
// always "consume" i/o resources
|
||||
pnp_release_io_resources(io_resources);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// public: unregister device node
|
||||
|
||||
status_t
|
||||
pnp_unregister_device(pnp_node_info *node)
|
||||
{
|
||||
pnp_node_info *dependency_list = NULL;
|
||||
|
||||
TRACE(("pnp_unregister_device(%p)\n", node));
|
||||
|
||||
if (node == NULL)
|
||||
return B_OK;
|
||||
|
||||
// unregistered node and all children
|
||||
benaphore_lock(&gNodeLock);
|
||||
pnp_unregister_node_rec(node, &dependency_list);
|
||||
benaphore_unlock(&gNodeLock);
|
||||
|
||||
pnp_unload_driver_automatically(node, false);
|
||||
|
||||
// tell drivers about their unregistration
|
||||
pnp_notify_unregistration(dependency_list);
|
||||
|
||||
// now, we can safely decrease ref_count of unregistered nodes
|
||||
pnp_unref_unregistered_nodes(dependency_list);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// remove <registered> flag of node and all children.
|
||||
// list of all unregistered nodes is appended to <dependency_list>;
|
||||
// (node_lock must be hold)
|
||||
|
||||
void
|
||||
pnp_unregister_node_rec(pnp_node_info *node, pnp_node_info **dependency_list)
|
||||
{
|
||||
pnp_node_info *child, *next_child;
|
||||
|
||||
TRACE(("pnp_unregister_node_rec(%p)\n", node));
|
||||
|
||||
{
|
||||
const char *driver_name, *type;
|
||||
|
||||
if (pnp_get_attr_string_nolock(node, PNP_DRIVER_DRIVER, &driver_name, false) != B_OK) {
|
||||
dprintf("unregister_node: Missing driver filename in node\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (pnp_get_attr_string_nolock( node, PNP_DRIVER_TYPE, &type, false) != B_OK) {
|
||||
dprintf("unregister_node: Missing type in node registered by %s\n", driver_name);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
TRACE(("driver: %s, type: %s\n", driver_name, type));
|
||||
err1:
|
||||
}
|
||||
|
||||
// especially when we go through children, it can happen that they
|
||||
// got unregistered already, so ignore them silently
|
||||
if (!node->registered)
|
||||
return;
|
||||
|
||||
TRACE(("Preparing unregistration\n"));
|
||||
|
||||
node->registered = false;
|
||||
ADD_DL_LIST_HEAD(node, *dependency_list, notify_);
|
||||
|
||||
// unregister children recursively
|
||||
for (child = node->children; child; child = next_child) {
|
||||
next_child = child->children_next;
|
||||
pnp_unregister_node_rec(child, dependency_list);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// defer probing of children.
|
||||
// node_lock must be hold
|
||||
|
||||
void
|
||||
pnp_defer_probing_of_children_nolock(pnp_node_info *node)
|
||||
{
|
||||
++node->defer_probing;
|
||||
}
|
||||
|
||||
|
||||
// defer probing of children
|
||||
|
||||
void
|
||||
pnp_defer_probing_of_children(pnp_node_info *node)
|
||||
{
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
pnp_defer_probing_of_children_nolock(node);
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
||||
|
||||
// execute deferred probing of children
|
||||
// (node_lock must be hold)
|
||||
|
||||
void
|
||||
pnp_probe_waiting_children_nolock(pnp_node_info *node)
|
||||
{
|
||||
if (--node->defer_probing > 0 && node->unprobed_children != 0)
|
||||
return;
|
||||
|
||||
TRACE(("execute deferred probing of parent %p", node));
|
||||
|
||||
while (node->unprobed_children) {
|
||||
pnp_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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// execute deferred probing of children
|
||||
|
||||
void
|
||||
pnp_probe_waiting_children(pnp_node_info *node)
|
||||
{
|
||||
benaphore_lock(&gNodeLock);
|
||||
|
||||
pnp_probe_waiting_children_nolock(node);
|
||||
|
||||
benaphore_unlock(&gNodeLock);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user