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:
parent
4a3cf87482
commit
b80d229ec2
@ -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
|
||||
;
|
||||
|
@ -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/"
|
||||
|
||||
|
450
src/kernel/core/device_manager/driver_loader.c
Normal file
450
src/kernel/core/device_manager/driver_loader.c
Normal 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;
|
||||
}
|
||||
|
224
src/kernel/core/device_manager/notifications.c
Normal file
224
src/kernel/core/device_manager/notifications.c
Normal 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);
|
||||
}
|
||||
}
|
241
src/kernel/core/device_manager/patterns.c
Normal file
241
src/kernel/core/device_manager/patterns.c
Normal 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;
|
||||
}
|
571
src/kernel/core/device_manager/probe.c
Normal file
571
src/kernel/core/device_manager/probe.c
Normal 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;
|
||||
}
|
||||
|
445
src/kernel/core/device_manager/scan.c
Normal file
445
src/kernel/core/device_manager/scan.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user