Managed to compile all other needed files from the main device manager.

Added all of them - module_loader.c and boot_hack.c won't be needed anymore.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@7351 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2004-05-02 17:04:25 +00:00
parent 4a3cf87482
commit b80d229ec2
7 changed files with 1937 additions and 5 deletions

View File

@ -7,16 +7,16 @@ KernelMergeObject kernel_device_manager.o :
attributes.c
# boot_hack.c
device_manager.c
# driver_loader.c
driver_loader.c
id_generator.c
io_resources.c
# module_loader.c
nodes.c
# notifications.c
# patterns.c
# probe.c
notifications.c
patterns.c
probe.c
registration.c
# scan.c
scan.c
:
-fno-pic -Wno-unused -D_KERNEL_MODE
;

View File

@ -18,6 +18,7 @@
#define GENERIC_SUBDIR "/generic"
// driver registration directories
#define PNP_DIR
#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/"

View File

@ -0,0 +1,450 @@
/*
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/*
Part of PnP Manager
PnP Driver loader.
*/
#include "device_manager_private.h"
#include <KernelExport.h>
#include <stdlib.h>
#include <string.h>
#define TRACE_DRIVER_LOADER
#ifdef TRACE_DRIVER_LOADER
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
/** unblock loading of driver
* node_lock must be hold
*/
void
pnp_unblock_load(pnp_node_info *node)
{
if (--node->load_block_count == 0 && node->num_blocked_loads > 0) {
TRACE(("Releasing blocked loads of %p\n", node));
release_sem_etc(node->load_block_sem,
node->num_blocked_loads, B_DO_NOT_RESCHEDULE);
node->num_blocked_loads = 0;
}
}
/** if loading of driver is blocked, wait until it is unblocked
* node_lock must be hold
*/
static void
pnp_wait_load_block(pnp_node_info *node)
{
if (node->load_block_count > 0) {
TRACE(("Loading of %p is blocked - wait until unblock\n", node));
// make sure we get informed about unblock
++node->num_blocked_loads;
benaphore_unlock(&gNodeLock);
acquire_sem(node->load_block_sem);
benaphore_lock(&gNodeLock);
TRACE(("Continuing loading of %p\n", node));
}
}
/** load driver automatically after registration/rescan of node. */
void
pnp_load_driver_automatically(pnp_node_info *node, bool after_rescan)
{
uint8 always_loaded;
status_t res;
pnp_driver_info *interface;
void *cookie;
benaphore_lock(&gNodeLock);
// ignore dead nodes
if (!node->registered)
goto err;
if (node->automatically_loaded)
goto err;
if (pnp_get_attr_uint8_nolock(node, PNP_DRIVER_ALWAYS_LOADED,
&always_loaded, false ) != B_OK)
goto err;
if (always_loaded < 1 || always_loaded > 2)
goto err;
// this test is probably useless as the driver would be kept loaded
// during rescan anyway
if (always_loaded == 2 && after_rescan)
goto err;
// alright - time to get the driver loaded
benaphore_unlock(&gNodeLock);
res = pnp_load_driver(node, NULL, &interface, &cookie);
benaphore_lock(&gNodeLock);
// if driver cannot be loaded, we don't really care
if (res == B_OK )
node->automatically_loaded = true;
else
dprintf("driver could not be automatically loaded\n");
err:
benaphore_unlock(&gNodeLock);
return;
}
/** unload driver that got loaded automatically. */
void
pnp_unload_driver_automatically(pnp_node_info *node, bool before_rescan)
{
uint8 always_loaded;
status_t res;
benaphore_lock(&gNodeLock);
if (!node->automatically_loaded)
goto err;
if (pnp_get_attr_uint8_nolock(node, PNP_DRIVER_ALWAYS_LOADED,
&always_loaded, false) != B_OK)
goto err;
// this test is probably useless as the driver wouldn't be
// loaded anyway
if (always_loaded < 1 || always_loaded > 2)
goto err;
if (always_loaded == 2 && before_rescan)
goto err;
// time to unload the driver
benaphore_unlock(&gNodeLock);
res = pnp_unload_driver(node);
benaphore_lock(&gNodeLock);
// don't reset flag if unloading failed;
// if this happens during unregistration, this won't help;
// but if this happens during rescan, the driver only stays
// loaded during rescan, which can be handled
if (res == B_OK)
node->automatically_loaded = false;
else
dprintf("driver could not be unloaded during scan\n");
err:
benaphore_unlock(&gNodeLock);
return;
}
/** load driver for real
* (loader_lock must be hold)
*/
static status_t
load_driver_int(pnp_node_info *node, void *user_cookie)
{
char *module_name;
pnp_driver_info *driver;
void *cookie;
status_t res;
TRACE(("load_driver_int()\n"));
res = pnp_get_attr_string(node, PNP_DRIVER_DRIVER, &module_name, false);
if (res != B_OK)
return res;
TRACE(("%s\n", module_name));
res = get_module(module_name, (module_info **)&driver);
if (res < B_OK) {
dprintf("Cannot load module %s\n", module_name);
goto err;
}
if (driver->init_device == NULL) {
dprintf("Driver %s has no init_device hook\n", module_name);
res = B_ERROR;
goto err2;
}
res = driver->init_device(node, user_cookie, &cookie);
if (res < B_OK)
goto err2;
node->driver = driver;
node->cookie = cookie;
free(module_name);
return B_OK;
err2:
put_module(module_name);
err:
free(module_name);
return res;
}
/** public: load driver */
status_t
pnp_load_driver(pnp_node_handle node, void *user_cookie,
pnp_driver_info **interface, void **cookie)
{
status_t res;
TRACE(("pnp_load_driver()\n"));
if (node == NULL)
return B_BAD_VALUE;
benaphore_lock(&gNodeLock);
if (!node->registered) {
benaphore_unlock(&gNodeLock);
TRACE(("Refused loading as driver got unregistered\n"));
return B_NAME_NOT_FOUND;
}
// increase ref_count whenever someone loads the driver
// to keep node information alive
++node->ref_count;
if (node->load_count == 0) {
// driver is not loaded
// tell others that someone will load the driver
// (after this, noone is allowed to block loading)
++node->loading;
// wait until loading is not blocked
pnp_wait_load_block(node);
// make sure noone else load/unloads/notifies the driver
pnp_start_hook_call_nolock(node);
// during load, driver may register children;
// probing them for consumers may be impossible as they
// may try to load driver -> deadlock
// so we block probing for consumers of children
pnp_defer_probing_of_children_nolock(node);
if (!node->registered) {
// device got lost meanwhile
TRACE(("Device got lost during wait\n"));
res = B_NAME_NOT_FOUND;
} else if (node->load_count == 0) {
// noone loaded the driver meanwhile, so
// we have to do that
benaphore_unlock(&gNodeLock);
TRACE(("Driver will be loaded\n"));
res = load_driver_int(node, user_cookie);
benaphore_lock(&gNodeLock);
} else {
// some other thread loaded the driver meanwhile
TRACE(("Driver got loaded during wait\n"));
res = B_OK;
}
// info: the next statements may have a strange order,
// but as we own node_lock, order is not important
// finish loading officially
// (don't release node_lock before load_count gets increased!)
--node->loading;
// unblock concurrent load/unload/notification
pnp_finish_hook_call_nolock(node);
if (res == B_OK) {
// everything went fine - increase load counter
++node->load_count;
// now we can safely probe for consumers of children as the
// driver is fully loaded
pnp_probe_waiting_children_nolock(node);
} else {
// loading failed or device got removed - restore reference count;
// but first (i.e. as long as ref_count is increased) get
// rid of waiting children
pnp_probe_waiting_children_nolock(node);
pnp_remove_node_ref_nolock(node);
}
} else {
// driver is already loaded, so increase load_count
++node->load_count;
res = B_OK;
}
benaphore_unlock(&gNodeLock);
if (res == B_OK) {
if (interface)
*interface = node->driver;
if (cookie)
*cookie = node->cookie;
TRACE(("load_driver: Success\n"));
} else {
TRACE(("load_driver: Failure (%s)\n", strerror(res)));
}
return res;
}
/** unload driver for real */
static status_t
unload_driver_int(pnp_node_info *node)
{
char *module_name;
status_t res;
pnp_driver_info *driver = node->driver;
res = pnp_get_attr_string(node, PNP_DRIVER_DRIVER, &module_name, false);
if (res != B_OK)
return res;
TRACE(("unload_driver_int: %s\n", module_name));
if (driver->uninit_device == NULL) {
// it has no uninit - we can't unload it, so it stays in memory forever
TRACE(("Driver %s has no uninit_device hook\n", module_name));
res = B_ERROR;
goto err;
}
res = driver->uninit_device(node->cookie);
if (res != B_OK) {
TRACE(("Failed to uninit driver %s (%s)\n", module_name, strerror(res)));
goto err;
}
// if it was unregistered a while ago but couldn't get cleaned up
// because it was in use, do the cleanup now
if (!node->registered) {
if (driver->device_cleanup != NULL)
driver->device_cleanup(node);
}
put_module(module_name);
free(module_name);
return B_OK;
err:
free(module_name);
return res;
}
/** public: unload driver */
status_t
pnp_unload_driver(pnp_node_handle node)
{
status_t res;
TRACE(("pnp_unload_driver: %p\n", node));
if (node == NULL)
return B_BAD_VALUE;
benaphore_lock(&gNodeLock);
if (node->loading > 0 || node->load_count == 1) {
// we may have to unload the driver because:
// - load_count will be zero after decrement, or
// - there is a concurrent load/unload call, so we
// don't know what the future load_count will be
// signal active unload;
// don't decrease load_count - the driver is still loaded!
++node->loading;
// wait for concurrent load/unload/notification
pnp_start_hook_call_nolock(node);
if (node->load_count == 1) {
// node is still to be unloaded
benaphore_unlock(&gNodeLock);
TRACE(("unloading %p\n", node));
res = unload_driver_int(node);
benaphore_lock(&gNodeLock);
} else {
// concurrent load/unload either unloaded the driver or
// increased load_count so unload is not necessary or forbidden
TRACE(("don't unload as current load_count is %d\n",
node->load_count));
res = B_OK;
}
// signal unload finished
--node->loading;
if (res == B_OK) {
// everything is fine, so decrease load_count ...
--node->load_count;
// ... and reference count
pnp_remove_node_ref_nolock(node);
} else {
// unloading failed: leave loaded in memory forever
// as load_count is not decreased, the driver will never
// be unloaded, and as reference count is not decreased,
// the node will exist forever
TRACE(("Unload failed - leaving driver of %p in memory\n", node));
}
// unblock others
pnp_finish_hook_call_nolock(node);
} else {
// no concurrent load/unload and load_count won't reach zero
--node->load_count;
// each load increased reference count by one - time to undo that
pnp_remove_node_ref_nolock(node);
res = B_OK;
}
benaphore_unlock(&gNodeLock);
return res;
}

