Some work in progress cut back to support R5 style device exploration.
Also includes the loader of R5 styled drivers (taken from dev.c). git-svn-id: file:///srv/svn/repos/haiku/trunk/current@10833 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
321231a056
commit
a9435d9740
|
@ -16,6 +16,13 @@
|
||||||
#include "device_manager_private.h"
|
#include "device_manager_private.h"
|
||||||
|
|
||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
|
#include <image.h>
|
||||||
|
#include <elf.h>
|
||||||
|
#include <kmodule.h>
|
||||||
|
#include <fs/KPath.h>
|
||||||
|
#include <util/Stack.h>
|
||||||
|
#include <util/kernel_cpp.h>
|
||||||
|
#include <devfs.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
@ -31,12 +38,14 @@
|
||||||
# define TRACE(x) ;
|
# define TRACE(x) ;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ToDo: for now - it won't be necessary to do this later anymore
|
|
||||||
#define pnp_boot_safe_realpath realpath
|
struct path_entry {
|
||||||
#define pnp_boot_safe_lstat lstat
|
struct list_link link;
|
||||||
#define pnp_boot_safe_opendir opendir
|
char *path;
|
||||||
#define pnp_boot_safe_readdir readdir
|
dev_t device;
|
||||||
#define pnp_boot_safe_closedir closedir
|
ino_t node;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// list of driver registration directories
|
// list of driver registration directories
|
||||||
const char *pnp_registration_dirs[2] = {
|
const char *pnp_registration_dirs[2] = {
|
||||||
|
@ -46,19 +55,164 @@ const char *pnp_registration_dirs[2] = {
|
||||||
|
|
||||||
|
|
||||||
// list of module directories
|
// list of module directories
|
||||||
static const char *modules_dirs[2] = {
|
static const char *kModulePaths[] = {
|
||||||
COMMON_MODULES_DIR,
|
COMMON_MODULES_DIR,
|
||||||
SYSTEM_MODULES_DIR
|
"/boot/beos/system/add-ons/kernel",//SYSTEM_MODULES_DIR,
|
||||||
|
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();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
void Unset();
|
||||||
|
void AddPath(const char *path, const char *subPath = NULL);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Stack<KPath *> fPaths;
|
||||||
|
bool fRecursive;
|
||||||
|
DIR *fDirectory;
|
||||||
|
KPath *fBasePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryIterator::DirectoryIterator(const char *path, const char *subPath, bool recursive)
|
||||||
|
:
|
||||||
|
fDirectory(NULL),
|
||||||
|
fBasePath(NULL)
|
||||||
|
{
|
||||||
|
SetTo(path, subPath, recursive);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryIterator::DirectoryIterator(const char **paths, const char *subPath, bool recursive)
|
||||||
|
:
|
||||||
|
fDirectory(NULL),
|
||||||
|
fBasePath(NULL)
|
||||||
|
{
|
||||||
|
SetTo(paths, subPath, recursive);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryIterator::~DirectoryIterator()
|
||||||
|
{
|
||||||
|
Unset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DirectoryIterator::SetTo(const char *path, const char *subPath, bool recursive)
|
||||||
|
{
|
||||||
|
Unset();
|
||||||
|
fRecursive = recursive;
|
||||||
|
|
||||||
|
AddPath(path, subPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DirectoryIterator::SetTo(const char **paths, const char *subPath, bool recursive)
|
||||||
|
{
|
||||||
|
Unset();
|
||||||
|
fRecursive = recursive;
|
||||||
|
|
||||||
|
for (int32 i = 0; paths[i] != NULL; i++) {
|
||||||
|
AddPath(paths[i], subPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
DirectoryIterator::GetNext(KPath &path, struct stat &stat)
|
||||||
|
{
|
||||||
|
next_directory:
|
||||||
|
while (fDirectory == NULL) {
|
||||||
|
delete fBasePath;
|
||||||
|
fBasePath = NULL;
|
||||||
|
|
||||||
|
if (!fPaths.Pop(&fBasePath))
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
fDirectory = opendir(fBasePath->Path());
|
||||||
|
}
|
||||||
|
|
||||||
|
next_entry:
|
||||||
|
struct dirent *dirent = readdir(fDirectory);
|
||||||
|
if (dirent == NULL) {
|
||||||
|
// get over to next directory on the stack
|
||||||
|
closedir(fDirectory);
|
||||||
|
fDirectory = NULL;
|
||||||
|
|
||||||
|
goto next_directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(dirent->d_name, "..") || !strcmp(dirent->d_name, "."))
|
||||||
|
goto next_entry;
|
||||||
|
|
||||||
|
path.SetTo(fBasePath->Path());
|
||||||
|
path.Append(dirent->d_name);
|
||||||
|
|
||||||
|
if (::stat(path.Path(), &stat) != 0)
|
||||||
|
goto next_entry;
|
||||||
|
|
||||||
|
if (S_ISDIR(stat.st_mode) && fRecursive) {
|
||||||
|
KPath *nextPath = new KPath(path);
|
||||||
|
if (fPaths.Push(nextPath) != B_OK)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
goto next_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DirectoryIterator::Unset()
|
||||||
|
{
|
||||||
|
if (fDirectory != NULL) {
|
||||||
|
closedir(fDirectory);
|
||||||
|
fDirectory = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete fBasePath;
|
||||||
|
fBasePath = NULL;
|
||||||
|
|
||||||
|
KPath *path;
|
||||||
|
while (fPaths.Pop(&path))
|
||||||
|
delete path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DirectoryIterator::AddPath(const char *basePath, const char *subPath)
|
||||||
|
{
|
||||||
|
KPath *path = new KPath(basePath);
|
||||||
|
if (subPath != NULL)
|
||||||
|
path->Append(subPath);
|
||||||
|
|
||||||
|
fPaths.Push(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
/** notify a consumer that a device he might handle is added
|
/** notify a consumer that a device he might handle is added
|
||||||
* consumer_name - 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 *consumer_name)
|
notify_probe_by_file(device_node_info *node, const char *fileName)
|
||||||
{
|
{
|
||||||
char *type;
|
char *type;
|
||||||
char *resolved_path;
|
char *resolved_path;
|
||||||
|
@ -67,14 +221,14 @@ notify_probe_by_file(device_node_info *node, const char *consumer_name)
|
||||||
bool valid_module_name;
|
bool valid_module_name;
|
||||||
status_t res;
|
status_t res;
|
||||||
|
|
||||||
TRACE(("notify_probe_by_file(%s)\n", consumer_name));
|
TRACE(("notify_probe_by_file(%s)\n", fileName));
|
||||||
|
|
||||||
res = pnp_get_attr_string(node, PNP_DRIVER_TYPE, &type, false);
|
res = pnp_get_attr_string(node, PNP_DRIVER_TYPE, &type, false);
|
||||||
if (res != B_OK)
|
if (res != B_OK)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
// resolve link to actual driver file
|
// resolve link to actual driver file
|
||||||
resolved_path = (char *)malloc(PATH_MAX + 1);
|
resolved_path = (char *)malloc(B_PATH_NAME_LENGTH + 1);
|
||||||
if (resolved_path == NULL) {
|
if (resolved_path == NULL) {
|
||||||
res = B_NO_MEMORY;
|
res = B_NO_MEMORY;
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -85,7 +239,7 @@ notify_probe_by_file(device_node_info *node, const char *consumer_name)
|
||||||
module_name = NULL;
|
module_name = NULL;
|
||||||
if (module_name == NULL) {
|
if (module_name == NULL) {
|
||||||
// broken link or something
|
// broken link or something
|
||||||
dprintf("Cannot resolve driver file name: %s\n", consumer_name);
|
dprintf("Cannot resolve driver file name: %s\n", fileName);
|
||||||
res = errno;
|
res = errno;
|
||||||
goto err2;
|
goto err2;
|
||||||
}
|
}
|
||||||
|
@ -95,10 +249,10 @@ notify_probe_by_file(device_node_info *node, const char *consumer_name)
|
||||||
valid_module_name = false;
|
valid_module_name = false;
|
||||||
|
|
||||||
for (i = 0; i < (disable_useraddons ? 1 : 2); ++i) {
|
for (i = 0; i < (disable_useraddons ? 1 : 2); ++i) {
|
||||||
int len = strlen(modules_dirs[i]);
|
int len = strlen(kModulePaths[i]);
|
||||||
|
|
||||||
if (!strncmp(consumer_name, modules_dirs[i], len)
|
if (!strncmp(fileName, kModulePaths[i], len)
|
||||||
&& !strncmp(module_name, modules_dirs[i], len)) {
|
&& !strncmp(module_name, kModulePaths[i], len)) {
|
||||||
valid_module_name = true;
|
valid_module_name = true;
|
||||||
module_name = module_name + len;
|
module_name = module_name + len;
|
||||||
break;
|
break;
|
||||||
|
@ -107,14 +261,14 @@ notify_probe_by_file(device_node_info *node, const char *consumer_name)
|
||||||
|
|
||||||
if (!valid_module_name) {
|
if (!valid_module_name) {
|
||||||
TRACE(("Module file %s of consumer %s is in wrong path\n",
|
TRACE(("Module file %s of consumer %s is in wrong path\n",
|
||||||
consumer_name, module_name));
|
fileName, module_name));
|
||||||
res = B_NAME_NOT_FOUND;
|
res = B_NAME_NOT_FOUND;
|
||||||
goto err2;
|
goto err2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// append driver type to get specific module name
|
// append driver type to get specific module name
|
||||||
strlcat(module_name, "/", PATH_MAX);
|
strlcat(module_name, "/", B_PATH_NAME_LENGTH);
|
||||||
strlcat(module_name, type, PATH_MAX);
|
strlcat(module_name, type, B_PATH_NAME_LENGTH);
|
||||||
|
|
||||||
res = pnp_notify_probe_by_module(node, module_name);
|
res = pnp_notify_probe_by_module(node, module_name);
|
||||||
|
|
||||||
|
@ -148,8 +302,8 @@ compose_driver_names(device_node_info *node, const char *dir,
|
||||||
if (term_array == NULL)
|
if (term_array == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
strlcpy(path, dir, PATH_MAX);
|
strlcpy(path, dir, B_PATH_NAME_LENGTH);
|
||||||
strlcat(path, "/", PATH_MAX);
|
strlcat(path, "/", B_PATH_NAME_LENGTH);
|
||||||
|
|
||||||
TRACE(("compose_drive_names(%s)\n", path));
|
TRACE(("compose_drive_names(%s)\n", path));
|
||||||
|
|
||||||
|
@ -181,7 +335,7 @@ 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 PATH_MAX + 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -199,37 +353,37 @@ try_drivers(device_node_info *node, char *directory,
|
||||||
// first try user drivers, then system drivers
|
// first try user drivers, then system drivers
|
||||||
for (i = 0; i < (disable_useraddons ? 1 : 2); ++i) {
|
for (i = 0; i < (disable_useraddons ? 1 : 2); ++i) {
|
||||||
strcpy(buffer, pnp_registration_dirs[i]);
|
strcpy(buffer, pnp_registration_dirs[i]);
|
||||||
strlcat(buffer, directory, PATH_MAX);
|
strlcat(buffer, directory, B_PATH_NAME_LENGTH);
|
||||||
|
|
||||||
TRACE(("cur_dir: %s\n", buffer));
|
TRACE(("cur_dir: %s\n", buffer));
|
||||||
|
|
||||||
dir_len = strlen(buffer);
|
dir_len = strlen(buffer);
|
||||||
|
|
||||||
dir = pnp_boot_safe_opendir(buffer);
|
dir = opendir(buffer);
|
||||||
if (dir == NULL) {
|
if (dir == NULL) {
|
||||||
//SHOW_ERROR(3, "Directory %s doesn't exists", buffer);
|
//SHOW_ERROR(3, "Directory %s doesn't exists", buffer);
|
||||||
// directory doesn't exist
|
// directory doesn't exist
|
||||||
return tell_all ? B_OK : B_NAME_NOT_FOUND;
|
return tell_all ? B_OK : B_NAME_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((entry = pnp_boot_safe_readdir(dir)) != NULL) {
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
buffer[dir_len] = 0;
|
buffer[dir_len] = 0;
|
||||||
strlcat(buffer, "/", PATH_MAX);
|
strlcat(buffer, "/", B_PATH_NAME_LENGTH);
|
||||||
strlcat(buffer, entry->d_name, PATH_MAX);
|
strlcat(buffer, entry->d_name, B_PATH_NAME_LENGTH);
|
||||||
|
|
||||||
// skip default directory entries
|
// skip default directory entries
|
||||||
if (!strcmp( entry->d_name, ".")
|
if (!strcmp( entry->d_name, ".")
|
||||||
|| !strcmp( entry->d_name, ".."))
|
|| !strcmp( entry->d_name, ".."))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (notify_probe_by_file( node, buffer) == B_OK && !tell_all) {
|
if (notify_probe_by_file(node, buffer) == B_OK && !tell_all) {
|
||||||
// tell_all is false, return on first hit
|
// tell_all is false, return on first hit
|
||||||
pnp_boot_safe_closedir(dir);
|
closedir(dir);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pnp_boot_safe_closedir(dir);
|
closedir(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tell_all ? B_OK : B_NAME_NOT_FOUND;
|
return tell_all ? B_OK : B_NAME_NOT_FOUND;
|
||||||
|
@ -238,7 +392,7 @@ try_drivers(device_node_info *node, char *directory,
|
||||||
|
|
||||||
/** find normal consumer of node that are stored under <dir>; first, we
|
/** 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
|
* 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)
|
* 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -271,11 +425,11 @@ find_normal_consumer(device_node_info *node, const char *dir,
|
||||||
struct stat dummy;
|
struct stat dummy;
|
||||||
|
|
||||||
strcpy(buffer, pnp_registration_dirs[j]);
|
strcpy(buffer, pnp_registration_dirs[j]);
|
||||||
strlcat(buffer, path, PATH_MAX);
|
strlcat(buffer, path, B_PATH_NAME_LENGTH);
|
||||||
|
|
||||||
// do a stat to avoid error message if Specific Driver
|
// do a stat to avoid error message if Specific Driver
|
||||||
// isn't provided
|
// isn't provided
|
||||||
if (pnp_boot_safe_lstat(buffer, &dummy) == 0) {
|
if (lstat(buffer, &dummy) == 0) {
|
||||||
res = notify_probe_by_file(node, buffer);
|
res = notify_probe_by_file(node, buffer);
|
||||||
if (res == B_OK)
|
if (res == B_OK)
|
||||||
// got him!
|
// got him!
|
||||||
|
@ -297,8 +451,8 @@ find_normal_consumer(device_node_info *node, const char *dir,
|
||||||
}
|
}
|
||||||
|
|
||||||
// no specific consumer - go through generic driver
|
// no specific consumer - go through generic driver
|
||||||
strlcpy(path, dir, PATH_MAX);
|
strlcpy(path, dir, B_PATH_NAME_LENGTH);
|
||||||
strlcat(path, GENERIC_SUBDIR, PATH_MAX);
|
strlcat(path, GENERIC_SUBDIR, B_PATH_NAME_LENGTH);
|
||||||
|
|
||||||
if (try_drivers(node, path, false, buffer) != B_OK) {
|
if (try_drivers(node, path, false, buffer) != B_OK) {
|
||||||
*found_normal_driver = false;
|
*found_normal_driver = false;
|
||||||
|
@ -316,31 +470,29 @@ find_normal_consumer(device_node_info *node, const char *dir,
|
||||||
* 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>!)
|
||||||
* dir - directory
|
|
||||||
* 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_consumer_names(const char *pattern, char *buffer,
|
preprocess_consumer_names(const char *pattern, char *buffer,
|
||||||
char **dir, char **filename_pattern, int *const num_parts)
|
char **filename_pattern, int *const num_parts)
|
||||||
{
|
{
|
||||||
char *last_slash, *str, *dest;
|
char *str, *dest;
|
||||||
bool parts_began;
|
bool parts_began;
|
||||||
|
|
||||||
// make a copy of pattern as we will strip escapes from directory part
|
// make a copy of pattern as we will strip escapes from directory part
|
||||||
strlcpy(buffer, pattern, PATH_MAX);
|
strlcpy(buffer, pattern, B_PATH_NAME_LENGTH);
|
||||||
|
|
||||||
// find directory part and count splitpoints
|
// find directory part and count splitpoints
|
||||||
parts_began = false;
|
parts_began = false;
|
||||||
last_slash = NULL;
|
|
||||||
*num_parts = 1;
|
*num_parts = 1;
|
||||||
|
|
||||||
for (str = buffer; *str; ++str) {
|
for (str = buffer; *str; ++str) {
|
||||||
switch (*str) {
|
switch (*str) {
|
||||||
case '^':
|
case '\\':
|
||||||
// honour escaped characters, taking care of trailing escape
|
// honour escaped characters, taking care of trailing escape
|
||||||
if (*(str + 1))
|
if (str[1])
|
||||||
++str;
|
++str;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -354,8 +506,8 @@ preprocess_consumer_names(const char *pattern, char *buffer,
|
||||||
|
|
||||||
// find end of attribute name, taking care of escape sequences
|
// find end of attribute name, taking care of escape sequences
|
||||||
for (; *str != 0; ++str) {
|
for (; *str != 0; ++str) {
|
||||||
if (*str == '^') {
|
if (*str == '\\') {
|
||||||
if (*(str + 1) != 0)
|
if (str[1] != 0)
|
||||||
++str;
|
++str;
|
||||||
} else if (*str == '%')
|
} else if (*str == '%')
|
||||||
break;
|
break;
|
||||||
|
@ -366,37 +518,23 @@ preprocess_consumer_names(const char *pattern, char *buffer,
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '/':
|
|
||||||
// directory finishes at last "/" before first "|"
|
|
||||||
if (!parts_began)
|
|
||||||
last_slash = str;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last_slash == 0) {
|
*filename_pattern = buffer;
|
||||||
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
|
// remove escape sequences from directory
|
||||||
for (str = buffer, dest = buffer; *str; ++str) {
|
for (str = buffer, dest = buffer; *str; ++str) {
|
||||||
if (*str == '^' && (str + 1) != 0)
|
if (str[0] == '\\' && str[1] != 0)
|
||||||
++str;
|
++str;
|
||||||
|
|
||||||
*dest++ = *str;
|
*dest++ = *str;
|
||||||
}
|
}
|
||||||
|
|
||||||
*dest = 0;
|
*dest = '\0';
|
||||||
|
|
||||||
TRACE(("dir=%s, filename_pattern=%s, num_parts=%d\n",
|
TRACE(("filename_pattern = %s, num_parts = %d\n",
|
||||||
*dir, *filename_pattern, *num_parts));
|
*filename_pattern, *num_parts));
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
@ -408,32 +546,35 @@ preprocess_consumer_names(const char *pattern, char *buffer,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
notify_dynamic_consumer(device_node_info *node, const char *pattern,
|
notify_dynamic_consumer(device_node_info *node, const char *bus,
|
||||||
bool *has_normal_driver)
|
const char *pattern, bool *has_normal_driver)
|
||||||
{
|
{
|
||||||
status_t res;
|
status_t status;
|
||||||
char *buffers;
|
char *buffers;
|
||||||
char *dir, *filename_pattern;
|
char *filename_pattern;
|
||||||
int num_parts;
|
int num_parts;
|
||||||
|
|
||||||
TRACE(("notify_dynamic_consumer(pattern=%s, has_normal_driver=%d)\n",
|
TRACE(("notify_dynamic_consumer(bus = %s, pattern = %s, has_normal_driver = %d)\n",
|
||||||
pattern, *has_normal_driver));
|
bus, pattern, *has_normal_driver));
|
||||||
|
|
||||||
|
if (pattern == NULL)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
// we need three buffers - allocate them at once for simplicity
|
// we need three buffers - allocate them at once for simplicity
|
||||||
buffers = (char *)malloc(3 * (PATH_MAX + 1));
|
buffers = (char *)malloc(3 * (B_PATH_NAME_LENGTH + 1));
|
||||||
if (buffers == NULL)
|
if (buffers == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
res = preprocess_consumer_names(pattern, buffers + 2 * (PATH_MAX + 1),
|
status = preprocess_consumer_names(pattern, buffers + 2 * (B_PATH_NAME_LENGTH + 1),
|
||||||
&dir, &filename_pattern, &num_parts);
|
&filename_pattern, &num_parts);
|
||||||
if (res < B_OK)
|
if (status < B_OK)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (!*has_normal_driver) {
|
if (!*has_normal_driver) {
|
||||||
// find specific/generic consumer
|
// find specific/generic consumer
|
||||||
res = find_normal_consumer(node, dir, filename_pattern, num_parts,
|
status = find_normal_consumer(node, bus, filename_pattern, num_parts,
|
||||||
buffers, buffers + PATH_MAX + 1, has_normal_driver);
|
buffers, buffers + B_PATH_NAME_LENGTH + 1, has_normal_driver);
|
||||||
if (res != B_OK && res != B_NAME_NOT_FOUND)
|
if (status != B_OK && status != B_NAME_NOT_FOUND)
|
||||||
// only abort if there was a "real" problem;
|
// only abort if there was a "real" problem;
|
||||||
// if there is no specific/generic consumer, we don't bother
|
// if there is no specific/generic consumer, we don't bother
|
||||||
// (having no driver is not funny but happens)
|
// (having no driver is not funny but happens)
|
||||||
|
@ -441,11 +582,11 @@ notify_dynamic_consumer(device_node_info *node, const char *pattern,
|
||||||
}
|
}
|
||||||
|
|
||||||
// tell universal drivers
|
// tell universal drivers
|
||||||
strlcpy(buffers, dir, PATH_MAX);
|
strlcpy(buffers, bus, B_PATH_NAME_LENGTH);
|
||||||
strlcat(buffers, UNIVERSAL_SUBDIR, PATH_MAX);
|
strlcat(buffers, UNIVERSAL_SUBDIR, B_PATH_NAME_LENGTH);
|
||||||
|
|
||||||
res = try_drivers(node, buffers, true, buffers + PATH_MAX + 1);
|
status = try_drivers(node, buffers, true, buffers + B_PATH_NAME_LENGTH + 1);
|
||||||
if (res != B_OK && res != B_NAME_NOT_FOUND)
|
if (status != B_OK && status != B_NAME_NOT_FOUND)
|
||||||
// again, only abort on real problems
|
// again, only abort on real problems
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
@ -456,7 +597,176 @@ notify_dynamic_consumer(device_node_info *node, const char *pattern,
|
||||||
|
|
||||||
err:
|
err:
|
||||||
free(buffers);
|
free(buffers);
|
||||||
return res;
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static path_entry *
|
||||||
|
find_node_ref_in_list(struct list *list, dev_t device, ino_t node)
|
||||||
|
{
|
||||||
|
path_entry *entry = NULL;
|
||||||
|
|
||||||
|
while ((entry = (path_entry *)list_get_next_item(list, entry)) != NULL) {
|
||||||
|
if (entry->device == device
|
||||||
|
&& entry->node == node)
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static image_id
|
||||||
|
load_driver(const char *path)
|
||||||
|
{
|
||||||
|
status_t (*init_hardware)(void);
|
||||||
|
status_t (*init_driver)(void);
|
||||||
|
const char **devicePaths;
|
||||||
|
int32 exported = 0;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
// load the module
|
||||||
|
image_id image = load_kernel_add_on(path);
|
||||||
|
if (image < 0)
|
||||||
|
return image;
|
||||||
|
|
||||||
|
// for prettier debug output
|
||||||
|
const char *name = strrchr(path, '/');
|
||||||
|
if (name == NULL)
|
||||||
|
name = path;
|
||||||
|
else
|
||||||
|
name++;
|
||||||
|
|
||||||
|
// For a valid device driver the following exports are required
|
||||||
|
|
||||||
|
device_hooks *(*find_device)(const char *);
|
||||||
|
const char **(*publish_devices)(void);
|
||||||
|
uint32 *api_version;
|
||||||
|
if (get_image_symbol(image, "publish_devices", B_SYMBOL_TYPE_TEXT, (void **)&publish_devices) != B_OK
|
||||||
|
|| get_image_symbol(image, "api_version", B_SYMBOL_TYPE_DATA, (void **)&api_version) != B_OK
|
||||||
|
|| get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT, (void **)&find_device) != B_OK) {
|
||||||
|
dprintf("%s: mandatory driver symbol(s) missing!\n", name);
|
||||||
|
status = B_BAD_VALUE;
|
||||||
|
goto error1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test for init_hardware() and call it
|
||||||
|
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", name, strerror(status));
|
||||||
|
status = ENXIO;
|
||||||
|
goto error1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OK, so we now have what appears to be a valid module that has
|
||||||
|
* completed init_hardware and thus thinks it should be used.
|
||||||
|
* ToDo:
|
||||||
|
* - this is bogus!
|
||||||
|
* - the driver init routines should be called by devfs and
|
||||||
|
* only when the driver is first needed. However, that level
|
||||||
|
* level of support is not yet in devfs, so we have a hack
|
||||||
|
* here that calls the init_driver function at this point.
|
||||||
|
* As a result we will check to see if we actually manage to
|
||||||
|
* publish the device, and if we do we will keep the module
|
||||||
|
* loaded.
|
||||||
|
* - remove this when devfs is fixed!
|
||||||
|
*/
|
||||||
|
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", name, strerror(status));
|
||||||
|
status = ENXIO;
|
||||||
|
goto error2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we keep the driver loaded if it exports at least a single interface
|
||||||
|
devicePaths = publish_devices();
|
||||||
|
if (devicePaths == NULL) {
|
||||||
|
dprintf("%s: publish_devices() returned NULL.\n", name);
|
||||||
|
status = ENXIO;
|
||||||
|
goto error3;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; devicePaths[0]; devicePaths++) {
|
||||||
|
device_hooks *hooks = find_device(devicePaths[0]);
|
||||||
|
|
||||||
|
if (hooks && devfs_publish_device(devicePaths[0], NULL, hooks) == 0)
|
||||||
|
exported++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're all done, driver will be kept loaded (for now, see above comment)
|
||||||
|
if (exported > 0)
|
||||||
|
return image;
|
||||||
|
|
||||||
|
status = B_ERROR; // whatever...
|
||||||
|
|
||||||
|
|
||||||
|
error3:
|
||||||
|
{
|
||||||
|
status_t (*uninit_driver)(void);
|
||||||
|
if (get_image_symbol(image, "uninit_driver", B_SYMBOL_TYPE_TEXT,
|
||||||
|
(void **)&uninit_driver) == B_OK)
|
||||||
|
uninit_driver();
|
||||||
|
}
|
||||||
|
|
||||||
|
error2:
|
||||||
|
{
|
||||||
|
status_t (*uninit_hardware)(void);
|
||||||
|
if (get_image_symbol(image, "uninit_hardware", B_SYMBOL_TYPE_TEXT,
|
||||||
|
(void **)&uninit_hardware) == B_OK)
|
||||||
|
uninit_hardware();
|
||||||
|
}
|
||||||
|
error1:
|
||||||
|
/* If we've gotten here then the driver will be unloaded and an
|
||||||
|
* error code returned.
|
||||||
|
*/
|
||||||
|
unload_kernel_add_on(image);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** This is no longer part of the public kernel API, so we just export the symbol */
|
||||||
|
|
||||||
|
status_t load_driver_symbols(const char *driverName);
|
||||||
|
status_t
|
||||||
|
load_driver_symbols(const char *driverName)
|
||||||
|
{
|
||||||
|
// This will be globally done for the whole kernel via the settings file.
|
||||||
|
// We don't have to do anything here.
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
try_drivers(struct list &list)
|
||||||
|
{
|
||||||
|
path_entry *entry;
|
||||||
|
while ((entry = (path_entry *)list_remove_head_item(&list)) != NULL) {
|
||||||
|
image_id image = load_kernel_add_on(entry->path);
|
||||||
|
if (image >= 0) {
|
||||||
|
// check if it's a driver module
|
||||||
|
module_info **modules;
|
||||||
|
if (load_module(entry->path, &modules) == B_OK) {
|
||||||
|
// we have a module
|
||||||
|
dprintf("loaded module %s\n", entry->path);
|
||||||
|
} else {
|
||||||
|
// it can still be a standard old-style driver
|
||||||
|
if (load_driver(entry->path) == B_OK) {
|
||||||
|
// we have a driver
|
||||||
|
dprintf("loaded driver %s\n", entry->path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unload_kernel_add_on(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(entry->path);
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -472,59 +782,70 @@ err:
|
||||||
status_t
|
status_t
|
||||||
pnp_notify_dynamic_consumers(device_node_info *node)
|
pnp_notify_dynamic_consumers(device_node_info *node)
|
||||||
{
|
{
|
||||||
int i;
|
#if 0
|
||||||
char *buffer;
|
char *buffer;
|
||||||
status_t res;
|
char *bus;
|
||||||
bool has_normal_driver = false;
|
status_t status = B_OK;
|
||||||
|
int32 i, found = 0;
|
||||||
|
|
||||||
TRACE(("pnp_notify_dynamic_consumers()\n"));
|
TRACE(("pnp_notify_dynamic_consumers(node = %p)\n", node));
|
||||||
|
|
||||||
buffer = (char *)malloc(PATH_MAX + 1);
|
if (pnp_get_attr_string(node, PNP_DRIVER_CONSUMER_BUS, &bus, false) != B_OK)
|
||||||
if (buffer == NULL)
|
return B_OK;
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
|
||||||
//pnp_load_boot_links();
|
TRACE((" Search bus: \"%s\"\n", bus));
|
||||||
|
|
||||||
|
buffer = (char *)malloc(B_PATH_NAME_LENGTH + 1);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
status = B_NO_MEMORY;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
// first, append nothing, then "/0", "/1" etc.
|
// first, append nothing, then "/0", "/1" etc.
|
||||||
for (i = -1; ; ++i) {
|
for (i = -1; ; ++i) {
|
||||||
|
bool noSpecificDriver = false;
|
||||||
char *consumer;
|
char *consumer;
|
||||||
|
|
||||||
strcpy(buffer, PNP_DRIVER_DYNAMIC_CONSUMER);
|
strcpy(buffer, PNP_DRIVER_CONSUMER_MAPPING);
|
||||||
if (i >= 0)
|
if (i >= 0)
|
||||||
sprintf(buffer + strlen( buffer ), "/%d", i);
|
sprintf(buffer + strlen( buffer ), "/%ld", i);
|
||||||
|
|
||||||
// if no more dynamic consumers, cancel loop silently
|
// if no more dynamic consumers, cancel loop silently
|
||||||
if (pnp_get_attr_string(node, buffer, &consumer, false) != B_OK) {
|
if (pnp_get_attr_string(node, buffer, &consumer, false) != B_OK) {
|
||||||
// starting with .../0 is OK, so ignore error if i=-1
|
// starting with .../0 is OK, so ignore error if i = -1
|
||||||
if (i == -1)
|
if (i == -1)
|
||||||
continue;
|
continue;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE(("Consumer pattern %d: %s\n", i, consumer));
|
TRACE((" Consumer pattern %ld: %s\n", i, consumer));
|
||||||
|
status = notify_dynamic_consumer(node, bus, consumer, &noSpecificDriver);
|
||||||
res = notify_dynamic_consumer(node, consumer, &has_normal_driver);
|
|
||||||
|
|
||||||
free(consumer);
|
free(consumer);
|
||||||
|
found++;
|
||||||
|
|
||||||
if (res != B_OK) {
|
if (status != B_OK) {
|
||||||
// this is only reached if a serious error occured,
|
// this is only reached if a serious error occured,
|
||||||
// see notify_dynamic_consumer()
|
// see notify_dynamic_consumer()
|
||||||
goto err;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (found == 0) {
|
||||||
|
// no requirement for a special mapping, so we're just scanning the bus directory
|
||||||
|
bool noSpecificDriver = false;
|
||||||
|
status = notify_dynamic_consumer(node, bus, NULL, &noSpecificDriver);
|
||||||
|
}
|
||||||
|
|
||||||
|
// supposed to go through
|
||||||
free(buffer);
|
free(buffer);
|
||||||
//pnp_unload_boot_links();
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
err:
|
err:
|
||||||
free(buffer);
|
free(bus);
|
||||||
//pnp_unload_boot_links();
|
return status;
|
||||||
|
#else
|
||||||
return res;
|
return B_OK;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -539,9 +860,9 @@ pnp_notify_fixed_consumers(device_node_info *node)
|
||||||
int i;
|
int i;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
|
|
||||||
TRACE(("pnp_notify_fixed_consumers()\n"));
|
TRACE(("pnp_notify_fixed_consumers(node = %p)\n", node));
|
||||||
|
|
||||||
buffer = (char *)malloc(PATH_MAX + 1);
|
buffer = (char *)malloc(B_PATH_NAME_LENGTH + 1);
|
||||||
if (buffer == NULL)
|
if (buffer == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
@ -554,7 +875,7 @@ pnp_notify_fixed_consumers(device_node_info *node)
|
||||||
sprintf(buffer + strlen(buffer), "/%d", i);
|
sprintf(buffer + strlen(buffer), "/%d", i);
|
||||||
|
|
||||||
// if no more fixed consumers, cancel loop silently
|
// if no more fixed consumers, cancel loop silently
|
||||||
if (pnp_get_attr_string( node, buffer, &consumer, false) != B_OK)
|
if (pnp_get_attr_string(node, buffer, &consumer, false) != B_OK)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
TRACE(("Consumer %d: %s\n", i, consumer));
|
TRACE(("Consumer %d: %s\n", i, consumer));
|
||||||
|
@ -566,7 +887,6 @@ pnp_notify_fixed_consumers(device_node_info *node)
|
||||||
// as they are obviously crucial (else they wouldn't be fixed)
|
// as they are obviously crucial (else they wouldn't be fixed)
|
||||||
free(consumer);
|
free(consumer);
|
||||||
free(buffer);
|
free(buffer);
|
||||||
|
|
||||||
return B_NAME_NOT_FOUND;
|
return B_NAME_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,3 +897,78 @@ pnp_notify_fixed_consumers(device_node_info *node)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
probe_for_device_type(const char *type)
|
||||||
|
{
|
||||||
|
// search a node with an open connection of the specified type
|
||||||
|
// or notify bus managers to get one
|
||||||
|
|
||||||
|
status_t status = B_OK;
|
||||||
|
|
||||||
|
// build list of potential drivers for that type
|
||||||
|
|
||||||
|
struct list drivers;
|
||||||
|
list_init(&drivers);
|
||||||
|
|
||||||
|
char devType[64];
|
||||||
|
snprintf(devType, sizeof(devType), "drivers/dev%s%s", type[0] ? "/" : "", type);
|
||||||
|
|
||||||
|
DirectoryIterator iterator(kModulePaths, devType, false);
|
||||||
|
struct stat stat;
|
||||||
|
KPath path;
|
||||||
|
|
||||||
|
while (iterator.GetNext(path, stat) == B_OK) {
|
||||||
|
path_entry *entry = (path_entry *)malloc(sizeof(path_entry));
|
||||||
|
if (entry == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
entry->path = strdup(path.Path());
|
||||||
|
if (entry->path == NULL) {
|
||||||
|
free(entry);
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->device = stat.st_dev;
|
||||||
|
entry->node = stat.st_ino;
|
||||||
|
|
||||||
|
dprintf("found potential driver: %s\n", path.Path());
|
||||||
|
list_add_item(&drivers, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list_is_empty(&drivers))
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
// Iterate through bus managers to shrink the list (let them publish
|
||||||
|
// their own devices)
|
||||||
|
|
||||||
|
iterator.SetTo(kModulePaths, "drivers/bus", false);
|
||||||
|
|
||||||
|
while (iterator.GetNext(path, stat) == B_OK) {
|
||||||
|
DirectoryIterator busIterator(path.Path(), NULL, true);
|
||||||
|
|
||||||
|
struct list driversForBus;
|
||||||
|
list_init(&driversForBus);
|
||||||
|
|
||||||
|
while (busIterator.GetNext(path, stat) == B_OK) {
|
||||||
|
path_entry *entry = find_node_ref_in_list(&drivers, stat.st_dev, stat.st_ino);
|
||||||
|
if (entry == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// we found the driver here, so we should check it
|
||||||
|
list_remove_link(&entry->link);
|
||||||
|
list_add_item(&driversForBus, entry);
|
||||||
|
dprintf("found driver for bus \"%s\": \"%s\"\n", path.Path(), entry->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: do something with the bus drivers... :)
|
||||||
|
// ToDo: ask bus manager for driver (via mapping)
|
||||||
|
// ToDo: find all nodes where this driver could be attached to
|
||||||
|
try_drivers(driversForBus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: do something with the remaining drivers... :)
|
||||||
|
try_drivers(drivers);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue