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:
@@ -7,16 +7,16 @@ KernelMergeObject kernel_device_manager.o :
|
|||||||
attributes.c
|
attributes.c
|
||||||
# boot_hack.c
|
# boot_hack.c
|
||||||
device_manager.c
|
device_manager.c
|
||||||
# driver_loader.c
|
driver_loader.c
|
||||||
id_generator.c
|
id_generator.c
|
||||||
io_resources.c
|
io_resources.c
|
||||||
# module_loader.c
|
# module_loader.c
|
||||||
nodes.c
|
nodes.c
|
||||||
# notifications.c
|
notifications.c
|
||||||
# patterns.c
|
patterns.c
|
||||||
# probe.c
|
probe.c
|
||||||
registration.c
|
registration.c
|
||||||
# scan.c
|
scan.c
|
||||||
:
|
:
|
||||||
-fno-pic -Wno-unused -D_KERNEL_MODE
|
-fno-pic -Wno-unused -D_KERNEL_MODE
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#define GENERIC_SUBDIR "/generic"
|
#define GENERIC_SUBDIR "/generic"
|
||||||
|
|
||||||
// driver registration directories
|
// driver registration directories
|
||||||
|
#define PNP_DIR
|
||||||
#define SYSTEM_DRIVER_REGISTRATION "/boot/beos/add-ons/kernel/"PNP_DIR"registration/"
|
#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/"
|
#define COMMON_DRIVER_REGISTRATION "/boot/home/config/add-ons/kernel/"PNP_DIR"registration/"
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user