View File

@ -0,0 +1,224 @@
/*
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/*
Part of PnP Manager
Execution and synchronization of driver hook calls.
*/
#include "device_manager_private.h"
#include <KernelExport.h>
#include <stdlib.h>
#include <string.h>
#define TRACE_NOTIFICATIONS
#ifdef TRACE_NOTIFICATIONS
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
/** notify a consumer that a device he might handle is added
* consumer_name - module name (!) of consumer
*/
status_t
pnp_notify_probe_by_module(pnp_node_info *node, const char *consumer_name)
{
pnp_driver_info *consumer;
status_t res;
TRACE(("pnp_notify_probe_by_module(node %p, consumer: %s)\n", node, consumer_name));
res = get_module(consumer_name, (module_info **)&consumer);
if (res < B_OK) {
dprintf("Cannot load driver module %s (%s)\n", consumer_name, strerror(res));
goto exit;
}
if (consumer->probe == NULL) {
dprintf("Driver %s has no probe hook\n", consumer_name);
res = B_ERROR;
} else
res = consumer->probe(node);
put_module(consumer_name);
exit:
#ifdef TRACE_NOTIFICATIONS
if (res != B_OK)
dprintf("%s\n", strerror(res));
#endif
return res;
}
/** notify driver that it's device got removed */
static status_t
notify_device_removed(pnp_node_info *node)
{
bool loaded;
char *module_name;
pnp_driver_info *driver;
void *cookie;
status_t res;
res = pnp_get_attr_string(node, PNP_DRIVER_DRIVER, &module_name, false);
if (res != B_OK)
return res;
TRACE(("notify_device_removed(node: %p, consumer: %s)\n", node, module_name));
// block concurrent load/unload calls
pnp_start_hook_call(node);
// don't notify driver if it doesn't know that the device was
// published
if (!node->init_finished)
goto skip;
// don't take node->loading into account - we want to know
// whether driver is loaded, not whether it is about to get loaded
loaded = node->load_count > 0;
if (loaded) {
TRACE(("Already loaded\n"));
driver = node->driver;
cookie = node->cookie;
} else {
TRACE(("Not loaded yet\n"));
// driver is not loaded - load it temporarily without
// calling init_device and pass <cookie>=NULL to tell
// the driver about
res = get_module(module_name, (module_info **)&driver);
if (res < B_OK)
goto err;
cookie = NULL;
}
// this hook is optional, so ignore it silently if NULL
if (driver->device_removed != NULL)
driver->device_removed(node, cookie);
else
dprintf("Driver %s has no device_removed hook\n", module_name);
// if driver wasn't loaded, call its cleanup method
if (!loaded) {
// again: this hook is optional
if (driver->device_cleanup != NULL)
driver->device_cleanup(node);
}
if (!loaded)
put_module(module_name);
skip:
pnp_finish_hook_call(node);
free(module_name);
return B_OK;
err:
free(module_name);
return res;
}
/** notify all drivers in <notify_list> that their node got removed */
void
pnp_notify_unregistration(pnp_node_info *notify_list)
{
pnp_node_info *node;
TRACE(("pnp_notify_unregistration()\n"));
for (node = notify_list; node; node = node->notify_next)
notify_device_removed(node);
}
/** start driver hook call; must be called before a
* load/unload/notification hook is called
*/
void
pnp_start_hook_call(pnp_node_info *node)
{
bool blocked;
benaphore_lock(&gNodeLock);
blocked = ++node->num_waiting_hooks > 1;
benaphore_unlock(&gNodeLock);
if (blocked) {
TRACE(("Hook call of %p is blocked\n", node));
acquire_sem(node->hook_sem);
}
}
/** start driver hook call; same pnp_start_hook_call(), but
* node_lock must be hold
*/
void
pnp_start_hook_call_nolock(pnp_node_info *node)
{
if (++node->num_waiting_hooks > 1) {
benaphore_unlock(&gNodeLock);
TRACE(("Hook call of %p is blocked\n", node));
acquire_sem(node->hook_sem);
benaphore_lock(&gNodeLock);
}
}
/** finish driver hook call; must be called after a load/unload/notification
* hook is called
*/
void
pnp_finish_hook_call(pnp_node_info *node)
{
benaphore_lock(&gNodeLock);
if (--node->num_waiting_hooks > 1) {
TRACE(("Releasing other hook calls of %p\n", node));
release_sem(node->hook_sem);
}
benaphore_unlock(&gNodeLock);
}
/** finish driver hook call; same pnp_finish_hook_call(), but
* node_lock must be hold
*/
void
pnp_finish_hook_call_nolock(pnp_node_info *node)
{
if (--node->num_waiting_hooks > 1) {
TRACE(("Releasing other hook calls of %p\n", node));
release_sem(node->hook_sem);
}
}

