From fe01b8fa14a26213400177db8505169f06ec0223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Fri, 30 Apr 2004 16:42:58 +0000 Subject: [PATCH] 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 --- src/kernel/core/device_manager/Jamfile | 22 + src/kernel/core/device_manager/attributes.c | 489 +++++++++++++++ .../core/device_manager/device_manager.c | 167 +++++ .../device_manager/device_manager_private.h | 260 ++++++++ src/kernel/core/device_manager/dl_list.h | 86 +++ src/kernel/core/device_manager/id_generator.c | 232 +++++++ src/kernel/core/device_manager/io_resources.c | 568 ++++++++++++++++++ src/kernel/core/device_manager/nodes.c | 444 ++++++++++++++ src/kernel/core/device_manager/registration.c | 492 +++++++++++++++ 9 files changed, 2760 insertions(+) create mode 100644 src/kernel/core/device_manager/Jamfile create mode 100644 src/kernel/core/device_manager/attributes.c create mode 100644 src/kernel/core/device_manager/device_manager.c create mode 100644 src/kernel/core/device_manager/device_manager_private.h create mode 100644 src/kernel/core/device_manager/dl_list.h create mode 100644 src/kernel/core/device_manager/id_generator.c create mode 100644 src/kernel/core/device_manager/io_resources.c create mode 100644 src/kernel/core/device_manager/nodes.c create mode 100644 src/kernel/core/device_manager/registration.c diff --git a/src/kernel/core/device_manager/Jamfile b/src/kernel/core/device_manager/Jamfile new file mode 100644 index 0000000000..ec94356634 --- /dev/null +++ b/src/kernel/core/device_manager/Jamfile @@ -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 + ; diff --git a/src/kernel/core/device_manager/attributes.c b/src/kernel/core/device_manager/attributes.c new file mode 100644 index 0000000000..935941cc37 --- /dev/null +++ b/src/kernel/core/device_manager/attributes.c @@ -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 + +#include + +#include +#include + + +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; +} + diff --git a/src/kernel/core/device_manager/device_manager.c b/src/kernel/core/device_manager/device_manager.c new file mode 100644 index 0000000000..f4d4e5368f --- /dev/null +++ b/src/kernel/core/device_manager/device_manager.c @@ -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 +//#include + +//#include +//#include +//#include +//#include + +//#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 +}; + diff --git a/src/kernel/core/device_manager/device_manager_private.h b/src/kernel/core/device_manager/device_manager_private.h new file mode 100644 index 0000000000..d32d98ba18 --- /dev/null +++ b/src/kernel/core/device_manager/device_manager_private.h @@ -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 +#include + +#include + +#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 ); + diff --git a/src/kernel/core/device_manager/dl_list.h b/src/kernel/core/device_manager/dl_list.h new file mode 100644 index 0000000000..3df3c8bc93 --- /dev/null +++ b/src/kernel/core/device_manager/dl_list.h @@ -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 diff --git a/src/kernel/core/device_manager/id_generator.c b/src/kernel/core/device_manager/id_generator.c new file mode 100644 index 0000000000..a32b3ccbaa --- /dev/null +++ b/src/kernel/core/device_manager/id_generator.c @@ -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 +#include + +#include +#include + + +#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 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"); +} + diff --git a/src/kernel/core/device_manager/io_resources.c b/src/kernel/core/device_manager/io_resources.c new file mode 100644 index 0000000000..4b954ce118 --- /dev/null +++ b/src/kernel/core/device_manager/io_resources.c @@ -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 + +#include +#include + + +#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; +} + diff --git a/src/kernel/core/device_manager/nodes.c b/src/kernel/core/device_manager/nodes.c new file mode 100644 index 0000000000..a652a0ee41 --- /dev/null +++ b/src/kernel/core/device_manager/nodes.c @@ -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 +#include + +#include +#include + + +#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"); +} + diff --git a/src/kernel/core/device_manager/registration.c b/src/kernel/core/device_manager/registration.c new file mode 100644 index 0000000000..9d5a7ff10a --- /dev/null +++ b/src/kernel/core/device_manager/registration.c @@ -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 + +#include +#include + + +#define TRACE_REGISTRATION +#ifdef TRACE_REGISTRATION +# define TRACE(x) dprintf x +#else +# define TRACE(x) ; +#endif + + +// decrease ref_count of unregistered nodes, specified by + +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 flag of node and all children. +// list of all unregistered nodes is appended to ; +// (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); +} +