* Started implementing node monitoring in the devfs and device manager.
* Right now, only already known loaded drivers will be monitored for changes; their devices aren't republished, though, since that would cause a deadlock in the node notification mechanism (listeners are called synchronously); need to offload that the event handling to another thread. * On changes of (known) driver directories, the device manager will now print some info to the syslog. * Fixed republish_driver() I broke recently (would skip every other node), and moved it to the driver functions section of the devfs.cpp. * Implemented currently unused unpublish_driver() function that would have to be called before reloading a driver. * If a driver is in use when it's updated, we mark it, but we don't do anything with that info when we could. * Minor cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24036 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
SubDir HAIKU_TOP src system kernel device_manager ;
|
SubDir HAIKU_TOP src system kernel device_manager ;
|
||||||
|
|
||||||
UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ;
|
UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ;
|
||||||
UsePrivateHeaders [ FDirName kernel util ] ;
|
UsePrivateHeaders [ FDirName kernel util ] shared ;
|
||||||
|
|
||||||
KernelMergeObject kernel_device_manager.o :
|
KernelMergeObject kernel_device_manager.o :
|
||||||
attributes.c
|
attributes.c
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2004-2005, Axel Dörfler, [email protected]. All rights reserved.
|
* Copyright 2004-2008, Axel Dörfler, [email protected]. All rights reserved.
|
||||||
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
||||||
*
|
*
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Part of Device Manager
|
|
||||||
Probing for consumers.
|
Probing for consumers.
|
||||||
|
|
||||||
Here is all the core logic how consumers are found for one node.
|
Here is all the core logic how consumers are found for one node.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -17,14 +15,17 @@
|
|||||||
|
|
||||||
#include <boot_device.h>
|
#include <boot_device.h>
|
||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
#include <kmodule.h>
|
|
||||||
#include <fs/KPath.h>
|
|
||||||
#include <fs/devfs.h>
|
#include <fs/devfs.h>
|
||||||
|
#include <fs/KPath.h>
|
||||||
|
#include <fs/node_monitor.h>
|
||||||
|
#include <kmodule.h>
|
||||||
|
#include <Notifications.h>
|
||||||
#include <util/Stack.h>
|
#include <util/Stack.h>
|
||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
#include <image.h>
|
#include <image.h>
|
||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
|
#include <NodeMonitor.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
@@ -68,19 +69,22 @@ const char *pnp_registration_dirs[2] = {
|
|||||||
static const char *kModulePaths[] = {
|
static const char *kModulePaths[] = {
|
||||||
COMMON_MODULES_DIR,
|
COMMON_MODULES_DIR,
|
||||||
"/boot/beos/system/add-ons/kernel",//SYSTEM_MODULES_DIR,
|
"/boot/beos/system/add-ons/kernel",//SYSTEM_MODULES_DIR,
|
||||||
"/boot", // ToDo: this is for the bootfs boot - to be removed
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class DirectoryIterator {
|
class DirectoryIterator {
|
||||||
public:
|
public:
|
||||||
DirectoryIterator(const char *path, const char *subPath = NULL, bool recursive = false);
|
DirectoryIterator(const char *path, const char *subPath = NULL,
|
||||||
DirectoryIterator(const char **paths, const char *subPath = NULL, bool recursive = false);
|
bool recursive = false);
|
||||||
|
DirectoryIterator(const char **paths, const char *subPath = NULL,
|
||||||
|
bool recursive = false);
|
||||||
~DirectoryIterator();
|
~DirectoryIterator();
|
||||||
|
|
||||||
void SetTo(const char *path, const char *subPath = NULL, bool recursive = false);
|
void SetTo(const char *path, const char *subPath = NULL,
|
||||||
void SetTo(const char **paths, const char *subPath = NULL, bool recursive = false);
|
bool recursive = false);
|
||||||
|
void SetTo(const char **paths, const char *subPath = NULL,
|
||||||
|
bool recursive = false);
|
||||||
|
|
||||||
status_t GetNext(KPath &path, struct stat &stat);
|
status_t GetNext(KPath &path, struct stat &stat);
|
||||||
const char *CurrentName() const { return fCurrentName; }
|
const char *CurrentName() const { return fCurrentName; }
|
||||||
@@ -97,7 +101,22 @@ class DirectoryIterator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
DirectoryIterator::DirectoryIterator(const char *path, const char *subPath, bool recursive)
|
class DirectoryWatcher : public NotificationListener {
|
||||||
|
public:
|
||||||
|
DirectoryWatcher();
|
||||||
|
virtual ~DirectoryWatcher();
|
||||||
|
|
||||||
|
virtual void EventOccured(NotificationService& service,
|
||||||
|
const KMessage* event);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static DirectoryWatcher sDirectoryWatcher;
|
||||||
|
static bool sWatching;
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryIterator::DirectoryIterator(const char *path, const char *subPath,
|
||||||
|
bool recursive)
|
||||||
:
|
:
|
||||||
fDirectory(NULL),
|
fDirectory(NULL),
|
||||||
fBasePath(NULL),
|
fBasePath(NULL),
|
||||||
@@ -107,7 +126,8 @@ DirectoryIterator::DirectoryIterator(const char *path, const char *subPath, bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DirectoryIterator::DirectoryIterator(const char **paths, const char *subPath, bool recursive)
|
DirectoryIterator::DirectoryIterator(const char **paths, const char *subPath,
|
||||||
|
bool recursive)
|
||||||
:
|
:
|
||||||
fDirectory(NULL),
|
fDirectory(NULL),
|
||||||
fBasePath(NULL)
|
fBasePath(NULL)
|
||||||
@@ -226,7 +246,52 @@ DirectoryIterator::AddPath(const char *basePath, const char *subPath)
|
|||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
struct path_entry *
|
DirectoryWatcher::DirectoryWatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryWatcher::~DirectoryWatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DirectoryWatcher::EventOccured(NotificationService& service,
|
||||||
|
const KMessage* event)
|
||||||
|
{
|
||||||
|
int32 opcode = event->GetInt32("opcode", -1);
|
||||||
|
/*
|
||||||
|
if (opcode == B_ENTRY_CREATED)
|
||||||
|
devfs_publish_directory();
|
||||||
|
*/
|
||||||
|
dprintf("DRIVER DIRECTORY EVENT: %ld\n", opcode);
|
||||||
|
dprintf(" device %ld\n", event->GetInt32("device", -1));
|
||||||
|
dprintf(" node %Ld\n", event->GetInt64("node", -1));
|
||||||
|
dprintf(" name %s\n", event->GetString("name", "<none>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
start_watching(const char *base, const char *sub)
|
||||||
|
{
|
||||||
|
KPath path(base);
|
||||||
|
path.Append(sub);
|
||||||
|
|
||||||
|
// TODO: create missing directories?
|
||||||
|
struct stat stat;
|
||||||
|
if (::stat(path.Path(), &stat) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
|
||||||
|
sDirectoryWatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct path_entry *
|
||||||
new_path_entry(const char *path, dev_t device, ino_t node)
|
new_path_entry(const char *path, dev_t device, ino_t node)
|
||||||
{
|
{
|
||||||
path_entry *entry = (path_entry *)malloc(sizeof(path_entry));
|
path_entry *entry = (path_entry *)malloc(sizeof(path_entry));
|
||||||
@@ -246,7 +311,7 @@ new_path_entry(const char *path, dev_t device, ino_t node)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct path_entry *
|
static struct path_entry *
|
||||||
copy_path_entry(path_entry *entry)
|
copy_path_entry(path_entry *entry)
|
||||||
{
|
{
|
||||||
path_entry *newEntry = (path_entry *)malloc(sizeof(struct path_entry));
|
path_entry *newEntry = (path_entry *)malloc(sizeof(struct path_entry));
|
||||||
@@ -264,7 +329,7 @@ copy_path_entry(path_entry *entry)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
free_module_entry(module_entry *entry)
|
free_module_entry(module_entry *entry)
|
||||||
{
|
{
|
||||||
if (entry->name != NULL && entry->driver != NULL)
|
if (entry->name != NULL && entry->driver != NULL)
|
||||||
@@ -275,7 +340,7 @@ free_module_entry(module_entry *entry)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct module_entry *
|
static struct module_entry *
|
||||||
new_module_entry(const char *name, driver_module_info *driver)
|
new_module_entry(const char *name, driver_module_info *driver)
|
||||||
{
|
{
|
||||||
module_entry *entry = (module_entry *)malloc(sizeof(module_entry));
|
module_entry *entry = (module_entry *)malloc(sizeof(module_entry));
|
||||||
@@ -331,7 +396,8 @@ add_device_node(struct list *list, device_node_info *node)
|
|||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
get_next_device_node(struct list *list, uint32 *_cookie, device_node_info **_node)
|
get_next_device_node(struct list *list, uint32 *_cookie,
|
||||||
|
device_node_info **_node)
|
||||||
{
|
{
|
||||||
node_entry *entry = (node_entry *)list_get_next_item(list, (void *)*_cookie);
|
node_entry *entry = (node_entry *)list_get_next_item(list, (void *)*_cookie);
|
||||||
if (entry == NULL)
|
if (entry == NULL)
|
||||||
@@ -355,11 +421,10 @@ remove_device_nodes(struct list *list)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** notify a consumer that a device he might handle is added
|
/*! Notify a consumer that a device he might handle is added
|
||||||
* fileName - file name of consumer
|
fileName - file name of consumer
|
||||||
* (moved to end of file to avoid inlining)
|
(moved to end of file to avoid inlining)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
notify_probe_by_file(device_node_info *node, const char *fileName)
|
notify_probe_by_file(device_node_info *node, const char *fileName)
|
||||||
{
|
{
|
||||||
@@ -387,7 +452,9 @@ notify_probe_by_file(device_node_info *node, const char *fileName)
|
|||||||
TRACE(("notify_probe_by_file trying %s %s %s\n", module_name, bus, module_name + (strlen(module_name) - suffix_length + 1)));
|
TRACE(("notify_probe_by_file trying %s %s %s\n", module_name, bus, module_name + (strlen(module_name) - suffix_length + 1)));
|
||||||
if (strlen(module_name) > suffix_length
|
if (strlen(module_name) > suffix_length
|
||||||
&& !strncmp(module_name + (strlen(module_name) - suffix_length + 1), bus, strlen(bus))
|
&& !strncmp(module_name + (strlen(module_name) - suffix_length + 1), bus, strlen(bus))
|
||||||
&& !strncmp(module_name + (strlen(module_name) - sizeof(device_suffix) + 1), device_suffix, sizeof(device_suffix))) {
|
&& !strncmp(module_name + (strlen(module_name)
|
||||||
|
- sizeof(device_suffix) + 1), device_suffix,
|
||||||
|
sizeof(device_suffix))) {
|
||||||
// found the module
|
// found the module
|
||||||
res = dm_register_child_device(node, module_name, true);
|
res = dm_register_child_device(node, module_name, true);
|
||||||
break;
|
break;
|
||||||
@@ -401,15 +468,14 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** compose all possible names of Specific drivers; as there are
|
/*! Compose all possible names of Specific drivers; as there are
|
||||||
* multiple names which only differ in length, the most specific
|
multiple names which only differ in length, the most specific
|
||||||
* driver name gets stored in <path>, whereas <term_array> is a
|
driver name gets stored in <path>, whereas <term_array> is a
|
||||||
* list of lengths of individual driver names with index 0
|
list of lengths of individual driver names with index 0
|
||||||
* containing the length of the shortest and num_parts-1 the
|
containing the length of the shortest and num_parts-1 the
|
||||||
* length of the longest name; <buffer> is a supplied buffer of
|
length of the longest name; <buffer> is a supplied buffer of
|
||||||
* MAX_PATH+1 size
|
MAX_PATH+1 size
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
compose_driver_names(device_node_info *node, const char *dir,
|
compose_driver_names(device_node_info *node, const char *dir,
|
||||||
const char *filename_pattern, int num_parts,
|
const char *filename_pattern, int num_parts,
|
||||||
@@ -454,12 +520,11 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** notify all drivers under <directory>. If <tell_all> is true, notify all,
|
/*! Notify all drivers under <directory>. If <tell_all> is true, notify all,
|
||||||
* if false, stop once a drivers notification function returned B_OK
|
if false, stop once a drivers notification function returned B_OK
|
||||||
* buffer - scratch buffer of size B_PATH_NAME_LENGTH + 1 ; destroyed on exit
|
buffer - scratch buffer of size B_PATH_NAME_LENGTH + 1 ; destroyed on exit
|
||||||
* return: B_NAME_NOT_FOUND, if tell_all is false and no driver returned B_OK
|
return: B_NAME_NOT_FOUND, if tell_all is false and no driver returned B_OK
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
try_drivers(device_node_info *node, char *directory,
|
try_drivers(device_node_info *node, char *directory,
|
||||||
bool tell_all, char *buffer)
|
bool tell_all, char *buffer)
|
||||||
@@ -511,12 +576,11 @@ try_drivers(device_node_info *node, char *directory,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** find normal child of node that are stored under <dir>; first, we
|
/*! Find normal child of node that are stored under <dir>; first, we
|
||||||
* look for a specific driver; if none could be found, find a generic
|
look for a specific driver; if none could be found, find a generic
|
||||||
* one path, buffer - used as scratch buffer (all of size B_PATH_NAME_LENGTH + 1)
|
one path, buffer - used as scratch buffer (all of size B_PATH_NAME_LENGTH + 1)
|
||||||
* return: B_NAME_NOT_FOUND if no consumer could be found
|
return: B_NAME_NOT_FOUND if no consumer could be found
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
find_normal_child(device_node_info *node, const char *dir,
|
find_normal_child(device_node_info *node, const char *dir,
|
||||||
const char *filename_pattern, int num_parts,
|
const char *filename_pattern, int num_parts,
|
||||||
@@ -584,16 +648,15 @@ find_normal_child(device_node_info *node, const char *dir,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** pre-process dynamic child name pattern.
|
/*! Pre-process dynamic child name pattern.
|
||||||
* split into directory and pattern and count split positions;
|
split into directory and pattern and count split positions;
|
||||||
* further, remove quotes from directory
|
further, remove quotes from directory
|
||||||
* pattern - pattern of consumer name
|
pattern - pattern of consumer name
|
||||||
* buffer - buffer to store results in
|
buffer - buffer to store results in
|
||||||
* (returned strings all point to <buffer>!)
|
(returned strings all point to <buffer>!)
|
||||||
* filename_pattern - pattern of file name
|
filename_pattern - pattern of file name
|
||||||
* *num_parts - number of split positions
|
*num_parts - number of split positions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
preprocess_child_names(const char *pattern, char *buffer,
|
preprocess_child_names(const char *pattern, char *buffer,
|
||||||
char **filename_pattern, int *const num_parts)
|
char **filename_pattern, int *const num_parts)
|
||||||
@@ -660,11 +723,10 @@ preprocess_child_names(const char *pattern, char *buffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** find consumers for one given pattern
|
/*! Find consumers for one given pattern
|
||||||
* has_normal_drivers - in: true - don't search for specific driver
|
has_normal_drivers - in: true - don't search for specific driver
|
||||||
* out: true - specific driver was found
|
out: true - specific driver was found
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
register_dynamic_child_device(device_node_info *node, const char *bus,
|
register_dynamic_child_device(device_node_info *node, const char *bus,
|
||||||
const char *pattern, bool *has_normal_driver)
|
const char *pattern, bool *has_normal_driver)
|
||||||
@@ -738,14 +800,13 @@ find_node_ref_in_list(struct list *list, dev_t device, ino_t node)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Iterates over the given list and tries to load all drivers and modules
|
/*! Iterates over the given list and tries to load all drivers and modules
|
||||||
* in that list.
|
in that list.
|
||||||
* The list is emptied and freed during the traversal.
|
The list is emptied and freed during the traversal.
|
||||||
*
|
|
||||||
* ToDo: Old style drivers will be initialized as well, new style driver
|
|
||||||
* handling is not yet done.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
ToDo: Old style drivers will be initialized as well, new style driver
|
||||||
|
handling is not yet done.
|
||||||
|
*/
|
||||||
static status_t
|
static status_t
|
||||||
try_drivers(struct list &list, bool tryBusDrivers)
|
try_drivers(struct list &list, bool tryBusDrivers)
|
||||||
{
|
{
|
||||||
@@ -960,7 +1021,8 @@ get_loaded_modules(struct list *modules, const char *bus, const char *device)
|
|||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
get_nodes_for_device_type(device_node_info *node, struct list *list, const char *type)
|
get_nodes_for_device_type(device_node_info *node, struct list *list,
|
||||||
|
const char *type)
|
||||||
{
|
{
|
||||||
bool matches = false;
|
bool matches = false;
|
||||||
|
|
||||||
@@ -977,8 +1039,9 @@ get_nodes_for_device_type(device_node_info *node, struct list *list, const char
|
|||||||
if (!matches) {
|
if (!matches) {
|
||||||
// we also accept any dump busses
|
// we also accept any dump busses
|
||||||
uint8 onDemand = false;
|
uint8 onDemand = false;
|
||||||
pnp_get_attr_uint8(node, B_DRIVER_FIND_DEVICES_ON_DEMAND, &onDemand, false);
|
pnp_get_attr_uint8(node, B_DRIVER_FIND_DEVICES_ON_DEMAND, &onDemand,
|
||||||
|
false);
|
||||||
|
|
||||||
if (onDemand)
|
if (onDemand)
|
||||||
matches = true;
|
matches = true;
|
||||||
}
|
}
|
||||||
@@ -1009,12 +1072,12 @@ get_nodes_for_device_type(device_node_info *node, struct list *list, const char
|
|||||||
// device manager private API
|
// device manager private API
|
||||||
|
|
||||||
|
|
||||||
/** Register the device of a child for the \a node that might accept it.
|
/*! Register the device of a child for the \a node that might accept it.
|
||||||
* childName - module name of child/consumer
|
childName - module name of child/consumer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
dm_register_child_device(device_node_info *node, const char *childName, bool checkSupport)
|
dm_register_child_device(device_node_info *node, const char *childName,
|
||||||
|
bool checkSupport)
|
||||||
{
|
{
|
||||||
driver_module_info *child;
|
driver_module_info *child;
|
||||||
status_t status;
|
status_t status;
|
||||||
@@ -1057,11 +1120,10 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** find and notify dynamic consumers that device was added
|
/*! Find and notify dynamic consumers that device was added
|
||||||
* errors returned by consumers aren't reported, only problems
|
errors returned by consumers aren't reported, only problems
|
||||||
* like malformed consumer patterns
|
like malformed consumer patterns
|
||||||
*/
|
*/
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
dm_register_dynamic_child_devices(device_node_info *node)
|
dm_register_dynamic_child_devices(device_node_info *node)
|
||||||
{
|
{
|
||||||
@@ -1143,11 +1205,10 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Register all fixed child devices of of the given \a node; in contrast
|
/*! Register all fixed child devices of of the given \a node; in contrast
|
||||||
* to dynamic child devices, errors reported by fixed child devices are
|
to dynamic child devices, errors reported by fixed child devices are
|
||||||
* not ignored but returned, and regarded critical.
|
not ignored but returned, and regarded critical.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
dm_register_fixed_child_devices(device_node_info *node)
|
dm_register_fixed_child_devices(device_node_info *node)
|
||||||
{
|
{
|
||||||
@@ -1236,9 +1297,13 @@ probe_for_driver_modules(const char *type)
|
|||||||
|
|
||||||
while (iterator.GetNext(path, stat) == B_OK) {
|
while (iterator.GetNext(path, stat) == B_OK) {
|
||||||
if (S_ISDIR(stat.st_mode)) {
|
if (S_ISDIR(stat.st_mode)) {
|
||||||
|
add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
|
||||||
|
sDirectoryWatcher);
|
||||||
|
|
||||||
// We need to make sure that drivers in ie. "audio/raw/" can
|
// We need to make sure that drivers in ie. "audio/raw/" can
|
||||||
// be found as well - therefore, we must make sure that "audio"
|
// be found as well - therefore, we must make sure that "audio"
|
||||||
// exists on /dev.
|
// exists on /dev.
|
||||||
|
|
||||||
size_t length = strlen("drivers/dev");
|
size_t length = strlen("drivers/dev");
|
||||||
if (strncmp(type, "drivers/dev", length))
|
if (strncmp(type, "drivers/dev", length))
|
||||||
continue;
|
continue;
|
||||||
@@ -1274,7 +1339,8 @@ probe_for_driver_modules(const char *type)
|
|||||||
dprintf("bus: %s\n", path.Leaf());
|
dprintf("bus: %s\n", path.Leaf());
|
||||||
|
|
||||||
while (busIterator.GetNext(path, stat) == B_OK) {
|
while (busIterator.GetNext(path, stat) == B_OK) {
|
||||||
path_entry *entry = find_node_ref_in_list(&drivers, stat.st_dev, stat.st_ino);
|
path_entry *entry = find_node_ref_in_list(&drivers, stat.st_dev,
|
||||||
|
stat.st_ino);
|
||||||
if (entry == NULL)
|
if (entry == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -1310,7 +1376,20 @@ probe_for_device_type(const char *type)
|
|||||||
TRACE(("probe_for_device_type(type = %s)\n", type));
|
TRACE(("probe_for_device_type(type = %s)\n", type));
|
||||||
|
|
||||||
char deviceType[64];
|
char deviceType[64];
|
||||||
snprintf(deviceType, sizeof(deviceType), "drivers/dev%s%s", type[0] ? "/" : "", type);
|
snprintf(deviceType, sizeof(deviceType), "drivers/dev%s%s",
|
||||||
|
type[0] ? "/" : "", type);
|
||||||
|
|
||||||
|
if (!sWatching && gBootDevice > 0) {
|
||||||
|
// We're probing the actual boot volume for the first time,
|
||||||
|
// let's watch its driver directories for changes
|
||||||
|
|
||||||
|
new(&sDirectoryWatcher) DirectoryWatcher;
|
||||||
|
for (uint32 i = 0; kModulePaths[i] != NULL; i++) {
|
||||||
|
start_watching(kModulePaths[i], "drivers/dev");
|
||||||
|
start_watching(kModulePaths[i], "drivers/bin");
|
||||||
|
}
|
||||||
|
sWatching = true;
|
||||||
|
}
|
||||||
|
|
||||||
return probe_for_driver_modules(deviceType);
|
return probe_for_driver_modules(deviceType);
|
||||||
}
|
}
|
||||||
|
|||||||
+263
-145
@@ -7,34 +7,36 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "IOScheduler.h"
|
|
||||||
|
|
||||||
#include <KernelExport.h>
|
|
||||||
#include <Drivers.h>
|
|
||||||
#include <pnp_devfs.h>
|
|
||||||
#include <NodeMonitor.h>
|
|
||||||
|
|
||||||
#include <boot_device.h>
|
|
||||||
#include <kdevice_manager.h>
|
|
||||||
#include <KPath.h>
|
|
||||||
#include <vfs.h>
|
|
||||||
#include <debug.h>
|
|
||||||
#include <util/khash.h>
|
|
||||||
#include <util/AutoLock.h>
|
|
||||||
#include <elf.h>
|
|
||||||
#include <lock.h>
|
|
||||||
#include <vm.h>
|
|
||||||
#include <arch/cpu.h>
|
|
||||||
#include <boot/kernel_args.h>
|
|
||||||
|
|
||||||
#include <devfs.h>
|
#include <devfs.h>
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <Drivers.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <NodeMonitor.h>
|
||||||
|
#include <pnp_devfs.h>
|
||||||
|
|
||||||
|
#include <arch/cpu.h>
|
||||||
|
#include <boot/kernel_args.h>
|
||||||
|
#include <boot_device.h>
|
||||||
|
#include <debug.h>
|
||||||
|
#include <elf.h>
|
||||||
|
#include <kdevice_manager.h>
|
||||||
|
#include <KPath.h>
|
||||||
|
#include <lock.h>
|
||||||
|
#include <node_monitor.h>
|
||||||
|
#include <Notifications.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
#include <util/khash.h>
|
||||||
|
#include <vfs.h>
|
||||||
|
#include <vm.h>
|
||||||
|
|
||||||
|
#include "IOScheduler.h"
|
||||||
|
|
||||||
|
|
||||||
//#define TRACE_DEVFS
|
//#define TRACE_DEVFS
|
||||||
#ifdef TRACE_DEVFS
|
#ifdef TRACE_DEVFS
|
||||||
@@ -131,6 +133,8 @@ struct driver_entry {
|
|||||||
time_t last_modified;
|
time_t last_modified;
|
||||||
image_id image;
|
image_id image;
|
||||||
uint32 devices_published;
|
uint32 devices_published;
|
||||||
|
uint32 devices_used;
|
||||||
|
bool binary_updated;
|
||||||
|
|
||||||
// driver image information
|
// driver image information
|
||||||
int32 *api_version;
|
int32 *api_version;
|
||||||
@@ -140,14 +144,24 @@ struct driver_entry {
|
|||||||
status_t (*uninit_hardware)(void);
|
status_t (*uninit_hardware)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct node_path_entry : DoublyLinkedListLinkImpl<node_path_entry> {
|
struct node_path_entry : DoublyLinkedListLinkImpl<node_path_entry> {
|
||||||
char path[B_PATH_NAME_LENGTH];
|
char path[B_PATH_NAME_LENGTH];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DriverWatcher : public NotificationListener {
|
||||||
|
public:
|
||||||
|
DriverWatcher();
|
||||||
|
virtual ~DriverWatcher();
|
||||||
|
|
||||||
|
virtual void EventOccured(NotificationService& service,
|
||||||
|
const KMessage* event);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static void get_device_name(struct devfs_vnode *vnode, char *buffer,
|
static void get_device_name(struct devfs_vnode *vnode, char *buffer,
|
||||||
size_t size);
|
size_t size);
|
||||||
|
static status_t unpublish_node(struct devfs *fs, devfs_vnode *node,
|
||||||
|
mode_t type);
|
||||||
static status_t publish_device(struct devfs *fs, const char *path,
|
static status_t publish_device(struct devfs *fs, const char *path,
|
||||||
device_node_info *deviceNode, pnp_devfs_driver_info *info,
|
device_node_info *deviceNode, pnp_devfs_driver_info *info,
|
||||||
driver_entry *driver, device_hooks *ops, int32 apiVersion);
|
driver_entry *driver, device_hooks *ops, int32 apiVersion);
|
||||||
@@ -156,6 +170,7 @@ static status_t publish_device(struct devfs *fs, const char *path,
|
|||||||
/* the one and only allowed devfs instance */
|
/* the one and only allowed devfs instance */
|
||||||
static struct devfs *sDeviceFileSystem = NULL;
|
static struct devfs *sDeviceFileSystem = NULL;
|
||||||
static int32 sDefaultApiVersion = 1;
|
static int32 sDefaultApiVersion = 1;
|
||||||
|
static DriverWatcher sDriverWatcher;
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - driver private
|
// #pragma mark - driver private
|
||||||
@@ -211,24 +226,25 @@ load_driver(driver_entry *driver)
|
|||||||
#error Add checks here for new vs old api version!
|
#error Add checks here for new vs old api version!
|
||||||
#endif
|
#endif
|
||||||
if (*driver->api_version > B_CUR_DRIVER_API_VERSION) {
|
if (*driver->api_version > B_CUR_DRIVER_API_VERSION) {
|
||||||
dprintf("%s: api_version %ld not handled\n", driver->name,
|
dprintf("devfs: \"%s\" api_version %ld not handled\n", driver->name,
|
||||||
*driver->api_version);
|
*driver->api_version);
|
||||||
status = B_BAD_VALUE;
|
status = B_BAD_VALUE;
|
||||||
goto error1;
|
goto error1;
|
||||||
}
|
}
|
||||||
if (*driver->api_version < 1) {
|
if (*driver->api_version < 1) {
|
||||||
dprintf("%s: api_version invalid\n", driver->name);
|
dprintf("devfs: \"%s\" api_version invalid\n", driver->name);
|
||||||
status = B_BAD_VALUE;
|
status = B_BAD_VALUE;
|
||||||
goto error1;
|
goto error1;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
dprintf("%s: api_version missing\n", driver->name);
|
dprintf("devfs: \"%s\" api_version missing\n", driver->name);
|
||||||
|
|
||||||
if (get_image_symbol(image, "publish_devices", B_SYMBOL_TYPE_TEXT,
|
if (get_image_symbol(image, "publish_devices", B_SYMBOL_TYPE_TEXT,
|
||||||
(void **)&driver->publish_devices) != B_OK
|
(void **)&driver->publish_devices) != B_OK
|
||||||
|| get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT,
|
|| get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT,
|
||||||
(void **)&driver->find_device) != B_OK) {
|
(void **)&driver->find_device) != B_OK) {
|
||||||
dprintf("%s: mandatory driver symbol(s) missing!\n", driver->name);
|
dprintf("devfs: \"%s\" mandatory driver symbol(s) missing!\n",
|
||||||
|
driver->name);
|
||||||
status = B_BAD_VALUE;
|
status = B_BAD_VALUE;
|
||||||
goto error1;
|
goto error1;
|
||||||
}
|
}
|
||||||
@@ -238,8 +254,8 @@ load_driver(driver_entry *driver)
|
|||||||
if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT,
|
if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT,
|
||||||
(void **)&init_hardware) == B_OK
|
(void **)&init_hardware) == B_OK
|
||||||
&& (status = init_hardware()) != B_OK) {
|
&& (status = init_hardware()) != B_OK) {
|
||||||
dprintf("%s: init_hardware() failed: %s\n", driver->name,
|
TRACE(("%s: init_hardware() failed: %s\n", driver->name,
|
||||||
strerror(status));
|
strerror(status)));
|
||||||
status = ENXIO;
|
status = ENXIO;
|
||||||
goto error1;
|
goto error1;
|
||||||
}
|
}
|
||||||
@@ -247,8 +263,8 @@ load_driver(driver_entry *driver)
|
|||||||
if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT,
|
if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT,
|
||||||
(void **)&init_driver) == B_OK
|
(void **)&init_driver) == B_OK
|
||||||
&& (status = init_driver()) != B_OK) {
|
&& (status = init_driver()) != B_OK) {
|
||||||
dprintf("%s: init_driver() failed: %s\n", driver->name,
|
TRACE(("%s: init_driver() failed: %s\n", driver->name,
|
||||||
strerror(status));
|
strerror(status)));
|
||||||
status = ENXIO;
|
status = ENXIO;
|
||||||
goto error2;
|
goto error2;
|
||||||
}
|
}
|
||||||
@@ -267,7 +283,7 @@ load_driver(driver_entry *driver)
|
|||||||
// we keep the driver loaded if it exports at least a single interface
|
// we keep the driver loaded if it exports at least a single interface
|
||||||
devicePaths = driver->publish_devices();
|
devicePaths = driver->publish_devices();
|
||||||
if (devicePaths == NULL) {
|
if (devicePaths == NULL) {
|
||||||
dprintf("%s: publish_devices() returned NULL.\n", driver->name);
|
TRACE(("%s: publish_devices() returned NULL.\n", driver->name));
|
||||||
status = ENXIO;
|
status = ENXIO;
|
||||||
goto error3;
|
goto error3;
|
||||||
}
|
}
|
||||||
@@ -327,6 +343,133 @@ unload_driver(driver_entry *driver)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Collects all devices belonging to the \a driver and unpublishs them.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
unpublish_driver(driver_entry *driver)
|
||||||
|
{
|
||||||
|
RecursiveLocker locker(&sDeviceFileSystem->lock);
|
||||||
|
|
||||||
|
// Iterate through all nodes until all devices of this driver have
|
||||||
|
// been unpublished
|
||||||
|
|
||||||
|
while (driver->devices_published > 0) {
|
||||||
|
struct hash_iterator i;
|
||||||
|
hash_open(sDeviceFileSystem->vnode_hash, &i);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
devfs_vnode *vnode = (devfs_vnode *)hash_next(
|
||||||
|
sDeviceFileSystem->vnode_hash, &i);
|
||||||
|
if (vnode == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (S_ISCHR(vnode->stream.type)
|
||||||
|
&& vnode->stream.u.dev.driver == driver)
|
||||||
|
unpublish_node(sDeviceFileSystem, vnode, S_IFCHR);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_close(sDeviceFileSystem->vnode_hash, &i, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Collects all published devices of a driver, compares them to what the
|
||||||
|
driver would publish now, and then publishes/unpublishes the devices
|
||||||
|
as needed.
|
||||||
|
If the driver does not publish any devices anymore, it is unloaded.
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
republish_driver(driver_entry *driver)
|
||||||
|
{
|
||||||
|
if (driver->image < 0) {
|
||||||
|
// The driver is not yet loaded - go through the normal load procedure
|
||||||
|
return load_driver(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
RecursiveLocker locker(&sDeviceFileSystem->lock);
|
||||||
|
|
||||||
|
// build the list of currently present devices of this driver
|
||||||
|
// by iterating through all present nodes
|
||||||
|
struct hash_iterator i;
|
||||||
|
hash_open(sDeviceFileSystem->vnode_hash, &i);
|
||||||
|
|
||||||
|
DoublyLinkedList<node_path_entry> currentNodes;
|
||||||
|
while (true) {
|
||||||
|
devfs_vnode *vnode = (devfs_vnode *)hash_next(
|
||||||
|
sDeviceFileSystem->vnode_hash, &i);
|
||||||
|
if (vnode == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (S_ISCHR(vnode->stream.type)
|
||||||
|
&& vnode->stream.u.dev.driver == driver) {
|
||||||
|
node_path_entry *path = new(std::nothrow) node_path_entry;
|
||||||
|
if (!path) {
|
||||||
|
while ((path = currentNodes.RemoveHead()))
|
||||||
|
delete path;
|
||||||
|
hash_close(sDeviceFileSystem->vnode_hash, &i, false);
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_device_name(vnode, path->path, sizeof(path->path));
|
||||||
|
currentNodes.Add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash_close(sDeviceFileSystem->vnode_hash, &i, false);
|
||||||
|
|
||||||
|
// now ask the driver for it's currently published devices
|
||||||
|
const char **devicePaths = driver->publish_devices();
|
||||||
|
|
||||||
|
int32 exported = 0;
|
||||||
|
for (; devicePaths != NULL && devicePaths[0]; devicePaths++) {
|
||||||
|
bool present = false;
|
||||||
|
node_path_entry *entry = currentNodes.Head();
|
||||||
|
while (entry) {
|
||||||
|
if (strncmp(entry->path, devicePaths[0], B_PATH_NAME_LENGTH) == 0) {
|
||||||
|
// this device was present before and still is -> no republish
|
||||||
|
currentNodes.Remove(entry);
|
||||||
|
delete entry;
|
||||||
|
exported++;
|
||||||
|
present = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = currentNodes.GetNext(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (present)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// the device was not present before -> publish it now
|
||||||
|
device_hooks *hooks = driver->find_device(devicePaths[0]);
|
||||||
|
if (hooks == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TRACE(("devfs: publishing new device \"%s\"\n", devicePaths[0]));
|
||||||
|
if (publish_device(sDeviceFileSystem, devicePaths[0], NULL, NULL,
|
||||||
|
driver, hooks, *driver->api_version) == B_OK)
|
||||||
|
exported++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// what's left in currentNodes was present but is not anymore -> unpublish
|
||||||
|
node_path_entry *entry = currentNodes.Head();
|
||||||
|
while (entry) {
|
||||||
|
TRACE(("devfs: unpublishing no more present \"%s\"\n", entry->path));
|
||||||
|
devfs_unpublish_device(entry->path, true);
|
||||||
|
node_path_entry *next = currentNodes.GetNext(entry);
|
||||||
|
currentNodes.Remove(entry);
|
||||||
|
delete entry;
|
||||||
|
entry = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exported == 0) {
|
||||||
|
TRACE(("devfs: driver \"%s\" does not publish any more nodes and is unloaded\n", driver->path));
|
||||||
|
unload_driver(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
get_leaf(const char *path)
|
get_leaf(const char *path)
|
||||||
{
|
{
|
||||||
@@ -338,6 +481,25 @@ get_leaf(const char *path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static driver_entry *
|
||||||
|
find_driver(dev_t device, ino_t node)
|
||||||
|
{
|
||||||
|
hash_iterator iterator;
|
||||||
|
hash_open(sDeviceFileSystem->driver_hash, &iterator);
|
||||||
|
driver_entry *driver;
|
||||||
|
while (true) {
|
||||||
|
driver = (driver_entry *)hash_next(sDeviceFileSystem->driver_hash,
|
||||||
|
&iterator);
|
||||||
|
if (driver == NULL
|
||||||
|
|| driver->device == device && driver->node == node)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_close(sDeviceFileSystem->driver_hash, &iterator, false);
|
||||||
|
return driver;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
add_driver(const char *path, image_id image)
|
add_driver(const char *path, image_id image)
|
||||||
{
|
{
|
||||||
@@ -361,6 +523,8 @@ add_driver(const char *path, image_id image)
|
|||||||
sDeviceFileSystem->driver_hash, get_leaf(path));
|
sDeviceFileSystem->driver_hash, get_leaf(path));
|
||||||
if (driver != NULL) {
|
if (driver != NULL) {
|
||||||
// we know this driver
|
// we know this driver
|
||||||
|
// TODO: check if this driver is a different one and has precendence
|
||||||
|
// (ie. common supersedes system).
|
||||||
// TODO: test for changes here and/or via node monitoring and reload
|
// TODO: test for changes here and/or via node monitoring and reload
|
||||||
// the driver if necessary
|
// the driver if necessary
|
||||||
if (driver->image < B_OK)
|
if (driver->image < B_OK)
|
||||||
@@ -386,6 +550,9 @@ add_driver(const char *path, image_id image)
|
|||||||
driver->node = stat.st_ino;
|
driver->node = stat.st_ino;
|
||||||
driver->image = image;
|
driver->image = image;
|
||||||
driver->last_modified = stat.st_mtime;
|
driver->last_modified = stat.st_mtime;
|
||||||
|
driver->devices_published = 0;
|
||||||
|
driver->devices_used = 0;
|
||||||
|
driver->binary_updated = false;
|
||||||
|
|
||||||
driver->api_version = NULL;
|
driver->api_version = NULL;
|
||||||
driver->find_device = NULL;
|
driver->find_device = NULL;
|
||||||
@@ -394,6 +561,8 @@ add_driver(const char *path, image_id image)
|
|||||||
driver->uninit_hardware = NULL;
|
driver->uninit_hardware = NULL;
|
||||||
|
|
||||||
hash_insert(sDeviceFileSystem->driver_hash, driver);
|
hash_insert(sDeviceFileSystem->driver_hash, driver);
|
||||||
|
if (stat.st_dev > 0)
|
||||||
|
add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_STAT, sDriverWatcher);
|
||||||
|
|
||||||
// Even if loading the driver fails - its entry will stay with us
|
// Even if loading the driver fails - its entry will stay with us
|
||||||
// so that we don't have to go through it again
|
// so that we don't have to go through it again
|
||||||
@@ -446,6 +615,51 @@ scan_for_drivers(devfs_vnode *dir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - DriverWatcher
|
||||||
|
|
||||||
|
|
||||||
|
DriverWatcher::DriverWatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DriverWatcher::~DriverWatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DriverWatcher::EventOccured(NotificationService& service,
|
||||||
|
const KMessage* event)
|
||||||
|
{
|
||||||
|
if (event->GetInt32("opcode", -1) != B_STAT_CHANGED
|
||||||
|
|| (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RecursiveLocker locker(&sDeviceFileSystem->lock);
|
||||||
|
|
||||||
|
driver_entry *driver = find_driver(event->GetInt32("device", -1),
|
||||||
|
event->GetInt64("node", 0));
|
||||||
|
if (driver == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (driver->devices_used == 0) {
|
||||||
|
// reload driver
|
||||||
|
dprintf("devfs: reload driver \"%s\"\n", driver->name);
|
||||||
|
//unpublish_driver(driver);
|
||||||
|
// TODO: even though we would need to unpublish the driver, this
|
||||||
|
// doesn't work right now from here (dead lock in the node monitor
|
||||||
|
// notifications)
|
||||||
|
unload_driver(driver);
|
||||||
|
load_driver(driver);
|
||||||
|
} else {
|
||||||
|
// driver is in use right now, only mark it to be updated when possible
|
||||||
|
dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name);
|
||||||
|
driver->binary_updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - devfs private
|
// #pragma mark - devfs private
|
||||||
|
|
||||||
|
|
||||||
@@ -1028,105 +1242,6 @@ publish_device(struct devfs *fs, const char *path, device_node_info *deviceNode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Collects all published devices of a driver, compares them to what the
|
|
||||||
driver would publish now, and then publishes/unpublishes the devices
|
|
||||||
as needed.
|
|
||||||
If the driver does not publish any devices anymore, it is unloaded.
|
|
||||||
*/
|
|
||||||
static status_t
|
|
||||||
republish_driver(driver_entry *driver)
|
|
||||||
{
|
|
||||||
if (driver->image < 0) {
|
|
||||||
// The driver is not yet loaded - go through the normal load procedure
|
|
||||||
return load_driver(driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
RecursiveLocker locker(&sDeviceFileSystem->lock);
|
|
||||||
|
|
||||||
// build the list of currently present devices of this driver
|
|
||||||
// by iterating through all present nodes
|
|
||||||
struct hash_iterator i;
|
|
||||||
hash_open(sDeviceFileSystem->vnode_hash, &i);
|
|
||||||
|
|
||||||
DoublyLinkedList<node_path_entry> currentNodes;
|
|
||||||
while (true) {
|
|
||||||
devfs_vnode *vnode = (devfs_vnode *)hash_next(
|
|
||||||
sDeviceFileSystem->vnode_hash, &i);
|
|
||||||
if (vnode == NULL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (S_ISCHR(vnode->stream.type)
|
|
||||||
&& vnode->stream.u.dev.driver == driver) {
|
|
||||||
node_path_entry *path = new(std::nothrow) node_path_entry;
|
|
||||||
if (!path) {
|
|
||||||
while ((path = currentNodes.RemoveHead()))
|
|
||||||
delete path;
|
|
||||||
hash_close(sDeviceFileSystem->vnode_hash, &i, false);
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_device_name(vnode, path->path, sizeof(path->path));
|
|
||||||
currentNodes.Add(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
vnode = (devfs_vnode *)hash_next(sDeviceFileSystem->vnode_hash, &i);
|
|
||||||
}
|
|
||||||
hash_close(sDeviceFileSystem->vnode_hash, &i, false);
|
|
||||||
|
|
||||||
// now ask the driver for it's currently published devices
|
|
||||||
const char **devicePaths = driver->publish_devices();
|
|
||||||
|
|
||||||
int32 exported = 0;
|
|
||||||
for (; devicePaths != NULL && devicePaths[0]; devicePaths++) {
|
|
||||||
bool present = false;
|
|
||||||
node_path_entry *entry = currentNodes.Head();
|
|
||||||
while (entry) {
|
|
||||||
if (strncmp(entry->path, devicePaths[0], B_PATH_NAME_LENGTH) == 0) {
|
|
||||||
// this device was present before and still is -> no republish
|
|
||||||
currentNodes.Remove(entry);
|
|
||||||
delete entry;
|
|
||||||
exported++;
|
|
||||||
present = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = currentNodes.GetNext(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (present)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// the device was not present before -> publish it now
|
|
||||||
device_hooks *hooks = driver->find_device(devicePaths[0]);
|
|
||||||
if (hooks == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
TRACE(("devfs: publishing new device \"%s\"\n", devicePaths[0]));
|
|
||||||
if (publish_device(sDeviceFileSystem, devicePaths[0], NULL, NULL,
|
|
||||||
driver, hooks, *driver->api_version) == B_OK)
|
|
||||||
exported++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// what's left in currentNodes was present but is not anymore -> unpublish
|
|
||||||
node_path_entry *entry = currentNodes.Head();
|
|
||||||
while (entry) {
|
|
||||||
TRACE(("devfs: unpublishing no more present \"%s\"\n", entry->path));
|
|
||||||
devfs_unpublish_device(entry->path, true);
|
|
||||||
node_path_entry *next = currentNodes.GetNext(entry);
|
|
||||||
currentNodes.Remove(entry);
|
|
||||||
delete entry;
|
|
||||||
entry = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exported == 0) {
|
|
||||||
TRACE(("devfs: driver \"%s\" does not publish any more nodes and is unloaded\n", driver->path));
|
|
||||||
unload_driver(driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Construct complete device name (as used for device_open()).
|
/** Construct complete device name (as used for device_open()).
|
||||||
* This is safe to use only when the device is in use (and therefore
|
* This is safe to use only when the device is in use (and therefore
|
||||||
* cannot be unpublished during the iteration).
|
* cannot be unpublished during the iteration).
|
||||||
@@ -1200,7 +1315,7 @@ devfs_mount(dev_t id, const char *devfs, uint32 flags, const char *args,
|
|||||||
TRACE(("devfs_mount: entry\n"));
|
TRACE(("devfs_mount: entry\n"));
|
||||||
|
|
||||||
if (sDeviceFileSystem) {
|
if (sDeviceFileSystem) {
|
||||||
dprintf("double mount of devfs attempted\n");
|
TRACE(("double mount of devfs attempted\n"));
|
||||||
err = B_ERROR;
|
err = B_ERROR;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@@ -1233,6 +1348,8 @@ devfs_mount(dev_t id, const char *devfs, uint32 flags, const char *args,
|
|||||||
goto err3;
|
goto err3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new(&sDriverWatcher) DriverWatcher;
|
||||||
|
|
||||||
// create a vnode
|
// create a vnode
|
||||||
vnode = devfs_create_vnode(fs, NULL, "");
|
vnode = devfs_create_vnode(fs, NULL, "");
|
||||||
if (vnode == NULL) {
|
if (vnode == NULL) {
|
||||||
@@ -1384,23 +1501,20 @@ static status_t
|
|||||||
devfs_get_vnode(fs_volume _fs, ino_t id, fs_vnode *_vnode, bool reenter)
|
devfs_get_vnode(fs_volume _fs, ino_t id, fs_vnode *_vnode, bool reenter)
|
||||||
{
|
{
|
||||||
struct devfs *fs = (struct devfs *)_fs;
|
struct devfs *fs = (struct devfs *)_fs;
|
||||||
struct devfs_vnode *vnode;
|
|
||||||
|
|
||||||
TRACE(("devfs_get_vnode: asking for vnode id = %Ld, vnode = %p, r %d\n", id, _vnode, reenter));
|
TRACE(("devfs_get_vnode: asking for vnode id = %Ld, vnode = %p, r %d\n", id, _vnode, reenter));
|
||||||
|
|
||||||
if (!reenter)
|
RecursiveLocker _(fs->lock);
|
||||||
recursive_lock_lock(&fs->lock);
|
|
||||||
|
|
||||||
vnode = (devfs_vnode *)hash_lookup(fs->vnode_hash, &id);
|
|
||||||
|
|
||||||
if (!reenter)
|
|
||||||
recursive_lock_unlock(&fs->lock);
|
|
||||||
|
|
||||||
TRACE(("devfs_get_vnode: looked it up at %p\n", *_vnode));
|
|
||||||
|
|
||||||
|
struct devfs_vnode *vnode = (devfs_vnode *)hash_lookup(fs->vnode_hash, &id);
|
||||||
if (vnode == NULL)
|
if (vnode == NULL)
|
||||||
return B_ENTRY_NOT_FOUND;
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
if (S_ISCHR(vnode->stream.type) && vnode->stream.u.dev.driver != NULL)
|
||||||
|
vnode->stream.u.dev.driver->devices_used++;
|
||||||
|
|
||||||
|
TRACE(("devfs_get_vnode: looked it up at %p\n", *_vnode));
|
||||||
|
|
||||||
*_vnode = vnode;
|
*_vnode = vnode;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -1409,11 +1523,15 @@ devfs_get_vnode(fs_volume _fs, ino_t id, fs_vnode *_vnode, bool reenter)
|
|||||||
static status_t
|
static status_t
|
||||||
devfs_put_vnode(fs_volume _fs, fs_vnode _v, bool reenter)
|
devfs_put_vnode(fs_volume _fs, fs_vnode _v, bool reenter)
|
||||||
{
|
{
|
||||||
#ifdef TRACE_DEVFS
|
struct devfs *fs = (struct devfs *)_fs;
|
||||||
struct devfs_vnode *vnode = (struct devfs_vnode *)_v;
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_v;
|
||||||
|
|
||||||
TRACE(("devfs_put_vnode: entry on vnode %p, id = %Ld, reenter %d\n", vnode, vnode->id, reenter));
|
TRACE(("devfs_put_vnode: entry on vnode %p, id = %Ld, reenter %d\n", vnode, vnode->id, reenter));
|
||||||
#endif
|
|
||||||
|
RecursiveLocker _(fs->lock);
|
||||||
|
|
||||||
|
if (S_ISCHR(vnode->stream.type) && vnode->stream.u.dev.driver != NULL)
|
||||||
|
vnode->stream.u.dev.driver->devices_used++;
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user