View File

@ -0,0 +1,241 @@
/*
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/*
Part of PnP Manager
Expansion of patterns.
Used to expand consumer/connector/device names etc.
*/
#include "device_manager_private.h"
#include <KernelExport.h>
#include <TypeConstants.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define TRACE_PATTERNS
#ifdef TRACE_PATTERNS
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
/** expand node attribute
* node - node to get attributes from
* *pattern - name of attribute, ending with "%" (takes care of "^" sequences)
* on return, *patterns points to trailing "%"
* buffer - buffer for name
* dst - string to write content to
* node_lock must be hold
*/
static status_t
expand_attr(pnp_node_info *node, const char **pattern, char *buffer, char *dst)
{
pnp_node_attr_info *attr;
const char *str;
int buffer_len;
// extract attribute name
buffer_len = 0;
for (str = *pattern; *str; ++str) {
if (*str == '^' && *(str + 1) != 0)
// copy escaped character directly
++str;
else if (*str == '%')
break;
buffer[buffer_len++] = *str;
}
*pattern = str;
buffer[buffer_len] = '\0';
// get attribute content and add it to path
attr = pnp_find_attr_nolock(node, buffer, true, B_ANY_TYPE);
if (attr == NULL) {
dprintf("Reference to unknown attribute %s\n", buffer);
return B_NAME_NOT_FOUND;
}
switch (attr->attr.type) {
case B_UINT8_TYPE:
sprintf(buffer, "%02x", attr->attr.value.ui8);
strlcat(dst, buffer, PATH_MAX);
break;
case B_UINT16_TYPE:
sprintf(buffer, "%04x", attr->attr.value.ui16);
strlcat(dst, buffer, PATH_MAX);
break;
case B_UINT32_TYPE:
sprintf(buffer, "%08lx", attr->attr.value.ui32);
strlcat(dst, buffer, PATH_MAX);
break;
case B_UINT64_TYPE:
sprintf(buffer, "%16Lx", attr->attr.value.ui64);
strlcat(dst, buffer, PATH_MAX);
break;
case B_STRING_TYPE: {
const char *str;
strlcat(dst, "\"", PATH_MAX);
for (str = attr->attr.value.string; *str; ++str) {
char ch;
ch = *str;
if (ch >= 32 && ch <= 126 && ch != '/' && ch != '%' && ch != '"') {
buffer[0] = ch;
buffer[1] = 0;
} else {
// convert unprintable/forbidden characters to numbers
sprintf(buffer, "%%%u%%", (unsigned char)ch);
}
strlcat(dst, buffer, PATH_MAX);
}
strlcat(dst, "\"", PATH_MAX);
break;
}
case B_RAW_TYPE:
default:
benaphore_unlock(&gNodeLock);
return B_BAD_VALUE;
}
benaphore_unlock(&gNodeLock);
return B_OK;
}
/** expand pattern.
* the pattern is both expanded and the position of inidiual parts returned
* pattern - pattern to expand
* dest - expanded name is appended to this string
* buffer - buffer of MAX_PATH+1 size supplied by caller
* term_array - list of lengths of individual sub-names
* with index 0 containing the length of the shortest and
* num_parts-1 - length of the longest sub-name
* <term_array> and <num_parts> can be NULL
* node_lock must be hold
*/
status_t
pnp_expand_pattern(pnp_node_info *node, const char *pattern, char *dest,
char *buffer, size_t *term_array, int *num_parts)
{
const char *str;
int id;
status_t res;
int buffer_len = 0;
TRACE(("expand_pattern(pattern: %s)\n", pattern));
for (str = pattern, id = 0; *str; ++str) {
switch (*str) {
case '^':
// character following "^" is copied directly
// exception: if there is a trailing "^" at end of string,
// we copy it directly
if (*(str + 1) != 0)
++str;
buffer[buffer_len++] = *str;
break;
case '|':
// end of one name part
buffer[buffer_len] = '\0';
buffer_len = 0;
strlcat(dest, buffer, PATH_MAX);
TRACE(("%d: %s\n", id, dest));
if (term_array != NULL)
term_array[id++] = strlen(dest);
break;
case '%':
// begin of place-holder
buffer[buffer_len] = '\0';
buffer_len = 0;
strlcat(dest, buffer, PATH_MAX);
// skip "%"
++str;
res = expand_attr(node, &str, buffer, dest);
if (res != B_OK)
return res;
break;
default:
// normal character
buffer[buffer_len++] = *str;
}
// avoid overflow of buffer
if (buffer_len > PATH_MAX)
buffer_len = PATH_MAX;
}
// append trailing characters
buffer[buffer_len] = 0;
buffer_len = 0;
strlcat(dest, buffer, PATH_MAX);
if (term_array != NULL)
term_array[id++] = strlen(dest);
if (num_parts != NULL)
*num_parts = id;
TRACE(("result: %s\n", dest));
return B_OK;
}
/** expand pattern as specified by attribute <attr_name>
* node_lock must be hold
*/
status_t
pnp_expand_pattern_attr(pnp_node_info *node, const char *attr_name,
char **expanded)
{
const char *pattern;
status_t res;
TRACE(("pnp_expand_pattern_attr()\n"));
if (pnp_get_attr_string_nolock(node, attr_name, &pattern, false) != B_OK) {
*expanded = strdup("");
return B_OK;
}
*expanded = malloc((PATH_MAX + 1) * 2);
if (*expanded == NULL)
return B_NO_MEMORY;
**expanded = 0;
res = pnp_expand_pattern(node, pattern,
*expanded, *expanded + PATH_MAX + 1, NULL, NULL);
if (res != B_OK)
free(*expanded);
return res;
}

