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:
Axel Dörfler 2004-04-30 16:42:58 +00:00
parent 8addb7c3d8
commit fe01b8fa14
9 changed files with 2760 additions and 0 deletions

View 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
;

View 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;
}

View 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
};

View 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 );

View 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

View 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");
}

View 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;
}

View 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");
}

View 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);
}