* 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:
parent
9448bb6e0b
commit
db55a020e6
@ -1,7 +1,7 @@
|
||||
SubDir HAIKU_TOP src system kernel device_manager ;
|
||||
|
||||
UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ;
|
||||
UsePrivateHeaders [ FDirName kernel util ] ;
|
||||
UsePrivateHeaders [ FDirName kernel util ] shared ;
|
||||
|
||||
KernelMergeObject kernel_device_manager.o :
|
||||
attributes.c
|
||||
|
@ -1,14 +1,12 @@
|
||||
/*
|
||||
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Device Manager
|
||||
Probing for consumers.
|
||||
|
||||
Here is all the core logic how consumers are found for one node.
|
||||
*/
|
||||
|
||||
@ -17,14 +15,17 @@
|
||||
|
||||
#include <boot_device.h>
|
||||
#include <elf.h>
|
||||
#include <kmodule.h>
|
||||
#include <fs/KPath.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/kernel_cpp.h>
|
||||
|
||||
#include <image.h>
|
||||
#include <KernelExport.h>
|
||||
#include <NodeMonitor.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
@ -68,19 +69,22 @@ const char *pnp_registration_dirs[2] = {
|
||||
static const char *kModulePaths[] = {
|
||||
COMMON_MODULES_DIR,
|
||||
"/boot/beos/system/add-ons/kernel",//SYSTEM_MODULES_DIR,
|
||||
"/boot", // ToDo: this is for the bootfs boot - to be removed
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
class DirectoryIterator {
|
||||
public:
|
||||
DirectoryIterator(const char *path, const char *subPath = NULL, bool recursive = false);
|
||||
DirectoryIterator(const char **paths, const char *subPath = NULL, bool recursive = false);
|
||||
DirectoryIterator(const char *path, const char *subPath = NULL,
|
||||
bool recursive = false);
|
||||
DirectoryIterator(const char **paths, const char *subPath = NULL,
|
||||
bool recursive = false);
|
||||
~DirectoryIterator();
|
||||
|
||||
void SetTo(const char *path, const char *subPath = NULL, bool recursive = false);
|
||||
void SetTo(const char **paths, const char *subPath = NULL, bool recursive = false);
|
||||
void SetTo(const char *path, const char *subPath = NULL,
|
||||
bool recursive = false);
|
||||
void SetTo(const char **paths, const char *subPath = NULL,
|
||||
bool recursive = false);
|
||||
|
||||
status_t GetNext(KPath &path, struct stat &stat);
|
||||
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),
|
||||
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),
|
||||
fBasePath(NULL)
|
||||
@ -226,7 +246,52 @@ DirectoryIterator::AddPath(const char *basePath, const char *subPath)
|
||||
// #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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
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);
|
||||
if (entry == NULL)
|
||||
@ -355,11 +421,10 @@ remove_device_nodes(struct list *list)
|
||||
}
|
||||
|
||||
|
||||
/** notify a consumer that a device he might handle is added
|
||||
* fileName - file name of consumer
|
||||
* (moved to end of file to avoid inlining)
|
||||
*/
|
||||
|
||||
/*! Notify a consumer that a device he might handle is added
|
||||
fileName - file name of consumer
|
||||
(moved to end of file to avoid inlining)
|
||||
*/
|
||||
static status_t
|
||||
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)));
|
||||
if (strlen(module_name) > suffix_length
|
||||
&& !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
|
||||
res = dm_register_child_device(node, module_name, true);
|
||||
break;
|
||||
@ -401,15 +468,14 @@ err:
|
||||
}
|
||||
|
||||
|
||||
/** 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
|
||||
*/
|
||||
|
||||
/*! 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(device_node_info *node, const char *dir,
|
||||
const char *filename_pattern, int num_parts,
|
||||
@ -454,12 +520,11 @@ err:
|
||||
}
|
||||
|
||||
|
||||
/** 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 B_PATH_NAME_LENGTH + 1 ; destroyed on exit
|
||||
* return: B_NAME_NOT_FOUND, if tell_all is false and no driver returned B_OK
|
||||
*/
|
||||
|
||||
/*! 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 B_PATH_NAME_LENGTH + 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(device_node_info *node, char *directory,
|
||||
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
|
||||
* 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)
|
||||
* return: B_NAME_NOT_FOUND if no consumer could be found
|
||||
*/
|
||||
|
||||
/*! 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
|
||||
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
|
||||
*/
|
||||
static status_t
|
||||
find_normal_child(device_node_info *node, const char *dir,
|
||||
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.
|
||||
* 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>!)
|
||||
* filename_pattern - pattern of file name
|
||||
* *num_parts - number of split positions
|
||||
*/
|
||||
|
||||
/*! Pre-process dynamic child 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>!)
|
||||
filename_pattern - pattern of file name
|
||||
*num_parts - number of split positions
|
||||
*/
|
||||
static status_t
|
||||
preprocess_child_names(const char *pattern, char *buffer,
|
||||
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
|
||||
* has_normal_drivers - in: true - don't search for specific driver
|
||||
* out: true - specific driver was found
|
||||
*/
|
||||
|
||||
/*! 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
|
||||
register_dynamic_child_device(device_node_info *node, const char *bus,
|
||||
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
|
||||
* in that list.
|
||||
* 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.
|
||||
*/
|
||||
/*! Iterates over the given list and tries to load all drivers and modules
|
||||
in that list.
|
||||
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.
|
||||
*/
|
||||
static status_t
|
||||
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
|
||||
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;
|
||||
|
||||
@ -977,8 +1039,9 @@ get_nodes_for_device_type(device_node_info *node, struct list *list, const char
|
||||
if (!matches) {
|
||||
// we also accept any dump busses
|
||||
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)
|
||||
matches = true;
|
||||
}
|
||||
@ -1009,12 +1072,12 @@ get_nodes_for_device_type(device_node_info *node, struct list *list, const char
|
||||
// device manager private API
|
||||
|
||||
|
||||
/** Register the device of a child for the \a node that might accept it.
|
||||
* childName - module name of child/consumer
|
||||
*/
|
||||
|
||||
/*! Register the device of a child for the \a node that might accept it.
|
||||
childName - module name of child/consumer
|
||||
*/
|
||||
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;
|
||||
status_t status;
|
||||
@ -1057,11 +1120,10 @@ err:
|
||||
}
|
||||
|
||||
|
||||
/** find and notify dynamic consumers that device was added
|
||||
* errors returned by consumers aren't reported, only problems
|
||||
* like malformed consumer patterns
|
||||
*/
|
||||
|
||||
/*! Find and notify dynamic consumers that device was added
|
||||
errors returned by consumers aren't reported, only problems
|
||||
like malformed consumer patterns
|
||||
*/
|
||||
status_t
|
||||
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
|
||||
* to dynamic child devices, errors reported by fixed child devices are
|
||||
* not ignored but returned, and regarded critical.
|
||||
*/
|
||||
|
||||
/*! Register all fixed child devices of of the given \a node; in contrast
|
||||
to dynamic child devices, errors reported by fixed child devices are
|
||||
not ignored but returned, and regarded critical.
|
||||
*/
|
||||
status_t
|
||||
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) {
|
||||
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
|
||||
// be found as well - therefore, we must make sure that "audio"
|
||||
// exists on /dev.
|
||||
|
||||
size_t length = strlen("drivers/dev");
|
||||
if (strncmp(type, "drivers/dev", length))
|
||||
continue;
|
||||
@ -1274,7 +1339,8 @@ probe_for_driver_modules(const char *type)
|
||||
dprintf("bus: %s\n", path.Leaf());
|
||||
|
||||
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)
|
||||
continue;
|
||||
|
||||
@ -1310,7 +1376,20 @@ probe_for_device_type(const char *type)
|
||||
TRACE(("probe_for_device_type(type = %s)\n", type));
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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
|
||||
#ifdef TRACE_DEVFS
|
||||
@ -131,6 +133,8 @@ struct driver_entry {
|
||||
time_t last_modified;
|
||||
image_id image;
|
||||
uint32 devices_published;
|
||||
uint32 devices_used;
|
||||
bool binary_updated;
|
||||
|
||||
// driver image information
|
||||
int32 *api_version;
|
||||
@ -140,14 +144,24 @@ struct driver_entry {
|
||||
status_t (*uninit_hardware)(void);
|
||||
};
|
||||
|
||||
|
||||
struct node_path_entry : DoublyLinkedListLinkImpl<node_path_entry> {
|
||||
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,
|
||||
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,
|
||||
device_node_info *deviceNode, pnp_devfs_driver_info *info,
|
||||
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 */
|
||||
static struct devfs *sDeviceFileSystem = NULL;
|
||||
static int32 sDefaultApiVersion = 1;
|
||||
static DriverWatcher sDriverWatcher;
|
||||
|
||||
|
||||
// #pragma mark - driver private
|
||||
@ -211,24 +226,25 @@ load_driver(driver_entry *driver)
|
||||
#error Add checks here for new vs old api version!
|
||||
#endif
|
||||
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);
|
||||
status = B_BAD_VALUE;
|
||||
goto error1;
|
||||
}
|
||||
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;
|
||||
goto error1;
|
||||
}
|
||||
} 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,
|
||||
(void **)&driver->publish_devices) != B_OK
|
||||
|| get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT,
|
||||
(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;
|
||||
goto error1;
|
||||
}
|
||||
@ -238,8 +254,8 @@ load_driver(driver_entry *driver)
|
||||
if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT,
|
||||
(void **)&init_hardware) == B_OK
|
||||
&& (status = init_hardware()) != B_OK) {
|
||||
dprintf("%s: init_hardware() failed: %s\n", driver->name,
|
||||
strerror(status));
|
||||
TRACE(("%s: init_hardware() failed: %s\n", driver->name,
|
||||
strerror(status)));
|
||||
status = ENXIO;
|
||||
goto error1;
|
||||
}
|
||||
@ -247,8 +263,8 @@ load_driver(driver_entry *driver)
|
||||
if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT,
|
||||
(void **)&init_driver) == B_OK
|
||||
&& (status = init_driver()) != B_OK) {
|
||||
dprintf("%s: init_driver() failed: %s\n", driver->name,
|
||||
strerror(status));
|
||||
TRACE(("%s: init_driver() failed: %s\n", driver->name,
|
||||
strerror(status)));
|
||||
status = ENXIO;
|
||||
goto error2;
|
||||
}
|
||||
@ -267,7 +283,7 @@ load_driver(driver_entry *driver)
|
||||
// we keep the driver loaded if it exports at least a single interface
|
||||
devicePaths = driver->publish_devices();
|
||||
if (devicePaths == NULL) {
|
||||
dprintf("%s: publish_devices() returned NULL.\n", driver->name);
|
||||
TRACE(("%s: publish_devices() returned NULL.\n", driver->name));
|
||||
status = ENXIO;
|
||||
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 *
|
||||
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
|
||||
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));
|
||||
if (driver != NULL) {
|
||||
// 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
|
||||
// the driver if necessary
|
||||
if (driver->image < B_OK)
|
||||
@ -386,6 +550,9 @@ add_driver(const char *path, image_id image)
|
||||
driver->node = stat.st_ino;
|
||||
driver->image = image;
|
||||
driver->last_modified = stat.st_mtime;
|
||||
driver->devices_published = 0;
|
||||
driver->devices_used = 0;
|
||||
driver->binary_updated = false;
|
||||
|
||||
driver->api_version = NULL;
|
||||
driver->find_device = NULL;
|
||||
@ -394,6 +561,8 @@ add_driver(const char *path, image_id image)
|
||||
driver->uninit_hardware = NULL;
|
||||
|
||||
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
|
||||
// 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
|
||||
|
||||
|
||||
@ -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()).
|
||||
* This is safe to use only when the device is in use (and therefore
|
||||
* 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"));
|
||||
|
||||
if (sDeviceFileSystem) {
|
||||
dprintf("double mount of devfs attempted\n");
|
||||
TRACE(("double mount of devfs attempted\n"));
|
||||
err = B_ERROR;
|
||||
goto err;
|
||||
}
|
||||
@ -1233,6 +1348,8 @@ devfs_mount(dev_t id, const char *devfs, uint32 flags, const char *args,
|
||||
goto err3;
|
||||
}
|
||||
|
||||
new(&sDriverWatcher) DriverWatcher;
|
||||
|
||||
// create a vnode
|
||||
vnode = devfs_create_vnode(fs, 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)
|
||||
{
|
||||
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));
|
||||
|
||||
if (!reenter)
|
||||
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));
|
||||
RecursiveLocker _(fs->lock);
|
||||
|
||||
struct devfs_vnode *vnode = (devfs_vnode *)hash_lookup(fs->vnode_hash, &id);
|
||||
if (vnode == NULL)
|
||||
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;
|
||||
return B_OK;
|
||||
}
|
||||
@ -1409,11 +1523,15 @@ devfs_get_vnode(fs_volume _fs, ino_t id, fs_vnode *_vnode, bool reenter)
|
||||
static status_t
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user