View File

@ -0,0 +1,571 @@
/*
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/*
Part of PnP Manager
Probing for consumers.
Here is all the core logic how consumers are found for one node.
*/
#include "device_manager_private.h"
#include <KernelExport.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define TRACE_PROBE
#ifdef TRACE_PROBE
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
// ToDo: for now - it won't be necessary to do this later anymore
#define pnp_boot_safe_realpath realpath
#define pnp_boot_safe_lstat lstat
#define pnp_boot_safe_opendir opendir
#define pnp_boot_safe_readdir readdir
#define pnp_boot_safe_closedir closedir
// list of driver registration directories
const char *pnp_registration_dirs[2] = {
COMMON_DRIVER_REGISTRATION,
SYSTEM_DRIVER_REGISTRATION
};
// list of module directories
static const char *modules_dirs[2] = {
COMMON_MODULES_DIR,
SYSTEM_MODULES_DIR
};
/** notify a consumer that a device he might handle is added
* consumer_name - file name (!) of consumer
* (moved to end of file to avoid inlining)
*/
static
status_t notify_probe_by_file(pnp_node_info *node, const char *consumer_name)
{
char *type;
char *resolved_path;
char *module_name;
int i;
bool valid_module_name;
status_t res;
TRACE(("notify_probe_by_file(%s)\n", consumer_name));
res = pnp_get_attr_string(node, PNP_DRIVER_TYPE, &type, false);
if (res != B_OK)
return res;
// resolve link to actual driver file
resolved_path = malloc(PATH_MAX + 1);
if (resolved_path == NULL) {
res = B_NO_MEMORY;
goto err;
}
module_name = pnp_boot_safe_realpath(consumer_name, resolved_path);
if (module_name == NULL) {
// broken link or something
dprintf("Cannot resolve driver file name: %s\n", consumer_name);
res = errno;
goto err2;
}
// make sure both consumer and module file are either in
// system or user modules directory
valid_module_name = false;
for (i = 0; i < (disable_useraddons ? 1 : 2); ++i) {
int len = strlen(modules_dirs[i]);
if (!strncmp(consumer_name, modules_dirs[i], len)
&& !strncmp(module_name, modules_dirs[i], len)) {
valid_module_name = true;
module_name = module_name + len;
break;
}
}
if (!valid_module_name) {
TRACE(("Module file %s of consumer %s is in wrong path\n",
consumer_name, module_name));
res = B_NAME_NOT_FOUND;
goto err2;
}
// append driver type to get specific module name
strlcat(module_name, "/", PATH_MAX);
strlcat(module_name, type, PATH_MAX);
res = pnp_notify_probe_by_module(node, module_name);
err2:
free(resolved_path);
err:
free(type);
return res;
}
/** compose all possible names of Specific drivers; as there are
* multiple names which only differ in length, the most specific
* driver name gets stored in <path>, whereas <term_array> is a
* list of lengths of individual driver names with index 0
* containing the length of the shortest and num_parts-1 the
* length of the longest name; <buffer> is a supplied buffer of
* MAX_PATH+1 size
*/
static status_t
compose_driver_names(pnp_node_info *node, const char *dir,
const char *filename_pattern, int num_parts,
char *path, char *buffer, size_t **res_term_array)
{
size_t *term_array;
int id;
status_t res;
term_array = malloc(num_parts * sizeof(size_t));
if (term_array == NULL)
return B_NO_MEMORY;
strlcpy(path, dir, PATH_MAX);
strlcat(path, "/", PATH_MAX);
TRACE(("compose_drive_names(%s)\n", path));
benaphore_lock(&gNodeLock);
res = pnp_expand_pattern(node, filename_pattern, path, buffer,
term_array, &id);
benaphore_unlock(&gNodeLock);
if (res != B_OK)
goto err;
if (id != num_parts) {
panic("compose_driver_names: number of pattern parts in %s is inconsistent (%d!=%d)",
filename_pattern, num_parts, id);
}
TRACE(("driver=%s, parts=%d\n", buffer, id));
*res_term_array = term_array;
return B_OK;
err:
free(term_array);
return res;
}
/** notify all drivers under <directory>. If <tell_all> is true, notify all,
* if false, stop once a drivers notification function returned B_OK
* buffer - scratch buffer of size PATH_MAX + 1 ; destroyed on exit
* return: B_NAME_NOT_FOUND, if tell_all is false and no driver returned B_OK
*/
static status_t
try_drivers(pnp_node_info *node, char *directory,
bool tell_all, char *buffer)
{
DIR *dir;
size_t dir_len;
struct dirent *entry;
int i;
TRACE(("try_drivers(dir: %s)\n", directory));
// first try user drivers, then system drivers
for (i = 0; i < (disable_useraddons ? 1 : 2); ++i) {
strcpy(buffer, pnp_registration_dirs[i]);
strlcat(buffer, directory, PATH_MAX);
TRACE(("cur_dir: %s\n", buffer));
dir_len = strlen(buffer);
dir = pnp_boot_safe_opendir(buffer);
if (dir == NULL) {
//SHOW_ERROR(3, "Directory %s doesn't exists", buffer);
// directory doesn't exist
return tell_all ? B_OK : B_NAME_NOT_FOUND;
}
while ((entry = pnp_boot_safe_readdir(dir)) != NULL) {
buffer[dir_len] = 0;
strlcat(buffer, "/", PATH_MAX);
strlcat(buffer, entry->d_name, PATH_MAX);
// skip default directory entries
if (!strcmp( entry->d_name, ".")
|| !strcmp( entry->d_name, ".."))
continue;
if (notify_probe_by_file( node, buffer) == B_OK && !tell_all) {
// tell_all is false, return on first hit
pnp_boot_safe_closedir(dir);
return B_OK;
}
}
pnp_boot_safe_closedir(dir);
}
return tell_all ? B_OK : B_NAME_NOT_FOUND;
}
/** find normal consumer of node that are stored under <dir>; first, we
* look for a specific driver; if none could be found, find a generic
* one path, buffer - used as scratch buffer (all of size PATH_MAX + 1)
* return: B_NAME_NOT_FOUND if no consumer could be found
*/
static status_t
find_normal_consumer(pnp_node_info *node, const char *dir,
const char *filename_pattern, int num_parts,
char *path, char *buffer, bool *found_normal_driver)
{
status_t res;
size_t *term_array;
int i;
// don't search for specific consumers if there is only a directory given
if (*filename_pattern) {
res = compose_driver_names(node, dir, filename_pattern, num_parts,
path, buffer, &term_array);
if (res != B_OK)
return res;
// try to find specific driver, starting with most specific, i.e.
// the one with the longest name
for (i = num_parts - 1; i >= 0; --i) {
int j;
TRACE(("%d: %lu\n", i, term_array[i]));
path[term_array[i]] = 0;
// first, check for user driver, then system driver
for (j = 0; j < (disable_useraddons ? 1 : 2); ++j) {
struct stat dummy;
strcpy(buffer, pnp_registration_dirs[j]);
strlcat(buffer, path, PATH_MAX);
// do a stat to avoid error message if Specific Driver
// isn't provided
if (pnp_boot_safe_lstat(buffer, &dummy) == 0) {
res = notify_probe_by_file(node, buffer);
if (res == B_OK)
// got him!
break;
} else {
/*SHOW_ERROR( 4, "Specific driver %s doesn't exists",
buffer );*/
}
}
}
free(term_array);
if (i >= 0) {
// found specific consumer
*found_normal_driver = true;
return B_OK;
}
}
// no specific consumer - go through generic driver
strlcpy(path, dir, PATH_MAX);
strlcat(path, GENERIC_SUBDIR, PATH_MAX);
if (try_drivers(node, path, false, buffer) != B_OK) {
*found_normal_driver = false;
return B_NAME_NOT_FOUND;
}
*found_normal_driver = true;
return B_OK;
}
/** Notify fixed consumers that device was added; in contrast to dynamic
* consumers, errors reported by fixed consumers are not ignored but
* returned.
*/
status_t
pnp_notify_fixed_consumers(pnp_node_info *node)
{
int i;
char *buffer;
TRACE(("pnp_notify_fixed_consumers()\n"));
buffer = malloc(PATH_MAX + 1);
if (buffer == NULL)
return B_NO_MEMORY;
// first, append nothing, then "/0", "/1" etc.
for (i = -1; ; ++i) {
char *consumer;
strcpy(buffer, PNP_DRIVER_FIXED_CONSUMER);
if (i >= 0)
sprintf(buffer + strlen(buffer), "/%d", i);
// if no more fixed consumers, cancel loop silently
if (pnp_get_attr_string( node, buffer, &consumer, false) != B_OK)
break;
TRACE(("Consumer %d: %s\n", i, consumer));
if (pnp_notify_probe_by_module(node, consumer) != B_OK) {
dprintf("Cannot notify fixed consumer %s\n", consumer);
// report error if fixed consumers couldn't be loaded
// as they are obviously crucial (else they wouldn't be fixed)
free(consumer);
free(buffer);
return B_NAME_NOT_FOUND;
}
free(consumer);
}
free(buffer);
return B_OK;
}
/** pre-process dynamic consumer name pattern.
* split into directory and pattern and count split positions;
* further, remove quotes from directory
* pattern - pattern of consumer name
* buffer - buffer to store results in
* (returned strings all point to <buffer>!)
* dir - directory
* filename_pattern - pattern of file name
* *num_parts - number of split positions
*/
static status_t
preprocess_consumer_names(const char *pattern, char *buffer,
char **dir, char **filename_pattern, int *const num_parts)
{
char *last_slash, *str, *dest;
bool parts_began;
// make a copy of pattern as we will strip escapes from directory part
strlcpy(buffer, pattern, PATH_MAX);
// find directory part and count splitpoints
parts_began = false;
last_slash = NULL;
*num_parts = 1;
for (str = buffer; *str; ++str) {
switch (*str) {
case '^':
// honour escaped characters, taking care of trailing escape
if (*(str + 1))
++str;
break;
case '|':
++*num_parts;
parts_began = true;
break;
case '%':
++str;
// find end of attribute name, taking care of escape sequences
for (; *str != 0; ++str) {
if (*str == '^') {
if (*(str + 1) != 0)
++str;
} else if (*str == '%')
break;
}
if (*str == 0) {
dprintf("missing matching '%%' in consumer pattern %s\n", pattern);
return B_BAD_VALUE;
}
break;
case '/':
// directory finishes at last "/" before first "|"
if (!parts_began)
last_slash = str;
break;
}
}
if (last_slash == 0) {
dprintf("missing directory in consumer pattern %s\n", pattern);
return B_BAD_VALUE;
}
// split into directory and filename pattern in place
*dir = buffer;
*last_slash = 0;
*filename_pattern = last_slash + 1;
// remove escape sequences from directory
for (str = buffer, dest = buffer; *str; ++str) {
if (*str == '^' && (str + 1) != 0)
++str;
*dest++ = *str;
}
*dest = 0;
TRACE(("dir=%s, filename_pattern=%s, num_parts=%d\n",
*dir, *filename_pattern, *num_parts));
return B_OK;
}
/** find consumers for one given pattern
* has_normal_drivers - in: true - don't search for specific driver
* out: true - specific driver was found
*/
static status_t
notify_dynamic_consumer(pnp_node_info *node, const char *pattern,
bool *has_normal_driver)
{
status_t res;
char *buffers;
char *dir, *filename_pattern;
int num_parts;
TRACE(("notify_dynamic_consumer(pattern=%s, has_normal_driver=%d)\n",
pattern, *has_normal_driver));
// we need three buffers - allocate them at once for simpliness
buffers = malloc(3 * (PATH_MAX + 1));
if (buffers == NULL)
return B_NO_MEMORY;
res = preprocess_consumer_names(pattern, buffers + 2 * (PATH_MAX + 1),
&dir, &filename_pattern, &num_parts);
if (res < B_OK)
goto err;
if (!*has_normal_driver) {
// find specific/generic consumer
res = find_normal_consumer(node, dir, filename_pattern, num_parts,
buffers, buffers + PATH_MAX + 1, has_normal_driver);
if (res != B_OK && res != B_NAME_NOT_FOUND)
// only abort if there was a "real" problem;
// if there is no specific/generic consumer, we don't bother
// (having no driver is not funny but happens)
goto err;
}
// tell universal drivers
strlcpy(buffers, dir, PATH_MAX);
strlcat(buffers, UNIVERSAL_SUBDIR, PATH_MAX);
res = try_drivers(node, buffers, true, buffers + PATH_MAX + 1);
if (res != B_OK && res != B_NAME_NOT_FOUND)
// again, only abort on real problems
goto err;
free(buffers);
TRACE(("done\n"));
return B_OK;
err:
free(buffers);
return res;
}
/** find and notify dynamic consumers that device was added
* errors returned by consumers aren't reported, only problems
* like malformed consumer patterns
*/
status_t
pnp_notify_dynamic_consumers(pnp_node_info *node)
{
int i;
char *buffer;
status_t res;
bool has_normal_driver = false;
TRACE(("pnp_notify_dynamic_consumers()\n"));
buffer = malloc(PATH_MAX + 1);
if (buffer == NULL)
return B_NO_MEMORY;
//pnp_load_boot_links();
// first, append nothing, then "/0", "/1" etc.
for (i = -1; ; ++i) {
char *consumer;
strcpy(buffer, PNP_DRIVER_DYNAMIC_CONSUMER);
if (i >= 0)
sprintf(buffer + strlen( buffer ), "/%d", i);
// if no more dynamic consumers, cancel loop silently
if (pnp_get_attr_string(node, buffer, &consumer, false) != B_OK) {
// starting with .../0 is OK, so ignore error if i=-1
if (i == -1)
continue;
else
break;
}
TRACE(("Consumer pattern %d: %s\n", i, consumer));
res = notify_dynamic_consumer(node, consumer, &has_normal_driver);
free(consumer);
if (res != B_OK) {
// this is only reached if a serious error occured,
// see notify_dynamic_consumer()
goto err;
}
}
free(buffer);
//pnp_unload_boot_links();
return B_OK;
err:
free(buffer);
//pnp_unload_boot_links();
return res;
}

View File

@ -0,0 +1,445 @@
/*
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/*
Part of PnP Manager
Scanning for consumers.
Logic to execute (re)scanning of nodes.
During initial registration, problems with fixed consumers are
fatal. During rescan, this is ignored
(TODO: should this be done more consistently?)
*/
#include "device_manager_private.h"
#include "pnp_bus.h"
#include <KernelExport.h>
#include <string.h>
#define TRACE_SCAN
#ifdef TRACE_SCAN
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
/** public function: rescan PnP node */
status_t
pnp_rescan(pnp_node_info *node, uint depth)
{
return pnp_rescan_int(node, depth, false);
}
/** execute registration of fully constructed node */
status_t
pnp_initial_scan(pnp_node_info *node)
{
pnp_start_hook_call(node);
node->init_finished = true;
pnp_finish_hook_call(node);
return pnp_rescan_int(node, 1, false);
}
/** check whether rescanning a node makes sense in terms of
* double scans.
* node_lock must be hold
*/
static bool
is_rescan_sensible(pnp_node_info *node)
{
uint depth;
pnp_node_info *child;
TRACE(("is_rescan_sensible(node: %p)\n", node));
// if a child is being scanned, abort our rescan to avoid
// concurrent scans by the same driver on the same device
for (child = node->children; child != NULL; child = child->children_next) {
if (child->rescan_depth > 0) {
TRACE(("child %p is being scanned\n", child));
return false;
}
}
TRACE(("check parents\n"));
// make sure neither this nor parent node is scanned recursively
// up to this node as this would lead to unnecessary double-scans
// (don't need to check whether parent is unregistered as
// all children would have been unregistered immediately)
for (depth = 1; node != NULL; node = node->parent, ++depth) {
if (node->rescan_depth >= depth) {
TRACE(("parent %p is being scanned", node));
return false;
}
}
return true;
}
/** mark children of node as being scanned.
* children that have their drivers loaded are not
* scanned unless they allow that explicitely.
* node_lock must be hold
*/
static void
mark_children_scanned(pnp_node_info *node)
{
pnp_node_info *child, *next_child;
TRACE(("mark_children_scanned()\n"));
for (child = node->children; child != NULL; child = next_child) {
uint8 never_rescan, no_live_rescan;
next_child = child->children_next;
// ignore dead children
if (!child->registered)
continue;
// check whether device can be rescanned at all
if (pnp_get_attr_uint8_nolock(child, PNP_DRIVER_NEVER_RESCAN,
&never_rescan, false) == B_OK && never_rescan) {
TRACE(("no rescan allowed on device %p\n", child));
continue;
}
if (pnp_get_attr_uint8_nolock(child, PNP_DRIVER_NO_LIVE_RESCAN,
&no_live_rescan, false) != B_OK)
no_live_rescan = false;
if (no_live_rescan) {
TRACE(("no life rescan allowed on device %p\n", child));
if (child->load_count + child->loading > 0) {
TRACE(("device %p loaded - skipping it\n", child));
continue;
}
child->blocked_by_rescan = true;
++child->load_block_count;
}
child->verifying = true;
child->redetected = false;
// unload driver during rescan if needed
// (need to lock next_child temporarily as we release node_lock)
if (next_child)
++next_child->ref_count;
benaphore_unlock(&gNodeLock);
pnp_unload_driver_automatically(child, true);
benaphore_lock(&gNodeLock);
if (next_child)
pnp_remove_node_ref_nolock(next_child);
}
}
/** stop children verification, resetting associated flag and unblocking loads. */
static void
reset_children_verification(pnp_node_info *node)
{
pnp_node_info *child, *next_child;
TRACE(("reset_children_verification()\n"));
benaphore_lock(&gNodeLock);
for (child = node->children; child != NULL; child = next_child) {
next_child = child->children_next;
if (!child->verifying)
continue;
child->verifying = false;
if (child->blocked_by_rescan) {
child->blocked_by_rescan = false;
pnp_unblock_load(child);
}
if (next_child)
++next_child->ref_count;
benaphore_unlock(&gNodeLock);
pnp_load_driver_automatically(node, true);
benaphore_lock(&gNodeLock);
if (next_child)
pnp_remove_node_ref_nolock(next_child);
}
benaphore_unlock(&gNodeLock);
}
/** unregister child nodes that weren't detected anymore */
static void
unregister_lost_children(pnp_node_info *node)
{
pnp_node_info *child, *dependency_list;
TRACE(("unregister_lost_children()\n"));
benaphore_lock(&gNodeLock);
for (child = node->children, dependency_list = NULL; child != NULL;
child = child->children_next) {
if (child->verifying && !child->redetected) {
TRACE(("removing lost device %p\n", child));
// child wasn't detected anymore - put it onto remove list
pnp_unregister_node_rec(child, &dependency_list);
}
}
benaphore_unlock(&gNodeLock);
// finish verification of children
// (must be done now to unblock them;
// else we risk deadlocks during notification)
reset_children_verification(node);
// inform all drivers of unregistered nodes
pnp_notify_unregistration(dependency_list);
// now, we can safely decrease ref_count of unregistered nodes
pnp_unref_unregistered_nodes(dependency_list);
}
/** recursively scan children of node (using depth-scan).
* node_lock must be hold
*/
static status_t
recursive_scan(pnp_node_info *node, uint depth)
{
pnp_node_info *child, *next_child;
status_t res;
TRACE(("recursive_scan(node: %p, depth=%d)\n", node, depth));
child = node->children;
// the child we want to access must be locked
if (child != NULL)
++child->ref_count;
for (; child != NULL; child = next_child) {
next_child = child->children_next;
// lock next child, so its node is still valid after rescan
if (next_child != NULL)
++next_child->ref_count;
// during rescan, we must release node_lock to not deadlock
benaphore_unlock(&gNodeLock);
res = pnp_rescan(child, depth - 1);
benaphore_lock(&gNodeLock);
// unlock current child as it's not accessed anymore
pnp_remove_node_ref_nolock(child);
// ignore errors because of touching unregistered nodes
if (res != B_OK && res != B_NAME_NOT_FOUND)
goto err;
}
// no need unlock last child (don't be loop already)
TRACE(("recursive_scan(): done\n"));
return B_OK;
err:
if (next_child != NULL)
pnp_remove_node_ref_nolock(next_child);
TRACE(("recursive_scan(): failed\n"));
return res;
}
/** ask bus to (re-)scan for connected devices */
static void
rescan_bus(pnp_node_info *node)
{
// busses can register their children themselves
pnp_bus_info *interface;
void *cookie;
bool defer_probe;
uint8 defer_probe_var;
// delay children probing if requested
defer_probe = pnp_get_attr_uint8(node,
PNP_BUS_DEFER_PROBE, &defer_probe_var, false ) == B_OK
&& defer_probe_var != 0;
if (defer_probe) {
TRACE(("defer probing for consumers\n"));
pnp_defer_probing_of_children(node);
}
// load driver during rescan
pnp_load_driver(node, NULL, (pnp_driver_info **)&interface, &cookie);
TRACE(("scan bus\n"));
// block other notifications (currently, only "removed" could be
// called as driver is/will stay loaded and scanning the bus
// concurrently has been assured to not happen)
pnp_start_hook_call(node);
if (interface->rescan != NULL)
interface->rescan(cookie);
pnp_finish_hook_call(node);
pnp_unload_driver(node);
// time to execute delayed probing
if (defer_probe)
pnp_probe_waiting_children(node);
}
/** rescan for consumers
* ignore_fixed_consumers - true, to leave fixed consumers alone
*/
status_t
pnp_rescan_int(pnp_node_info *node, uint depth,
bool ignore_fixed_consumers)
{
bool is_bus;
uint8 dummy;
status_t res = B_OK;
TRACE(("pnp_rescan_int(node: %p, depth=%d)\n", node, depth));
//pnp_load_boot_links();
// ignore depth 0 silently
if (depth == 0) {
res = B_OK;
goto err3;
}
is_bus = pnp_get_attr_uint8(node, PNP_BUS_IS_BUS, &dummy, false) == B_OK;
benaphore_lock(&gNodeLock);
// don't scan unregistered nodes
if (!node->registered) {
TRACE(("node not registered"));
res = B_NAME_NOT_FOUND;
goto err2;
}
if (!is_rescan_sensible(node)) {
// somebody else does the rescan, so no need to report error
TRACE(("rescan not sensible\n"));
res = B_OK;
goto err2;
}
// mark old devices
// exception: during registration, fixed consumers are
// initialized seperately, and we don't want to mark their node
// as being old
if (!ignore_fixed_consumers || is_bus)
mark_children_scanned(node);
// tell other scans what we are doing to avoid double scans
node->rescan_depth = depth;
// keep node alive
++node->ref_count;
benaphore_unlock(&gNodeLock);
// do the real thing - scan node
if (is_bus)
rescan_bus(node);
// ask possible consumers to register their nodes
if (!ignore_fixed_consumers) {
TRACE(("scan fixed consumers\n"));
res = pnp_notify_fixed_consumers(node);
if (res != B_OK)
goto err;
}
res = pnp_notify_dynamic_consumers(node);
if (res != B_OK)
goto err;
// unregister children that weren't detected anymore
unregister_lost_children(node);
benaphore_lock(&gNodeLock);
// mark scan as being finished to not block recursive scans
node->rescan_depth = 0;
// scan children recursively;
// keep the node_lock to make sure noone removes children meanwhile
if (depth > 1)
res = recursive_scan(node, depth);
pnp_remove_node_ref_nolock(node);
benaphore_unlock(&gNodeLock);
//pnp_unload_boot_links();
TRACE(("pnp_rescan_int(): done (%p) - %s\n", node, strerror(res)));
return res;
err:
reset_children_verification(node);
benaphore_lock(&gNodeLock);
node->rescan_depth = 0;
pnp_remove_node_ref_nolock(node);
err2:
benaphore_unlock(&gNodeLock);
TRACE(("pnp_rescan_int(): failed (%p, %s)\n", node, strerror(res)));
err3:
//pnp_unload_boot_links();
return res;
}